diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bf61d17 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,281 @@ +#----------------------------------------------------------------------------- +# This is the root RTToolbox CMakeList file. +#----------------------------------------------------------------------------- +PROJECT(RTToolbox) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +IF(CMAKE_BACKWARDS_COMPATIBILITY GREATER 2.8) + SET(CMAKE_BACKWARDS_COMPATIBILITY 2.8 CACHE STRING "Latest version of CMake when this project was released." FORCE) +ENDIF(CMAKE_BACKWARDS_COMPATIBILITY GREATER 2.8) + +# RTToolbox version number. +SET(RTToolbox_VERSION_MAJOR "2") +SET(RTToolbox_VERSION_MINOR "0") +SET(RTToolbox_VERSION_PATCH "0") + +# 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) + +IF (WIN32) + OPTION(BUILD_SHARED_LIBS "Build MatchPoint with shared libraries." OFF) +ELSE (WIN32) + OPTION(BUILD_SHARED_LIBS "Build MatchPoint with shared libraries." ON) +ENDIF (WIN32) + +MARK_AS_ADVANCED(BUILD_SHARED_LIBS) + +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(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) + +message(STATUS ${CMAKE_BUILD_TYPE}) +#----------------------------------------------------------------------------- +# 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) + +#----------------------------------------------------------------------------- +# 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) + +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 RTToolboxt") +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/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 0000000..c052134 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,14 @@ +SET(CTEST_PROJECT_NAME "I_RT_RTToolbox") +SET(CTEST_NIGHTLY_START_TIME "22:00:00 CET") + +IF(NOT DEFINED CTEST_DROP_METHOD) + SET(CTEST_DROP_METHOD "http") +ENDIF(NOT DEFINED CTEST_DROP_METHOD) + +IF(CTEST_DROP_METHOD STREQUAL "http") + SET(CTEST_DROP_SITE "sidt-hpc1") + SET(CTEST_DROP_LOCATION "/CDash/submit.php?project=${CTEST_PROJECT_NAME}") + SET(CTEST_DROP_SITE_CDASH TRUE) +ENDIF(CTEST_DROP_METHOD STREQUAL "http") + +SET (CTEST_TRIGGER_SITE "http://${CTEST_DROP_SITE}/cgi-bin/Submit-${CTEST_PROJECT_NAME}-TestingResults.cgi") \ No newline at end of file diff --git a/FindRTToolbox.cmake b/FindRTToolbox.cmake new file mode 100644 index 0000000..68c921c --- /dev/null +++ b/FindRTToolbox.cmake @@ -0,0 +1,89 @@ +# +# Find an RTToolbox installation or build tree. +# +# When RTToolbox is found, the RTToolboxConfig.cmake file is sourced to setup the +# location and configuration of RTToolbox. Please read this file, or +# RTToolboxConfig.cmake.in from the RTToolbox source tree for the full list of +# definitions. Of particular interest is +# +# RTToolbox_USE_FILE - A CMake source file that can be included +# to set the include directories, library +# directories, and preprocessor macros. +# +# In addition to the variables read from RTToolboxConfig.cmake, this find +# module also defines +# +# RTToolbox_DIR - The directory containing RTToolboxConfig.cmake. This is either +# the root of the build tree, or the lib/InsightToolkit +# directory. This is the only cache entry. +# +# RTToolbox_FOUND - Whether RTToolbox was found. If this is true, RTToolbox_DIR is okay. +# +# USE_RTToolbox_FILE - The full path to the UseRTToolbox.cmake file. This is provided +# for backward compatability. Use RTToolbox_USE_FILE instead. +# + +SET(RTToolbox_DIR_STRING "directory containing RTToolboxConfig.cmake. This is either the root of the build tree, or PREFIX/lib/RTToolbox for an installation.") + +# Search only if the location is not already known. +IF(NOT RTToolbox_DIR) + # Get the system search path as a list. + IF(UNIX) + STRING(REGEX MATCHALL "[^:]+" RTToolbox_DIR_SEARCH1 "$ENV{PATH}") + ELSE(UNIX) + STRING(REGEX REPLACE "\\\\" "/" RTToolbox_DIR_SEARCH1 "$ENV{PATH}") + ENDIF(UNIX) + STRING(REGEX REPLACE "/;" ";" RTToolbox_DIR_SEARCH2 ${RTToolbox_DIR_SEARCH1}) + + # Construct a set of paths relative to the system search path. + SET(RTToolbox_DIR_SEARCH "") + FOREACH(dir ${RTToolbox_DIR_SEARCH2}) + SET(RTToolbox_DIR_SEARCH ${ITK_DIR_SEARCH} "${dir}/../lib/RTToolbox") + ENDFOREACH(dir) + + # + # Look for an installation or build tree. + # + FIND_PATH(RTToolbox_DIR RTToolboxConfig.cmake + # Look for an environment variable RTToolbox_DIR. + $ENV{RTToolbox_DIR} + + # Look in places relative to the system executable search path. + ${RTToolbox_DIR_SEARCH} + + # Look in standard UNIX install locations. + /usr/local/lib/RTToolbox + /usr/lib/RTToolbox + + # Read from the CMakeSetup registry entries. It is likely that + # RTToolbox will have been recently built. + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild1] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild2] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild3] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild4] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild5] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild6] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild7] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild8] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild9] + [HKEY_CURRENT_USER\\Software\\Kitware\\CMakeSetup\\Settings\\StartPath;WhereBuild10] + + # Help the user find it if we cannot. + DOC "The ${RTToolbox_DIR_STRING}" + ) +ENDIF(NOT RTToolbox_DIR) + +# If RTToolbox was found, load the configuration file to get the rest of the +# settings. +IF(RTToolbox_DIR) + SET(RTToolbox_FOUND 1) + INCLUDE(${RTToolbox_DIR}/RTToolboxConfig.cmake) + + # Set USE_RTToolbox_FILE for backward-compatability. + SET(USE_RTToolbox_FILE ${RTToolbox_USE_FILE}) +ELSE(RTToolbox_DIR) + SET(RTToolbox_FOUND 0) + IF(RTToolbox_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Please set RTToolbox_DIR to the ${RTToolbox_DIR_STRING}") + ENDIF(RTToolbox_FIND_REQUIRED) +ENDIF(RTToolbox_DIR) diff --git a/RTToolboxConfig.cmake.in b/RTToolboxConfig.cmake.in new file mode 100644 index 0000000..7d83139 --- /dev/null +++ b/RTToolboxConfig.cmake.in @@ -0,0 +1,38 @@ +#----------------------------------------------------------------------------- +# +# RTToolboxConfig.cmake - RTToolbox CMake configuration file for external projects. +# +# This file is configured by RTToolbox and used by the UseRTToolbox.cmake module +# to load RTToolbox's settings for an external project. + +SET(RTToolbox_FOUND 1) + +# The RTToolbox source tree. +SET(RTToolbox_SOURCE_DIR "@RTToolbox_SOURCE_DIR@") + +# The RTToolbox binary tree. +SET(RTToolbox_BINARY_DIR "@RTToolbox_BINARY_DIR@") + +# The RTToolbox include file directories. +SET(RTToolbox_INCLUDE_DIRS "@RTToolbox_INCLUDE_DIRS_CONFIG@") + +# The RTToolbox library directories. +SET(RTToolbox_LIBRARY_DIRS "@RTToolbox_LIBRARY_DIRS_CONFIG@") + +# The RTToolbox version number +SET(RTToolbox_VERSION_MAJOR "@RTToolbox_VERSION_MAJOR@") +SET(RTToolbox_VERSION_MINOR "@RTToolbox_VERSION_MINOR@") +SET(RTToolbox_VERSION_PATCH "@RTToolbox_VERSION_PATCH@") + +# The location of the UseRTToolbox.cmake file. +SET(RTToolbox_USE_FILE "@RTToolbox_USE_FILE@") + +# The location of the MatchPointTargets.cmake file. +SET(RTToolbox_TARGETS_FILE "@RTToolbox_TARGETS_FILE@") + +# The RTToolbox library dependencies. +IF(NOT RTToolbox_NO_LIBRARY_DEPENDS AND + EXISTS "@RTToolbox_LIBRARY_DEPENDS_FILE@") + INCLUDE("@RTToolbox_LIBRARY_DEPENDS_FILE@") +ENDIF(NOT RTToolbox_NO_LIBRARY_DEPENDS AND + EXISTS "@RTToolbox_LIBRARY_DEPENDS_FILE@") \ No newline at end of file diff --git a/RTToolboxConfigure.h.in b/RTToolboxConfigure.h.in new file mode 100644 index 0000000..bc84fe9 --- /dev/null +++ b/RTToolboxConfigure.h.in @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ SIDT RT Interface +// +// 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) +// +// Subversion HeadURL: $HeadURL: http://sidt-hpc1/dkfz_repository/NotMeVisLab/SIDT/RTToolbox/branch/restructure/RTToolboxConfigure.h.in $ +*/ + +//---------------------------------------------------------- +// !!!EXPERIMENTAL CODE!!! +// +// This code may not be used for release. +// Add #define SIDT_ENFORCE_MATURE_CODE to any release module +// to ensure this policy. +//---------------------------------------------------------- +#ifdef SIDT_ENFORCE_MATURE_CODE +#error “This code is marked as experimental code. It must not be used because this build enforces mature code.” +#endif +#ifndef SIDT_CONTAINS_EXPERIMENTAL_CODE + #define SIDT_CONTAINS_EXPERIMENTAL_CODE 1 +#endif + +#ifndef __RTT_CONFIGURE_H +#define __RTT_CONFIGURE_H + + +/*! @def RTT_ENFORCE_MATURE_CODE + * This define controls if RTToolbox should force the whole build to be mature code. + * Mature code convention is part of the SIDT coding styles. + * Projects that use RTToolbox are able to ensure with SIDT_ENFORCE_MATURE_CODE + * that used code is guaranteed to be tested and reviewed regarding the strict + * SIDT coding styles.\n + * RTT_ENFORCE_MATURE_CODE can be used to ensure that strictness when prebuilding + * static or dynamic libraries. + * @remark This definition should by configured via the advance options in CMake. +*/ +#cmakedefine RTT_ENFORCE_MATURE_CODE +#ifdef RTT_ENFORCE_MATURE_CODE + #define SIDT_ENFORCE_MATURE_CODE +#endif + +#define RTT_VERSION_MAJOR @RTToolbox_VERSION_MAJOR@ +#define RTT_VERSION_MINOR @RTToolbox_VERSION_MINOR@ +#define RTT_VERSION_PATCH @RTToolbox_VERSION_PATCH@ +#define RTT_VERSION_STRING "@RTToolbox_VERSION_STRING@" +#define RTT_FULL_VERSION_STRING "@RTToolbox_FULL_VERSION_STRING@" + + +#endif diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..1c65ca9 --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,147 @@ +RTToolbox - Read Me +---------------------------- +---------------------------- + + +Needed Third Party Libraries +---------------------------- + +Please load and compile the following third party libraries/tool: + +1. CMake (version 2.8 or higher) +2. Litmus (in this distribution; see \utilities\Litmus; if you want tests) +3. dcmtk (with RT support - 3.6.1 or newer; if you want dicom support) +4. boost (version 1.52.0 or higher) +5. doxygen (if you want to generate a source code documentation) + +Installation Instruction +------------------------ + +Remark: To make sure everything runs smoothly, please make sure that all libraries and the RTToolbox +are either compiled with \MD or \MT flags. + +If third party library packages cannot be found automatically, cmake will ask for them. +Please give the location of the root folder, where the libraries where built for dcmtk, and Litmus. + + +[A. If you build tests - Litmus] + +A.1. Configure Litmus with CMake (seperated binary folder recommended) +A.2. Build Litmus + +[B. If you want DICOM support - DCMTK] + +For Windows: + +To compile DCMTK with \MD flags (standard for all other libs), you need to patch the CMAKE +options of DCMTK (\\DCMTK\CMake\dcmtkPrepare.cmake), either by replacing "/MT" with "/MD" +or by expliciltly replacing lines 135 to 171 with the following lines: + + +IF(DCMTK_OVERWRITE_WIN32_COMPILER_FLAGS AND NOT BUILD_SHARED_LIBS) + + + # settings for Microsoft Visual Studio + + IF(CMAKE_GENERATOR MATCHES "Visual Studio .*") + + # get Visual Studio Version + + STRING(REGEX REPLACE "Visual Studio ([0-9]+).*" "\\1" VS_VERSION "${CMAKE_GENERATOR}") + + # these settings never change even for C or C++ + + SET(CMAKE_C_FLAGS_DEBUG "/MDd /Z7 /Od") + + SET(CMAKE_C_FLAGS_RELEASE "/DNDEBUG /MD /O2") + + SET(CMAKE_C_FLAGS_MINSIZEREL "/DNDEBUG /MD /O2") + + SET(CMAKE_C_FLAGS_RELWITHDEBINFO "/DNDEBUG /MDd /Z7 /Od") + + + SET(CMAKE_CXX_FLAGS_DEBUG "/MDd /Z7 /Od") + + SET(CMAKE_CXX_FLAGS_RELEASE "/DNDEBUG /MD /O2") + + SET(CMAKE_CXX_FLAGS_MINSIZEREL "/DNDEBUG /MD /O2") + + SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/DNDEBUG /MDd /Z7 /Od") + + # specific settings for the various Visual Studio versions + + IF(VS_VERSION EQUAL 6) + + SET(CMAKE_C_FLAGS "/nologo /W3 /GX /Gy /YX") + + SET(CMAKE_CXX_FLAGS "/nologo /W3 /GX /Gy /YX /Zm500") # /Zm500 increments heap size which is needed on some system to compile templates in dcmimgle + + ENDIF(VS_VERSION EQUAL 6) + + IF(VS_VERSION EQUAL 7) + + SET(CMAKE_C_FLAGS "/nologo /W3 /Gy") + + SET(CMAKE_CXX_FLAGS "/nologo /W3 /Gy") + + ENDIF(VS_VERSION EQUAL 7) + + IF(VS_VERSION GREATER 7) + + SET(CMAKE_C_FLAGS "/nologo /W3 /Gy /EHsc") + + SET(CMAKE_CXX_FLAGS "/nologo /W3 /Gy /EHsc") + + ENDIF(VS_VERSION GREATER 7) + + ENDIF(CMAKE_GENERATOR MATCHES "Visual Studio .*") + + + # settings for Borland C++ + + IF(CMAKE_GENERATOR MATCHES "Borland Makefiles") + + # further settings required? not tested for a very long time! + + SET(CMAKE_STANDARD_LIBRARIES "import32.lib cw32md.lib") + + ENDIF(CMAKE_GENERATOR MATCHES "Borland Makefiles") + + +ENDIF(DCMTK_OVERWRITE_WIN32_COMPILER_FLAGS AND NOT BUILD_SHARED_LIBS) + +B.1. Configure DCMTK with CMake +For unix-like systems: Set the CMake parameter DCMTK_FORCE_FPIC_ON_UNIX to true (It is an advanced parameter) + +B.2. Build DCMTK + + +[C. Configure/Build RTToolbox] + +C.1 Configure with CMake + +C.1.1 Set BOOST_INCLUDE_DIR to the main boost directory (where "boost_build.jam" is located) + REMARK: For the current version of the RTToolbox you do need to build boost only if you want to use the provided Helax-Dicom data support. + Otherwise you can use headers only. + +C.2 Select all packages you like to build (Parameters "BUILD_*"; e.g. BUILD_IO_Dicom) + +C.2.1 If you select BUILD_IO_DICOM the configuration will ask you for the DCMTK main directory (parameter DCMTK_DIR; where you have built DCMTK). + REMARK: If you have built DCMTK as out source build you have to also set the + CMake parameter DCMTK_SOURCE_DIR to the root directory of the DCMTK source. + +C.3 Generate CMake configuration + +C.4 Build RTToolbox and have fun! + + +[D. Documentation] + +D.1 Generate the documentation using doxygen and the configuration found in "RTTB_binary_dir/documentation/doxygen.config". + + +Remarks +------- +R.1 Virtuos Support: Virtuos is a proprietary data format and therefore not included in the RTToolbox. The virtuos support (wrapper) for the RTToolbox is currently being restructured and will be included in the near future. + +R.2 Database support: The database support for the RTToolbox is currently being restructured and will be included in the near future. diff --git a/The GNU General Public License v3.0.htm b/The GNU General Public License v3.0.htm new file mode 100644 index 0000000..d937125 --- /dev/null +++ b/The GNU General Public License v3.0.htm @@ -0,0 +1,720 @@ + + + + + + + + + + + + +The GNU General Public License v3.0 - GNU Project - Free Software Foundation (FSF) + + + + + + + + + + + + + + + +
+

GNU GENERAL PUBLIC LICENSE

+

Version 3, 29 June 2007

+ +

Copyright © 2007 Free Software Foundation, Inc. + <http://fsf.org/>

+ Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed.

+ +

Preamble

+ +

The GNU General Public License is a free, copyleft license for +software and other kinds of works.

+ +

The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too.

+ +

When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things.

+ +

To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others.

+ +

For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights.

+ +

Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it.

+ +

For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions.

+ +

Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users.

+ +

Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free.

+ +

The precise terms and conditions for copying, distribution and +modification follow.

+ +

TERMS AND CONDITIONS

+ +

0. Definitions.

+ +

“This License” refers to version 3 of the GNU General Public License.

+ +

“Copyright” also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks.

+ +

“The Program” refers to any copyrightable work licensed under this +License. Each licensee is addressed as “you”. “Licensees” and +“recipients” may be individuals or organizations.

+ +

To “modify” a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a “modified version” of the +earlier work or a work “based on” the earlier work.

+ +

A “covered work” means either the unmodified Program or a work based +on the Program.

+ +

To “propagate” a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well.

+ +

To “convey” a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying.

+ +

An interactive user interface displays “Appropriate Legal Notices” +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion.

+ +

1. Source Code.

+ +

The “source code” for a work means the preferred form of the work +for making modifications to it. “Object code” means any non-source +form of a work.

+ +

A “Standard Interface” means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language.

+ +

The “System Libraries” of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +“Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it.

+ +

The “Corresponding Source” for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work.

+ +

The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source.

+ +

The Corresponding Source for a work in source code form is that +same work.

+ +

2. Basic Permissions.

+ +

All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law.

+ +

You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you.

+ +

Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary.

+ +

3. Protecting Users' Legal Rights From Anti-Circumvention Law.

+ +

No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures.

+ +

When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures.

+ +

4. Conveying Verbatim Copies.

+ +

You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program.

+ +

You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee.

+ +

5. Conveying Modified Source Versions.

+ +

You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions:

+ +
    +
  • a) The work must carry prominent notices stating that you modified + it, and giving a relevant date.
  • + +
  • b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + “keep intact all notices”.
  • + +
  • c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it.
  • + +
  • d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so.
  • +
+ +

A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +“aggregate” if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate.

+ +

6. Conveying Non-Source Forms.

+ +

You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways:

+ +
    +
  • a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange.
  • + +
  • b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge.
  • + +
  • c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b.
  • + +
  • d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements.
  • + +
  • e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d.
  • +
+ +

A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work.

+ +

A “User Product” is either (1) a “consumer product”, which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, “normally used” refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product.

+ +

“Installation Information” for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made.

+ +

If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM).

+ +

The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network.

+ +

Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying.

+ +

7. Additional Terms.

+ +

“Additional permissions” are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions.

+ +

When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission.

+ +

Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms:

+ +
    +
  • a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or
  • + +
  • b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or
  • + +
  • c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or
  • + +
  • d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or
  • + +
  • e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or
  • + +
  • f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors.
  • +
+ +

All other non-permissive additional terms are considered “further +restrictions” within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying.

+ +

If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms.

+ +

Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way.

+ +

8. Termination.

+ +

You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11).

+ +

However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation.

+ +

Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice.

+ +

Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10.

+ +

9. Acceptance Not Required for Having Copies.

+ +

You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so.

+ +

10. Automatic Licensing of Downstream Recipients.

+ +

Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License.

+ +

An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts.

+ +

You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it.

+ +

11. Patents.

+ +

A “contributor” is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's “contributor version”.

+ +

A contributor's “essential patent claims” are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, “control” includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License.

+ +

Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version.

+ +

In the following three paragraphs, a “patent license” is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To “grant” such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party.

+ +

If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. “Knowingly relying” means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid.

+ +

If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it.

+ +

A patent license is “discriminatory” if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007.

+ +

Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law.

+ +

12. No Surrender of Others' Freedom.

+ +

If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program.

+ +

13. Use with the GNU Affero General Public License.

+ +

Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such.

+ +

14. Revised Versions of this License.

+ +

The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns.

+ +

Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License “or any later version” applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation.

+ +

If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program.

+ +

Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version.

+ +

15. Disclaimer of Warranty.

+ +

THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

+ +

16. Limitation of Liability.

+ +

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES.

+ +

17. Interpretation of Sections 15 and 16.

+ +

If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee.

+ +

END OF TERMS AND CONDITIONS

+ +

How to Apply These Terms to Your New Programs

+ +

If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms.

+ +

To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the “copyright” line and a pointer to where the full notice is found.

+ +
    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+ +

Also add information on how to contact you by electronic and paper mail.

+ +

If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode:

+ +
    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+ +

The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an “about box”.

+ +

You should also get your employer (if you work as a programmer) or school, +if any, to sign a “copyright disclaimer” for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>.

+ +

The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ +
+ + + + \ No newline at end of file diff --git a/UseRTToolbox.cmake.in b/UseRTToolbox.cmake.in new file mode 100644 index 0000000..7cc4033 --- /dev/null +++ b/UseRTToolbox.cmake.in @@ -0,0 +1,15 @@ +# +# This file sets up include directories, link directories, and +# compiler settings for a project to use RTToolbox. It should not be +# included directly, but rather through the RTToolbox_USE_FILE setting +# obtained from RTToolboxConfig.cmake. +# + +# Add include directories needed to use RTToolbox. +INCLUDE_DIRECTORIES(BEFORE ${RTToolbox_INCLUDE_DIRS}) + +# Add link directories needed to use RTToolbox. +LINK_DIRECTORIES(${RTToolbox_LIBRARY_DIRS}) + +# Add target properties. +INCLUDE(${RTToolbox_TARGETS_FILE}) \ No newline at end of file diff --git a/cmake/CTestCustom.ctest.in b/cmake/CTestCustom.ctest.in new file mode 100644 index 0000000..cf7bcba --- /dev/null +++ b/cmake/CTestCustom.ctest.in @@ -0,0 +1,13 @@ +SET(CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE 1000000) +SET(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS 1000) +SET(CTEST_CUSTOM_WARNING_EXCEPTION + ${CTEST_CUSTOM_WARNING_EXCEPTION} + "warning LNK4221.*no public symbols found" + ) + +IF(APPLE) +SET(CTEST_CUSTOM_WARNING_EXCEPTION + ${CTEST_CUSTOM_WARNING_EXCEPTION} + "warning -.: directory name .* does not exist" + ) +ENDIF(APPLE) diff --git a/cmake/ITKConfig.cmake.in b/cmake/ITKConfig.cmake.in new file mode 100644 index 0000000..18fa660 --- /dev/null +++ b/cmake/ITKConfig.cmake.in @@ -0,0 +1,13 @@ +SET(MatchPoint_ITK_IS_ENABLED ON) + +# The itk source tree used by MatchPoint. +SET(MatchPoint_ITK_SOURCE_DIR @ITK_SOURCE_DIR@) + +# The itk library directories used by MatchPoint. +SET(MatchPoint_ITK_LIBRARY_DIRS @ITK_LIBRARY_DIRS@) + +# The itk libraries used by MatchPoint. +SET(MatchPoint_ITK_LIBRARIES @ITK_LIBRARIES@) + +# The itk include directories used by MatchPoint. +SET(MatchPoint_ITK_INCLUDE_DIRS @ITK_INCLUDE_DIRS@) diff --git a/cmake/MacroParseArguments.cmake b/cmake/MacroParseArguments.cmake new file mode 100644 index 0000000..c7cfc53 --- /dev/null +++ b/cmake/MacroParseArguments.cmake @@ -0,0 +1,79 @@ +# MACRO (MACRO_PARSE_ARGUMENTS prefix arg_names option_names) +# +# From http://www.cmake.org/Wiki/CMakeMacroParseArguments: +# +# The MACRO_PARSE_ARGUMENTS macro will take the arguments of another macro and +# define several variables: +# +# 1) The first argument to is a prefix to put on all variables it creates. +# 2) The second argument is a quoted list of names, +# 3) and the third argument is a quoted list of options. +# +# The rest of MACRO_PARSE_ARGUMENTS are arguments from another macro to be +# parsed. +# +# MACRO_PARSE_ARGUMENTS(prefix arg_names options arg1 arg2...) +# +# For each item in options, MACRO_PARSE_ARGUMENTS will create a variable +# with that name, prefixed with prefix_. So, for example, if prefix is +# MY_MACRO and options is OPTION1;OPTION2, then PARSE_ARGUMENTS will create +# the variables MY_MACRO_OPTION1 and MY_MACRO_OPTION2. These variables will +# be set to true if the option exists in the command line or false otherwise. +# +# For each item in arg_names, MACRO_PARSE_ARGUMENTS will create a variable +# with that name, prefixed with prefix_. Each variable will be filled with the +# arguments that occur after the given arg_name is encountered up to the next +# arg_name or the end of the arguments. All options are removed from these +# lists. +# +# MACRO_PARSE_ARGUMENTS also creates a prefix_DEFAULT_ARGS variable containing +# the list of all arguments up to the first arg_name encountered. + +IF(NOT COMMAND MACRO_PARSE_ARGUMENTS) + +MACRO (MACRO_PARSE_ARGUMENTS prefix arg_names option_names) + + SET(DEFAULT_ARGS) + + FOREACH (arg_name ${arg_names}) + SET(${prefix}_${arg_name}) + ENDFOREACH (arg_name) + + FOREACH (option ${option_names}) + SET(${prefix}_${option} FALSE) + ENDFOREACH (option) + + SET(current_arg_name DEFAULT_ARGS) + SET(current_arg_list) + + FOREACH (arg ${ARGN}) + + SET(larg_names ${arg_names}) + LIST(FIND larg_names "${arg}" is_arg_name) + + IF (is_arg_name GREATER -1) + + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name "${arg}") + SET(current_arg_list) + + ELSE (is_arg_name GREATER -1) + + SET(loption_names ${option_names}) + LIST(FIND loption_names "${arg}" is_option) + + IF (is_option GREATER -1) + SET(${prefix}_${arg} TRUE) + ELSE (is_option GREATER -1) + SET(current_arg_list ${current_arg_list} "${arg}") + ENDIF (is_option GREATER -1) + + ENDIF (is_arg_name GREATER -1) + + ENDFOREACH (arg ${ARGN}) + + SET(${prefix}_${current_arg_name} ${current_arg_list}) + +ENDMACRO (MACRO_PARSE_ARGUMENTS) + +ENDIF(NOT COMMAND MACRO_PARSE_ARGUMENTS) diff --git a/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake b/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake new file mode 100644 index 0000000..8935567 --- /dev/null +++ b/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake @@ -0,0 +1,28 @@ +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) + SET(Boost_USE_STATIC_LIBS OFF) + ELSE(BUILD_SHARED_LIBS) + SET(Boost_USE_STATIC_LIBS ON) + ENDIF(BUILD_SHARED_LIBS) + + FIND_PACKAGE(Boost REQUIRED COMPONENTS filesystem timer) + + 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_Boost_Config.cmake b/cmake/PackageDepends/RTTB_Boost_Config.cmake new file mode 100644 index 0000000..a24115f --- /dev/null +++ b/cmake/PackageDepends/RTTB_Boost_Config.cmake @@ -0,0 +1,19 @@ +IF(NOT Boost_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) + + FIND_PACKAGE(Boost REQUIRED) + + LIST(APPEND ALL_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) + # this is only necessary if we use more than just the headers + LIST(APPEND ALL_LIBRARIES ${Boost_LIBRARIES}) + link_directories(${Boost_LIBRARY_DIRS}) + + MARK_AS_ADVANCED(CLEAR Boost_INCLUDE_DIR) + +ENDIF(NOT Boost_FOUND) + diff --git a/cmake/PackageDepends/RTTB_DCMTK_Config.cmake b/cmake/PackageDepends/RTTB_DCMTK_Config.cmake new file mode 100644 index 0000000..ad40ba0 --- /dev/null +++ b/cmake/PackageDepends/RTTB_DCMTK_Config.cmake @@ -0,0 +1,216 @@ +#----------------------------------------------------------------------------- +# Find DCMTK +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# This part is based on the findDCMTK of CMake 2.8.8 +# it was patched because: +# 1. the find_library statment had an incomplet 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) + set(DCMTK_DIR "/usr/include/dcmtk/") +endif() + +#ensure that we always have the variable as cache, undependent from +#setting it via gui or commandline +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, undependet from +#setting it via gui or commandline +set(DCMTK_SOURCE_DIR ${DCMTK_SOURCE_DIR} CACHE PATH "Root of DCMTK tree.") + +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}) + + 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) + 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}/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) +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) +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 new file mode 100644 index 0000000..a524635 --- /dev/null +++ b/cmake/PackageDepends/RTTB_ITK_Config.cmake @@ -0,0 +1,25 @@ +#----------------------------------------------------------------------------- +# Find ITK. +#----------------------------------------------------------------------------- + + + +FIND_PACKAGE(ITK) +IF(ITK_FOUND) + INCLUDE(${ITK_USE_FILE}) +ELSE(ITK_FOUND) + MESSAGE(FATAL_ERROR + "Cannot build without ITK. Please set ITK_DIR.") +ENDIF(ITK_FOUND) + +IF(${ITK_VERSION_MAJOR} LESS 4 OR (${ITK_VERSION_MAJOR} EQUAL 4 AND ${ITK_VERSION_MINOR} LESS 0)) + MESSAGE(FATAL_ERROR + "Outdated ITK version. Cannot build MatchPoint without sufficient ITK version. ITK 4.0 or above is needed.") +ENDIF(${ITK_VERSION_MAJOR} LESS 4 OR (${ITK_VERSION_MAJOR} EQUAL 4 AND ${ITK_VERSION_MINOR} LESS 0)) + +#LIST(APPEND ALL_INCLUDE_DIRECTORIES ${ITK_INCLUDE_DIRS}) +#LIST(APPEND ALL_LIBRARIES ${ITK_LIBRARIES}) + +#LINK_DIRECTORIES(${ITK_LIBRARY_DIRS}) + +CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/CMake/ITKConfig.cmake.in ${RTTB_MODULES_CONF_DIR}/ITKConfig.cmake @ONLY) diff --git a/cmake/PackageDepends/RTTB_Litmus_Config.cmake b/cmake/PackageDepends/RTTB_Litmus_Config.cmake new file mode 100644 index 0000000..ff377b8 --- /dev/null +++ b/cmake/PackageDepends/RTTB_Litmus_Config.cmake @@ -0,0 +1,26 @@ +#----------------------------------------------------------------------------- +# Find Litmus +#----------------------------------------------------------------------------- +IF(DEFINED Litmus_DIR) + IF(NOT IS_ABSOLUTE ${Litmus_DIR}) + SET(Litmus_DIR "${RTToolbox_BINARY_DIR}/${Litmus_DIR}") + ENDIF(NOT IS_ABSOLUTE ${Litmus_DIR}) + + SET(Litmus_DIR ${Litmus_DIR} CACHE PATH "Litmus binary dir.") +ENDIF(DEFINED Litmus_DIR) + + + + +FIND_PACKAGE(Litmus) +IF(Litmus_FOUND) + INCLUDE(${Litmus_USE_FILE}) +ELSE(Litmus_FOUND) + MESSAGE(FATAL_ERROR + "Cannot build without Litmus. Please set Litmus_DIR.") +ENDIF(Litmus_FOUND) + +LIST(APPEND ALL_INCLUDE_DIRECTORIES ${Litmus_INCLUDE_DIRS}) +LIST(APPEND ALL_LIBRARIES LitmusCommon) + +LINK_DIRECTORIES(${Litmus_LIBRARY_DIRS}) diff --git a/cmake/PackageDepends/RTTB_MatchPoint_Config.cmake b/cmake/PackageDepends/RTTB_MatchPoint_Config.cmake new file mode 100644 index 0000000..68464aa --- /dev/null +++ b/cmake/PackageDepends/RTTB_MatchPoint_Config.cmake @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Find MatchPoint +#----------------------------------------------------------------------------- +FIND_PACKAGE(MatchPoint) +IF(MatchPoint_FOUND) + INCLUDE(${MatchPoint_USE_FILE}) +ELSE(MatchPoint_FOUND) + MESSAGE(FATAL_ERROR + "Cannot build without MATCHPOINT. Please set MATCHPOINT_DIR.") +ENDIF(MatchPoint_FOUND) + +LIST(APPEND ALL_INCLUDE_DIRECTORIES ${MatchPoint_INCLUDE_DIRS} ${MatchPoint_ITK_INCLUDE_DIRS}) +LIST(APPEND ALL_LIBRARIES MAPCore MAPAlgorithms MAPAlgorithmsITK MAPDeployment MAPIO ${MatchPoint_ITK_LIBRARIES}) + +LINK_DIRECTORIES(${MatchPoint_LIBRARY_DIRS} ${ITK_LIBRARY_DIRS}) \ No newline at end of file diff --git a/cmake/PackageDepends/RTTB_PostgreSQL_Config.cmake b/cmake/PackageDepends/RTTB_PostgreSQL_Config.cmake new file mode 100644 index 0000000..d815cdb --- /dev/null +++ b/cmake/PackageDepends/RTTB_PostgreSQL_Config.cmake @@ -0,0 +1,17 @@ +#use postgresql +#if using Win64 generator +IF(CMAKE_CL_64) + SET(POSTGRESQL_DIR ${RTToolbox_SOURCE_DIR}/../PostgreSQL-8.4-64) +ELSE() + SET(POSTGRESQL_DIR ${RTToolbox_SOURCE_DIR}/../postgresql-8.3.8-1) +ENDIF(CMAKE_CL_64) + +LIST(APPEND ALL_INCLUDE_DIRECTORIES ${POSTGRESQL_DIR}/include) + +IF(CMAKE_CL_64) + LIST(APPEND ALL_LIBRARIES libpq libpqdll) +ELSE() + LIST(APPEND ALL_LIBRARIES libpq) +ENDIF(CMAKE_CL_64) + +LINK_DIRECTORIES(${POSTGRESQL_DIR}/lib) diff --git a/cmake/PackageDepends/RTTB_VirtuosIO_Config.cmake b/cmake/PackageDepends/RTTB_VirtuosIO_Config.cmake new file mode 100644 index 0000000..5acd6f0 --- /dev/null +++ b/cmake/PackageDepends/RTTB_VirtuosIO_Config.cmake @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Find Virtuos IO +#----------------------------------------------------------------------------- +FIND_PACKAGE(VirtuosIOCore) +IF(VirtuosIOCore_FOUND) + INCLUDE(${VirtuosIOCore_USE_FILE}) +ELSE(VirtuosIOCore_FOUND) + MESSAGE(FATAL_ERROR + "Cannot build without VirtuosIOCore. Please set VirtuosIOCore_DIR.") +ENDIF(VirtuosIOCore_FOUND) + +LIST(APPEND ALL_INCLUDE_DIRECTORIES ${VirtuosIOCore_INCLUDE_DIRS}) +LIST(APPEND ALL_LIBRARIES VirtuosIO) + +LINK_DIRECTORIES(${VirtuosIOCore_LIBRARY_DIRS}) + +# include path to gzip/gunzip diff --git a/cmake/RemoveTemporaryFiles.cmake.in b/cmake/RemoveTemporaryFiles.cmake.in new file mode 100644 index 0000000..5e2d77e --- /dev/null +++ b/cmake/RemoveTemporaryFiles.cmake.in @@ -0,0 +1,22 @@ +# +# Remove temporary files that are generated by build submissions, +# cmake configure and testing +# +# Cleanup old dashboard submissions +FILE(GLOB OLD_DASHBOARDS "@RTToolbox_BINARY_DIR@/testing/20*") +FOREACH(OLD ${OLD_DASHBOARDS}) + MESSAGE(" Removing old submission in ${OLD}") + FILE(REMOVE_RECURSE "${OLD}") +ENDFOREACH(OLD ${OLD_DASHBOARDS}) + +# Cleanup the Testing/Temporary directory +FILE(REMOVE_RECURSE "@RTToolbox_BINARY_DIR@/Testing/Temporary") + +# Cleanup the CMake Error and Output logs +FILE(REMOVE "@RTToolbox_BINARY_DIR@${CMAKE_FILES_DIRECTORY}/CMakeOutput.log") +FILE(REMOVE "@RTToolbox_BINARY_DIR@${CMAKE_FILES_DIRECTORY}/CMakeOutput.log") +FILE(REMOVE "@RTToolbox_BINARY_DIR@/CMakeOutput.log") + +FILE(REMOVE "@RTToolbox_BINARY_DIR@${CMAKE_FILES_DIRECTORY}/CMakeError.log") +FILE(REMOVE "@RTToolbox_BINARY_DIR@${CMAKE_FILES_DIRECTORY}/CMakeError.log") +FILE(REMOVE "@RTToolbox_BINARY_DIR@/CMakeError.log") diff --git a/cmake/moduleConf.cmake.in b/cmake/moduleConf.cmake.in new file mode 100644 index 0000000..75b0049 --- /dev/null +++ b/cmake/moduleConf.cmake.in @@ -0,0 +1,10 @@ + +SET(MODULE_CURRENT_NAME "@MODULE_NAME@") +SET(@MODULE_NAME@_IS_ENABLED "@MODULE_IS_ENABLED@") +IF(@MODULE_NAME@_IS_ENABLED) + SET(@MODULE_NAME@_INCLUDE_DIRS "@MODULE_INCLUDE_DIRS@") + SET(@MODULE_NAME@_PROVIDES "@MODULE_PROVIDES@") + SET(@MODULE_NAME@_DEPENDS "@MODULE_DEPENDS@") + SET(@MODULE_NAME@_PACKAGE_DEPENDS "@MODULE_PACKAGE_DEPENDS@") + SET(@MODULE_NAME@_LIBRARY_DIRS "@CMAKE_LIBRARY_OUTPUT_DIRECTORY@") +ENDIF(@MODULE_NAME@_IS_ENABLED) diff --git a/cmake/moduleExports.h.in b/cmake/moduleExports.h.in new file mode 100644 index 0000000..e2ab862 --- /dev/null +++ b/cmake/moduleExports.h.in @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 @MODULE_NAME@_EXPORTS_H + #define @MODULE_NAME@_EXPORTS_H + #if defined(WIN32) + #ifdef @MODULE_PROVIDES@_EXPORTS + #define @MODULE_EXPORT_DEFINE@ __declspec(dllexport) + #else + #define @MODULE_EXPORT_DEFINE@ __declspec(dllimport) + #endif + #else + #define @MODULE_EXPORT_DEFINE@ + #endif + #ifndef _CMAKE_MODULENAME + #ifdef @MODULE_PROVIDES@_EXPORTS + #define _CMAKE_MODULENAME "@MODULE_NAME@" + #endif + #endif +#endif + + diff --git a/cmake/rttbFunctionOrganizeSources.cmake b/cmake/rttbFunctionOrganizeSources.cmake new file mode 100644 index 0000000..c60ecda --- /dev/null +++ b/cmake/rttbFunctionOrganizeSources.cmake @@ -0,0 +1,51 @@ +FUNCTION(ORGANIZE_SOURCES) + + # this functions gets a filelist as input and looks + # for corresponding h-files to add them to the project. + + # additionally files are grouped in source-groups. + + # No parameters explicitly declared here, because + # we want to allow for variable argument lists, which + # are later access by the keyword FOREACH(MYFILE ${ARGV}) + + # output: after calling the macro, files that were found + # correspondigly to the given files are stored in the + # variable: + # ${CORRESPONDING_H_FILES} + # ${CORRESPONDING_TXX_FILES} + + # Globbed can be found in the variables + # ${GLOBBED_TXX_FILES} (CURRENTLY COMMENTED OUT) + # ${GLOBBED_DOX_FILES} + + MACRO_PARSE_ARGUMENTS(_ORG "HEADER;SOURCE;TXX;DOC" "" ${ARGN}) + + SET(CORRESPONDING__H_FILES "" ) + SET(GLOBBED__H_FILES "" ) + + IF(_ORG_HEADER) + FOREACH(_file ${_ORG_SOURCE}) + STRING(REGEX REPLACE "(.*)\\.(txx|tpp|cpp|c|cxx)$" "\\1.h" H_FILE ${_file}) + IF(EXISTS ${H_FILE}) + LIST(APPEND CORRESPONDING__H_FILES "${H_FILE}") + ENDIF() + ENDFOREACH() + ELSE() + FILE(GLOB_RECURSE GLOBBED__H_FILES *.h) + ENDIF() + + + #_MACRO_APPEND_TO_LIST(_ORG_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/") + SOURCE_GROUP("Source Files" FILES ${_ORG_SOURCE}) + + #_MACRO_APPEND_TO_LIST(_ORG_TXX "${CMAKE_CURRENT_SOURCE_DIR}/") + SOURCE_GROUP("Template Files" FILES ${_ORG_TXX}) + + #_MACRO_APPEND_TO_LIST(_ORG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/") + SOURCE_GROUP("Header Files" FILES ${_ORG_HEADER} ${CORRESPONDING__H_FILES} ${GLOBBED__H_FILES}) + + #_MACRO_APPEND_TO_LIST(_ORG_DOC "${CMAKE_CURRENT_SOURCE_DIR}/") + SOURCE_GROUP("Doxygen Files" FILES ${_ORG_DOC}) + +ENDFUNCTION(ORGANIZE_SOURCES) diff --git a/cmake/rttbMacroCheckModule.cmake b/cmake/rttbMacroCheckModule.cmake new file mode 100644 index 0000000..5b47e3d --- /dev/null +++ b/cmake/rttbMacroCheckModule.cmake @@ -0,0 +1,73 @@ +# Usage: RTTB_CHECK_MODULE(RESULT_VAR [dependencies ...] ) +# check if all required modules exist and stores missing module names in RESULT_VAR. +MACRO(RTTB_CHECK_MODULE RESULT_VAR) + SET(${RESULT_VAR} "") + SET(DEPENDS "") + SET(DEPENDS_BEFORE "not initialized") + SET(PACKAGE_DEPENDS "") + + + # check for each parameter if it is a package (3rd party) + FOREACH(package ${ARGN}) + SET(is_package) + FOREACH(dir ${MODULES_PACKAGE_DEPENDS_DIRS}) + IF(EXISTS "${dir}/RTTB_${package}_Config.cmake") + LIST(APPEND PACKAGE_DEPENDS ${package}) + SET(is_package 1) + BREAK() + ENDIF() + ENDFOREACH() + IF(NOT is_package) + LIST(APPEND DEPENDS ${package}) + ENDIF() + ENDFOREACH(package) + + # create a list of all lowercase module names + FILE(GLOB _ALL_MODULES RELATIVE ${RTTB_MODULES_CONF_DIR} ${RTTB_MODULES_CONF_DIR}/*Config.cmake) + SET(_ALL_MODULES_LOWERCASE "") + FOREACH(_module ${_ALL_MODULES}) + STRING(TOLOWER ${_module} _lowermodule) + LIST(APPEND _ALL_MODULES_LOWERCASE ${_lowermodule}) + ENDFOREACH(_module ${_ALL_MODULES}) + + WHILE(NOT "${DEPENDS}" STREQUAL "${DEPENDS_BEFORE}") + SET(DEPENDS_BEFORE ${DEPENDS}) + FOREACH(dependency ${DEPENDS}) + + SET(_dependency_file_name ${dependency}Config.cmake) + LIST(FIND _ALL_MODULES ${_dependency_file_name} _index) + IF(NOT _index EQUAL -1) + INCLUDE(${RTTB_MODULES_CONF_DIR}/${dependency}Config.cmake) + IF(${dependency}_IS_ENABLED) + LIST(APPEND DEPENDS ${${dependency}_DEPENDS}) + LIST(APPEND PACKAGE_DEPENDS ${${dependency}_PACKAGE_DEPENDS}) + ELSE(${dependency}_IS_ENABLED) + LIST(APPEND ${RESULT_VAR} ${dependency}) + LIST(REMOVE_DUPLICATES ${RESULT_VAR}) + ENDIF(${dependency}_IS_ENABLED) + ELSE(NOT _index EQUAL -1) + STRING(TOLOWER ${_dependency_file_name} _lowercase_dependency_file_name) + LIST(FIND _ALL_MODULES_LOWERCASE ${_lowercase_dependency_file_name} _index_lower) + IF(NOT _index_lower EQUAL -1) + LIST(GET _ALL_MODULES ${_index_lower} _real_module_name) + STRING(REPLACE "Config.cmake" "" _real_module_name ${_real_module_name}) + MESSAGE("Warning: case mismatch for module name ${dependency}, did you mean ${_real_module_name} ?") + ENDIF(NOT _index_lower EQUAL -1) + LIST(APPEND ${RESULT_VAR} ${dependency}) + LIST(REMOVE_DUPLICATES ${RESULT_VAR}) + ENDIF(NOT _index EQUAL -1) + + ENDFOREACH(dependency) + LIST(REMOVE_DUPLICATES DEPENDS) + LIST(REMOVE_DUPLICATES PACKAGE_DEPENDS) + LIST(SORT DEPENDS) + LIST(SORT PACKAGE_DEPENDS) + ENDWHILE() + + FOREACH(_package PACKAGE_DEPENDS) + IF((DEFINED RTTB_USE_${_package}) AND NOT (${RTTB_USE_${_package}})) + LIST(APPEND ${RESULT_VAR} ${_package}) + ENDIF() + ENDFOREACH() + +ENDMACRO(RTTB_CHECK_MODULE) diff --git a/cmake/rttbMacroCreateApplication.cmake b/cmake/rttbMacroCreateApplication.cmake new file mode 100644 index 0000000..f25ab5a --- /dev/null +++ b/cmake/rttbMacroCreateApplication.cmake @@ -0,0 +1,79 @@ +################################################################## +# +# RTTB_CREATE_APPLICATION +# +#! Creates a application using the automatic module dependency system within RTTB. +#! Configurations are generated in the moduleConf directory. +#! +#! USAGE: +#! +#! \code +#! MAP_CREATE_APPLICATION( +#! [INCLUDE_DIRS ] +#! [DEPENDS ] +#! [PACKAGE_DEPENDS ] +#! \endcode +#! +#! \param APP_NAME_IN The name for the new application +# +################################################################## +MACRO(RTTB_CREATE_APPLICATION APP_NAME_IN) + MACRO_PARSE_ARGUMENTS(APP + "INCLUDE_DIRS;DEPENDS;PACKAGE_DEPENDS" + "FORCE_STATIC;HEADERS_ONLY" + ${ARGN}) + + SET(APP_NAME ${APP_NAME_IN}) + + MESSAGE(STATUS "configuring Application ${APP_NAME}...") + # first of all we check for the dependencies + RTTB_CHECK_MODULE(_MISSING_DEP ${APP_DEPENDS}) + IF(_MISSING_DEP) + MESSAGE("Application ${APP_NAME} won't be built, missing dependency: ${_MISSING_DEP}") + SET(APP_IS_ENABLED 0) + ELSE(_MISSING_DEP) + SET(APP_IS_ENABLED 1) + # now check for every package if it is enabled. This overlaps a bit with + # RTTB_CHECK_MODULE ... + FOREACH(_package ${APP_PACKAGE_DEPENDS}) + IF((DEFINED RTTB_USE_${_package}) AND NOT (RTTB_USE_${_package})) + MESSAGE("Application ${APP_NAME} won't be built. Turn on RTTB_USE_${_package} if you want to use it.") + SET(APP_IS_ENABLED 0) + ENDIF() + ENDFOREACH() + + IF(APP_IS_ENABLED) + SET(DEPENDS "${APP_DEPENDS}") + SET(DEPENDS_BEFORE "not initialized") + SET(PACKAGE_DEPENDS "${APP_PACKAGE_DEPENDS}") + RTTB_USE_MODULE("${APP_DEPENDS}") + + IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/source) + SET(ALL_INCLUDE_DIRECTORIES ${ALL_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR}/source) + ENDIF() + + IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include) + SET(ALL_INCLUDE_DIRECTORIES ${ALL_INCLUDE_DIRECTORIES} ${CMAKE_CURRENT_SOURCE_DIR}/include) + ENDIF() + + INCLUDE_DIRECTORIES(. ${ALL_INCLUDE_DIRECTORIES}) + INCLUDE(files.cmake) + + ORGANIZE_SOURCES(SOURCE ${CPP_FILES} + HEADER ${H_FILES} + TPP ${TPP_FILES} + DOC ${DOX_FILES} + ) + + LINK_DIRECTORIES(${ALL_LIBRARY_DIRS}) + + SET(coverage_sources ${CPP_FILES} ${H_FILES} ${TXX_FILES}) + + ADD_EXECUTABLE(${APP_NAME} ${coverage_sources}) + + TARGET_LINK_LIBRARIES(${APP_NAME} ${ALL_LIBRARIES}) + ENDIF(APP_IS_ENABLED) + + ENDIF(_MISSING_DEP) + +ENDMACRO(RTTB_CREATE_APPLICATION) diff --git a/cmake/rttbMacroCreateModule.cmake b/cmake/rttbMacroCreateModule.cmake new file mode 100644 index 0000000..ec772af --- /dev/null +++ b/cmake/rttbMacroCreateModule.cmake @@ -0,0 +1,113 @@ +################################################################## +# +# RTTB_CREATE_MODULE +# +#! Creates a module for the automatic module dependency system within RTTB. +#! Configurations are generated in the moduleConf directory. +#! +#! USAGE: +#! +#! \code +#! RTTB_CREATE_MODULE( +#! [INCLUDE_DIRS ] +#! [INTERNAL_INCLUDE_DIRS ] +#! [DEPENDS ] +#! [PROVIDES ] +#! [PACKAGE_DEPENDS ] +#! [EXPORT_DEFINE ] +#! \endcode +#! +#! \param MODULE_NAME_IN The name for the new module +# +################################################################## +MACRO(RTTB_CREATE_MODULE MODULE_NAME_IN) + MACRO_PARSE_ARGUMENTS(MODULE + "INCLUDE_DIRS;INTERNAL_INCLUDE_DIRS;DEPENDS;DEPENDS_INTERNAL;PROVIDES;PACKAGE_DEPENDS;EXPORT_DEFINE;ADDITIONAL_LIBS" + "FORCE_STATIC;HEADERS_ONLY" + ${ARGN}) + + SET(MODULE_NAME ${MODULE_NAME_IN}) + + # assume worst case + SET(MODULE_IS_ENABLED 0) + # first we check if we have an explicit module build list + IF(RTTB_MODULES_TO_BUILD) + LIST(FIND RTTB_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) + IF(_MOD_INDEX EQUAL -1) + SET(MODULE_IS_EXCLUDED 1) + ENDIF() + ENDIF() + + IF(NOT MODULE_IS_EXCLUDED) + MESSAGE(STATUS "configuring Module ${MODULE_NAME}...") + # first of all we check for the dependencies + RTTB_CHECK_MODULE(_MISSING_DEP ${MODULE_DEPENDS}) + IF(_MISSING_DEP) + MESSAGE("Module ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") + SET(MODULE_IS_ENABLED 0) + ELSE(_MISSING_DEP) + SET(MODULE_IS_ENABLED 1) + # now check for every package if it is enabled. This overlaps a bit with + # RTTB_CHECK_MODULE ... + FOREACH(_package ${MODULE_PACKAGE_DEPENDS}) + IF((DEFINED RTTB_USE_${_package}) AND NOT (RTTB_USE_${_package})) + MESSAGE("Module ${MODULE_NAME} won't be built. Turn on RTTB_USE_${_package} if you want to use it.") + SET(MODULE_IS_ENABLED 0) + ENDIF() + ENDFOREACH() + + IF(MODULE_IS_ENABLED) + SET(MODULE_IS_ENABLED 1) + _RTTB_CREATE_MODULE_CONF() + IF(NOT MODULE_EXPORT_DEFINE) + SET(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT) + ENDIF(NOT MODULE_EXPORT_DEFINE) + + CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/cmake/moduleExports.h.in ${RTTB_MODULES_CONF_DIR}/${MODULE_NAME}Exports.h @ONLY) + + SET(DEPENDS "${MODULE_DEPENDS}") + SET(DEPENDS_BEFORE "not initialized") + SET(PACKAGE_DEPENDS "${MODULE_PACKAGE_DEPENDS}") + RTTB_USE_MODULE("${MODULE_DEPENDS}") + + # ok, now create the module itself + INCLUDE_DIRECTORIES(. ${ALL_INCLUDE_DIRECTORIES}) + INCLUDE(files.cmake) + + ORGANIZE_SOURCES(SOURCE ${CPP_FILES} + HEADER ${H_FILES} + TXX ${TXX_FILES} + DOC ${DOX_FILES} + ) + + IF(MODULE_FORCE_STATIC) + SET(_STATIC ${RTTB_WIN32_FORCE_STATIC}) + ENDIF(MODULE_FORCE_STATIC) + + IF(NOT MODULE_HEADERS_ONLY) + IF(ALL_LIBRARY_DIRS) + # LINK_DIRECTORIES applies only to targets which are added after the call to LINK_DIRECTORIES + LINK_DIRECTORIES(${ALL_LIBRARY_DIRS}) + ENDIF(ALL_LIBRARY_DIRS) + + SET(coverage_sources ${CPP_FILES} ${H_FILES} ${TXX_FILES}) + SET_PROPERTY(SOURCE ${coverage_sources} APPEND PROPERTY LABELS ${MODULE_SUBPROJECTS}) + + ADD_LIBRARY(${MODULE_PROVIDES} ${_STATIC} ${coverage_sources} ${CPP_FILES_GENERATED}) + + SET_TARGET_PROPERTIES(${MODULE_PROVIDES} PROPERTIES ${RTToolbox_LIBRARY_PROPERTIES} OUTPUT_NAME ${MODULE_PROVIDES}-${RTToolbox_VERSION_MAJOR}.${RTToolbox_VERSION_MINOR} OUTPUT_NAME_DEBUG ${MODULE_PROVIDES}-D-${RTToolbox_VERSION_MAJOR}.${RTToolbox_VERSION_MINOR}) + EXPORT(TARGETS ${MODULE_PROVIDES} APPEND FILE ${RTToolbox_TARGETS_FILE}) + + IF(ALL_LIBRARIES) + TARGET_LINK_LIBRARIES(${MODULE_PROVIDES} ${ALL_LIBRARIES}) + ENDIF(ALL_LIBRARIES) + ENDIF() + + ENDIF(MODULE_IS_ENABLED) + ENDIF(_MISSING_DEP) + ENDIF(NOT MODULE_IS_EXCLUDED) + + IF(NOT MODULE_IS_ENABLED) + _RTTB_CREATE_MODULE_CONF() + ENDIF(NOT MODULE_IS_ENABLED) +ENDMACRO(RTTB_CREATE_MODULE) diff --git a/cmake/rttbMacroCreateModuleConf.cmake b/cmake/rttbMacroCreateModuleConf.cmake new file mode 100644 index 0000000..f08e701 --- /dev/null +++ b/cmake/rttbMacroCreateModuleConf.cmake @@ -0,0 +1,36 @@ +################################################################### +# +# RTTB_CREATE_MODULE_CONF +# +# This can be called in a similar way like RTTB_CREATE_MODULE +# but it just creates the module configuration files without +# actually building it. It is used for integration of legacy libraries +# into the RTTB module build system +# +################################################################## +MACRO(RTTB_CREATE_MODULE_CONF MODULE_NAME_IN) + MACRO_PARSE_ARGUMENTS(MODULE "INCLUDE_DIRS;DEPENDS;PROVIDES" "QT_MODULE;HEADERS_ONLY" ${ARGN}) + SET(MODULE_NAME ${MODULE_NAME_IN}) + SET(MODULE_IS_ENABLED 1) + _RTTB_CREATE_MODULE_CONF() +ENDMACRO(RTTB_CREATE_MODULE_CONF) + +MACRO(_RTTB_CREATE_MODULE_CONF) + IF(NOT MODULE_PROVIDES AND NOT MODULE_HEADERS_ONLY) + SET(MODULE_PROVIDES ${MODULE_NAME}) + ENDIF(NOT MODULE_PROVIDES AND NOT MODULE_HEADERS_ONLY) + SET(MODULE_INCLUDE_DIRS_ABSOLUTE "") + FOREACH(dir ${MODULE_INCLUDE_DIRS}) + GET_FILENAME_COMPONENT(abs_dir ${dir} ABSOLUTE) + SET(MODULE_INCLUDE_DIRS_ABSOLUTE ${MODULE_INCLUDE_DIRS_ABSOLUTE} ${abs_dir}) + ENDFOREACH(dir) + + SET(MODULE_INCLUDE_DIRS ${MODULE_INCLUDE_DIRS_ABSOLUTE} ${CMAKE_CURRENT_SOURCE_DIR}) + + # Qt generates headers in the binary tree + IF(MODULE_QT_MODULE) + SET(MODULE_INCLUDE_DIRS ${MODULE_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) + ENDIF(MODULE_QT_MODULE) + + CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/cmake/moduleConf.cmake.in ${RTTB_MODULES_CONF_DIR}/${MODULE_NAME}Config.cmake @ONLY) +ENDMACRO(_RTTB_CREATE_MODULE_CONF) diff --git a/cmake/rttbMacroCreateTestModule.cmake b/cmake/rttbMacroCreateTestModule.cmake new file mode 100644 index 0000000..1a6ee71 --- /dev/null +++ b/cmake/rttbMacroCreateTestModule.cmake @@ -0,0 +1,172 @@ +################################################################## +# +# RTTB_CREATE_MODULE +# +#! Creates a module for the automatic module dependency system within RTTB. +#! Configurations are generated in the moduleConf directory. +#! +#! USAGE: +#! +#! \code +#! RTTB_CREATE_MODULE( +#! [INCLUDE_DIRS ] +#! [INTERNAL_INCLUDE_DIRS ] +#! [DEPENDS ] +#! [PROVIDES ] +#! [PACKAGE_DEPENDS ] +#! [HEADER_TEST] +#! \endcode +#! +#! \param MODULE_NAME_IN The name for the new module +# +################################################################## +MACRO(RTTB_CREATE_TEST_MODULE MODULE_NAME_IN) + MACRO_PARSE_ARGUMENTS(MODULE + "INCLUDE_DIRS;INTERNAL_INCLUDE_DIRS;DEPENDS;DEPENDS_INTERNAL;PROVIDES;PACKAGE_DEPENDS;ADDITIONAL_LIBS" + "HEADER_TESTS" + ${ARGN}) + + SET(MODULE_NAME "${MODULE_NAME_IN}Tests") + + # assume worst case + SET(MODULE_IS_ENABLED 0) + # first we check if we have an explicit module build list + IF(RTTB_MODULES_TO_BUILD) + LIST(FIND RTTB_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) + IF(_MOD_INDEX EQUAL -1) + SET(MODULE_IS_EXCLUDED 1) + ENDIF() + ENDIF() + + IF(NOT MODULE_IS_EXCLUDED) + MESSAGE(STATUS "configuring Tests ${MODULE_NAME}...") + # first of all we check for the dependencies + RTTB_CHECK_MODULE(_MISSING_DEP ${MODULE_DEPENDS}) + IF(_MISSING_DEP) + MESSAGE("Module ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") + SET(MODULE_IS_ENABLED 0) + ELSE(_MISSING_DEP) + SET(MODULE_IS_ENABLED 1) + # now check for every package if it is enabled. This overlaps a bit with + # RTTB_CHECK_MODULE ... + FOREACH(_package ${MODULE_PACKAGE_DEPENDS}) + IF((DEFINED RTTB_USE_${_package}) AND NOT (RTTB_USE_${_package})) + MESSAGE("Module ${MODULE_NAME} won't be built. Turn on RTTB_USE_${_package} if you want to use it.") + SET(MODULE_IS_ENABLED 0) + ENDIF() + ENDFOREACH() + + IF(NOT MODULE_PROVIDES) + SET(MODULE_PROVIDES ${MODULE_NAME}) + ENDIF(NOT MODULE_PROVIDES) + + SET(DEPENDS "${MODULE_DEPENDS}") + SET(DEPENDS_BEFORE "not initialized") + SET(PACKAGE_DEPENDS "${MODULE_PACKAGE_DEPENDS}") + RTTB_USE_MODULE("${MODULE_DEPENDS}") + + # ok, now create the module itself + INCLUDE_DIRECTORIES(. ${ALL_INCLUDE_DIRECTORIES}) + INCLUDE(files.cmake) + + ORGANIZE_SOURCES(SOURCE ${CPP_FILES} + HEADER ${H_FILES} + TXX ${TXX_FILES} + DOC ${DOX_FILES} + ) + + + IF(ALL_LIBRARY_DIRS) + # LINK_DIRECTORIES applies only to targets which are added after the call to LINK_DIRECTORIES + LINK_DIRECTORIES(${ALL_LIBRARY_DIRS}) + ENDIF(ALL_LIBRARY_DIRS) + + SET(coverage_sources ${CPP_FILES} ${H_FILES} ${TXX_FILES}) + + ADD_EXECUTABLE(${MODULE_PROVIDES} ${coverage_sources} ${CPP_FILES_GENERATED}) + + IF(ALL_LIBRARIES) + TARGET_LINK_LIBRARIES(${MODULE_PROVIDES} ${ALL_LIBRARIES}) + ENDIF(ALL_LIBRARIES) + + IF(MODULE_HEADER_TESTS) + MESSAGE(STATUS "generating header tests ${MODULE_NAME}...") + SET(HEADER_TEST_CPP "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME_IN}HeaderTests.cpp") + MESSAGE(STATUS "generating header tests ${HEADER_TEST_CPP}") + + FILE(WRITE ${HEADER_TEST_CPP} ${RTTB_HEADER_TESTS_HEADER}) + FOREACH(_h_file ${H_FILES}) + FILE(APPEND ${HEADER_TEST_CPP} "#include <${_h_file}>\n") + ENDFOREACH() + FILE(APPEND ${HEADER_TEST_CPP} ${RTTB_HEADER_TESTS_FOOTER}) + + ADD_EXECUTABLE(${MODULE_NAME_IN}HeaderTests ${HEADER_TEST_CPP}) + + IF(ALL_LIBRARIES) + TARGET_LINK_LIBRARIES(${MODULE_NAME_IN}HeaderTests ${ALL_LIBRARIES}) + ENDIF(ALL_LIBRARIES) + + ENDIF(MODULE_HEADER_TESTS) + + #----------------------------------------------------------------------------- + + IF(RTToolbox_BINARY_DIR) + SET(RTToolbox_SYSTEM_INFORMATION_DIR ${RTToolbox_BINARY_DIR}) + ELSE(RTToolbox_BINARY_DIR) + SET(RTToolbox_SYSTEM_INFORMATION_DIR ${RTTBTesting_BINARY_DIR}) + ENDIF(RTToolbox_BINARY_DIR) + + WRITE_FILE( + "${RTToolbox_SYSTEM_INFORMATION_DIR}/testing/HTML/TestingResults/Sites/${SITE}/${BUILDNAME}/BuildNameNotes.xml" + + "\n" + "\n" + "\n" + "\n" + "Wed Oct 24 1:00:00 EST\n" + "\n" + "Here is some basic information:\n" + "RTToolbox_SOURCE_DIR = ${RTToolbox_SOURCE_DIR}\n" + "RTToolbox_BINARY_DIR = ${RTToolbox_BINARY_DIR}\n" + "RTTBTesting_SOURCE_DIR = ${RTTBTesting_SOURCE_DIR}\n" + "RTTBTesting_BINARY_DIR = ${RTTBTesting_BINARY_DIR}\n" + "CMAKE_C_COMPILER = ${CMAKE_C_COMPILER}\n" + "CMAKE_C_FLAGS = ${CMAKE_C_FLAGS}\n" + "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}\n" + "CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}\n" + "CMAKE_SYSTEM = ${CMAKE_SYSTEM}\n" + "CMAKE_MAKE_PROGRAM = ${CMAKE_MAKE_PROGRAM}\n" + "\n" + "\n" + "\n" + "\n" + ) + + ENDIF(_MISSING_DEP) + ENDIF(NOT MODULE_IS_EXCLUDED) +ENDMACRO(RTTB_CREATE_TEST_MODULE) + +SET(RTTB_HEADER_TESTS_HEADER +"// -----------------------------------------------------------------------\n" +"// RTToolbox - DKFZ radiotherapy quantitative evaluation library\n" +"//\n" +"// (c) Copyright 2007, DKFZ, Heidelberg, Germany\n" +"// ALL RIGHTS RESERVED\n" +"//\n" +"// THIS FILE CONTAINS CONFIDENTIAL AND PROPRIETARY INFORMATION OF DKFZ.\n" +"// ANY DUPLICATION, MODIFICATION, DISTRIBUTION, OR\n" +"// DISCLOSURE IN ANY FORM, IN WHOLE, OR IN PART, IS STRICTLY PROHIBITED\n" +"// WITHOUT THE PRIOR EXPRESS WRITTEN PERMISSION OF DKFZ.\n" +"//\n" +"//------------------------------------------------------------------------\n" +"// Automatically generated header test file \n" +"#if defined(_MSC_VER)\n" +"#pragma warning ( disable : 4786 )\n" +"#endif\n" +"#include \n") + +SET(RTTB_HEADER_TESTS_FOOTER +"\nint main ( int , char* )\n" +"{\n" +" return EXIT_SUCCESS;\n" +"}\n") diff --git a/cmake/rttbMacroUseModule.cmake b/cmake/rttbMacroUseModule.cmake new file mode 100644 index 0000000..6c43016 --- /dev/null +++ b/cmake/rttbMacroUseModule.cmake @@ -0,0 +1,65 @@ +# Usage: RTTB_USE_MODULE([dependencies ...] ) +# loads module config files of all dependencies specified. +MACRO(RTTB_USE_MODULE) + SET(DEPENDS "") + SET(DEPENDS_BEFORE "not initialized") + + # check for each parameter if it is a package (3rd party) + FOREACH(package ${ARGN}) + SET(is_package) + FOREACH(dir ${MODULES_PACKAGE_DEPENDS_DIRS}) + IF(EXISTS "${dir}/RTTB_${package}_Config.cmake") + LIST(APPEND PACKAGE_DEPENDS ${package}) + SET(is_package 1) + BREAK() + ENDIF() + ENDFOREACH() + IF(NOT is_package) + LIST(APPEND DEPENDS ${package}) + ENDIF() + ENDFOREACH(package) + + # go through all module dependencies and get there dependencies for modules and packages + WHILE(NOT "${DEPENDS}" STREQUAL "${DEPENDS_BEFORE}") + SET(DEPENDS_BEFORE ${DEPENDS}) + FOREACH(dependency ${DEPENDS}) + INCLUDE(${RTTB_MODULES_CONF_DIR}/${dependency}Config.cmake) + LIST(APPEND DEPENDS ${${dependency}_DEPENDS}) + LIST(APPEND PACKAGE_DEPENDS ${${dependency}_PACKAGE_DEPENDS}) + ENDFOREACH(dependency) + IF(DEPENDS) + LIST(REMOVE_DUPLICATES DEPENDS) + LIST(SORT DEPENDS) + ENDIF(DEPENDS) + IF(PACKAGE_DEPENDS) + LIST(REMOVE_DUPLICATES PACKAGE_DEPENDS) + LIST(SORT PACKAGE_DEPENDS) + ENDIF(PACKAGE_DEPENDS) + ENDWHILE() + + # now go through all module dependencies and get dirs and libraries + FOREACH(dependency ${DEPENDS} ${MODULE_DEPENDS_INTERNAL}) + INCLUDE(${RTTB_MODULES_CONF_DIR}/${dependency}Config.cmake) + SET(ALL_INCLUDE_DIRECTORIES ${ALL_INCLUDE_DIRECTORIES} ${${dependency}_INCLUDE_DIRS}) + SET(ALL_LIBRARIES ${ALL_LIBRARIES} ${${dependency}_PROVIDES}) + SET(ALL_LIBRARY_DIRS ${ALL_LIBRARY_DIRS} ${${dependency}_LIBRARY_DIRS}) + ENDFOREACH(dependency) + + # now go through all package dependencies and include there config file + FOREACH(package ${PACKAGE_DEPENDS}) + FOREACH(dir ${MODULES_PACKAGE_DEPENDS_DIRS}) + IF(EXISTS "${dir}/RTTB_${package}_Config.cmake") + INCLUDE("${dir}/RTTB_${package}_Config.cmake") + BREAK() + ENDIF() + ENDFOREACH() + ENDFOREACH(package) + + SET(ALL_LIBRARIES ${ALL_LIBRARIES} ${MODULE_ADDITIONAL_LIBS}) + SET(ALL_INCLUDE_DIRECTORIES ${ALL_INCLUDE_DIRECTORIES} ${MODULE_INCLUDE_DIRS} ${MODULE_INTERNAL_INCLUDE_DIRS} ${RTTB_MODULES_CONF_DIR}) + + IF(ALL_LIBRARY_DIRS) + LIST(REMOVE_DUPLICATES ALL_LIBRARY_DIRS) + ENDIF(ALL_LIBRARY_DIRS) + +ENDMACRO(RTTB_USE_MODULE) diff --git a/cmake/rttbSampleBuildTest.cmake.in b/cmake/rttbSampleBuildTest.cmake.in new file mode 100644 index 0000000..12e3363 --- /dev/null +++ b/cmake/rttbSampleBuildTest.cmake.in @@ -0,0 +1,108 @@ +# +# This is a recommended file for RTToolbox dashboard submissions +# The generated file will use the current settings in the CMakeCache.txt file +# The file should be copied, edited if necessary and moved to another location +# +# This file was generated from +# @RTToolbox_SOURCE_DIR@/cmake/rttbSampleBuildTest.cmake +# + +###################### +# Set the Dashboard to one of Nightly, Continuous or Experimental +# +SET(DASHBOARD Nightly) +SET(DASHBOARD Continuous) +SET(DASHBOARD Experimental) + +###################### +# Set the Default Build Type to one of Release, Debug, RelWithDebInfo +# or MinSizeRel +# +SET(DEFAULT_BUILD_TYPE "Debug") +SET(DEFAULT_BUILD_TYPE "RelWithDebInfo") +SET(DEFAULT_BUILD_TYPE "MinSizeRel") +SET(DEFAULT_BUILD_TYPE "Release") + +IF("@CMAKE_BUILD_TYPE@" STREQUAL "") + SET(BUILD_TYPE ${DEFAULT_BUILD_TYPE}) +ELSE("@CMAKE_BUILD_TYPE@" STREQUAL "") + SET(BUILD_TYPE @CMAKE_BUILD_TYPE@) +ENDIF("@CMAKE_BUILD_TYPE@" STREQUAL "") + + +# These are the the locations of the source and binary directory +SET(CTEST_SOURCE_DIRECTORY "@RTToolbox_SOURCE_DIR@") +SET(CTEST_BINARY_DIRECTORY "@RTToolbox_BINARY_DIR@") + +# The ctest command to use for running the dashboard +SET(CTEST_COMMAND + "@CMAKE_COMMAND@ -P \"${CTEST_BINARY_DIRECTORY}/cmake/RemoveTemporaryFiles.cmake\"" + "@CMAKE_CTEST_COMMAND@ -D ${DASHBOARD} -A \"${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}\"" +) + +# cmake/RemoveTemporaryFiles.cmake must exist before the CTEST_COMMAND runs +IF(NOT EXISTS ${CTEST_BINARY_DIRECTORY}/cmake/RemoveTemporaryFiles.cmake) + SET(CTEST_CMAKE_COMMAND "@CMAKE_COMMAND@") +ENDIF(NOT EXISTS ${CTEST_BINARY_DIRECTORY}/cmake/RemoveTemporaryFiles.cmake) + +# CMakeCache.txt must exist before the CTEST_COMMAND runs +IF(NOT EXISTS ${CTEST_BINARY_DIRECTORY}/CMakeCache.txt) + SET(CTEST_CMAKE_COMMAND "@CMAKE_COMMAND@") +ENDIF(NOT EXISTS ${CTEST_BINARY_DIRECTORY}/CMakeCache.txt) + +# Specify how long to run the continuous in minutes +IF(${DASHBOARD} STREQUAL "Continuous") + SET (CTEST_CONTINUOUS_DURATION 1080) + SET (CTEST_CONTINUOUS_MINIMUM_INTERVAL 15) +ENDIF(${DASHBOARD} STREQUAL "Continuous") + +# Set defaults +SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY FALSE) +SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE FALSE) + +# For Nightly builds, this should be TRUE +IF(${DASHBOARD} STREQUAL "Nightly") + SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY TRUE) +ELSE(${DASHBOARD} STREQUAL "Nightly") + SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY FALSE) +ENDIF(${DASHBOARD} STREQUAL "Nightly") + +# For Continuous builds. this should be TRUE +IF(${DASHBOARD} STREQUAL "Continuous") + SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE TRUE) +ELSE(${DASHBOARD} STREQUAL "Continuous") + SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE FALSE) +ENDIF(${DASHBOARD} STREQUAL "Continuous") + +# For Experimental builds, these should be FALSE +IF(${DASHBOARD} STREQUAL "Experimental") + SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY FALSE) + SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY_ONCE FALSE) +ENDIF(${DASHBOARD} STREQUAL "Experimental") + +# This is the initial cache to use for the binary tree, be careful to escape +# any quotes inside of this string if you use it +SET (CTEST_INITIAL_CACHE " +SITE:STRING=@SITE@ +BUILDNAME:STRING=@BUILDNAME@ +CVSCOMMAND:FILEPATH=@CVSCOMMAND@ +CVS_UPDATE_OPTIONS:STRING=-d -A -P + +CMAKE_GENERATOR:INTERNAL=@CMAKE_GENERATOR@ +CMAKE_CXX_COMPILER:FILEPATH=@CMAKE_CXX_COMPILER@ +CMAKE_CXX_FLAGS:STRING=@CMAKE_CXX_FLAGS@ +CMAKE_C_COMPILER:FILEPATH=@CMAKE_C_COMPILER@ +CMAKE_C_FLAGS:STRING=@CMAKE_C_FLAGS@ +CMAKE_CONFIGURATION_TYPES:STRING=${BUILD_TYPE} +CMAKE_BUILD_TYPE:STRING=${BUILD_TYPE} + +BUILD_SHARED_LIBS:BOOL=@BUILD_SHARED_LIBS@ +BUILD_TESTING:BOOL=@BUILD_TESTING@ + +RTTB_ENFORCE_MATURE_CODE:BOOL=@RTTB_ENFORCE_MATURE_CODE@ + +") + +# Set any extra envionment variables here +SET (CTEST_ENVIRONMENT + ) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt new file mode 100644 index 0000000..ab86801 --- /dev/null +++ b/code/CMakeLists.txt @@ -0,0 +1,22 @@ +MESSAGE(STATUS "processing RTToolbox code") +ADD_SUBDIRECTORY(core) +ADD_SUBDIRECTORY(algorithms) +ADD_SUBDIRECTORY(indices) + + +OPTION(BUILD_Models "Determine if the model classes will be generated." OFF) +IF(BUILD_Models) +ADD_SUBDIRECTORY(models) +ENDIF(BUILD_Models) + +OPTION(BUILD_Masks "Determine if the mask classes will be generated." OFF) +IF(BUILD_Masks) +ADD_SUBDIRECTORY(masks) +ENDIF(BUILD_Masks) + +OPTION(BUILD_Interpolation "Determine if the dose interpolation classes will be generated." OFF) +IF(BUILD_Interpolation) +ADD_SUBDIRECTORY(interpolation) +ENDIF(BUILD_Interpolation) + +ADD_SUBDIRECTORY(io) diff --git a/code/algorithms/CMakeLists.txt b/code/algorithms/CMakeLists.txt new file mode 100644 index 0000000..ad2fe4a --- /dev/null +++ b/code/algorithms/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBAlgorithms DEPENDS RTTBCore PACKAGE_DEPENDS Boost) \ No newline at end of file diff --git a/code/algorithms/files.cmake b/code/algorithms/files.cmake new file mode 100644 index 0000000..458f698 --- /dev/null +++ b/code/algorithms/files.cmake @@ -0,0 +1,12 @@ +SET(CPP_FILES + rttbDoseStatistics.cpp + rttbArithmetic.cpp + ) + +SET(H_FILES + rttbDoseStatistics.h + rttbArithmetic.h + rttbArithmetic.tpp + rttbBinaryFunctorDoseAccessor.h + rttbBinaryFunctorDoseAccessor.tpp +) diff --git a/code/algorithms/rttbArithmetic.cpp b/code/algorithms/rttbArithmetic.cpp new file mode 100644 index 0000000..9308412 --- /dev/null +++ b/code/algorithms/rttbArithmetic.cpp @@ -0,0 +1,100 @@ +// ----------------------------------------------------------------------- +// 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 "rttbArithmetic.h" + + +namespace rttb +{ + namespace algorithms + { + namespace arithmetic + { + + void add(const DoseAccessorPointer dose1, const DoseAccessorPointer dose2, + MutableDoseAccessorPointer result) + { + doseOp::Add addOP; + arithmetic(dose1, dose2, result, addOP); + } + + void add(const MaskAccessorPointer mask1, const MaskAccessorPointer mask2, + MutableMaskAccessorPointer result) + { + maskOp::Add addOP; + arithmetic(mask1, mask2, result, addOP); + } + + void subtract(const MaskAccessorPointer mask1, const MaskAccessorPointer mask2, + MutableMaskAccessorPointer result) + { + maskOp::Subtract subOP; + arithmetic(mask1, mask2, result, subOP); + } + + void multiply(const DoseAccessorPointer dose, const MaskAccessorPointer mask, + MutableDoseAccessorPointer result) + { + doseMaskOp::Multiply multOP; + arithmetic(dose, mask, result, multOP); + } + + namespace doseOp + { + DoseTypeGy Add::calc(const DoseTypeGy dose1Val, const DoseTypeGy dose2Val) const + { + return dose1Val + dose2Val; + } + + AddWeighted::AddWeighted(const DoseCalcType w1, const DoseCalcType w2): + weight1(w1), weight2(w2) {}; + + DoseTypeGy AddWeighted::calc(const DoseTypeGy dose1Val, const DoseTypeGy dose2Val) const + { + return weight1 * dose1Val + weight2 * dose2Val; + } + } + + namespace doseMaskOp + { + DoseTypeGy Multiply::calc(const DoseTypeGy doseVal, const FractionType maskVal) const + { + return doseVal * maskVal; + } + } + + namespace maskOp + { + FractionType Add::calc(const FractionType mask1Val, const FractionType mask2Val) const + { + FractionType added = mask1Val + mask2Val; + return (1 < added ? 1 : added); + } + + FractionType Subtract::calc(const FractionType mask1Val, const FractionType mask2Val) const + { + FractionType sub = mask1Val - mask2Val; + return (0 > sub ? 0 : sub); + } + } + } + } +} \ No newline at end of file diff --git a/code/algorithms/rttbArithmetic.h b/code/algorithms/rttbArithmetic.h new file mode 100644 index 0000000..33c8793 --- /dev/null +++ b/code/algorithms/rttbArithmetic.h @@ -0,0 +1,159 @@ +// ----------------------------------------------------------------------- +// 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 __ARITHMETIC_H +#define __ARITHMETIC_H + +#include "rttbBaseType.h" +#include "rttbDoseAccessorInterface.h" +#include "rttbMutableMaskAccessorInterface.h" +#include "rttbMutableDoseAccessorInterface.h" +#include "rttbMaskAccessorInterface.h" +#include "rttbMaskVoxel.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" + +namespace rttb +{ + namespace algorithms + { + + namespace arithmetic + { + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef core::MutableDoseAccessorInterface::MutableDoseAccessorPointer MutableDoseAccessorPointer; + typedef core::MutableMaskAccessorInterface::MutableMaskAccessorPointer MutableMaskAccessorPointer; + typedef core::MaskAccessorInterface::MaskAccessorPointer MaskAccessorPointer; + typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + + + /*! Applies the given dose operation to the given doses and stores the result in result + @pre pointers to accessors are not NULL. The geometric Info of the individual accessors must be equal. + @exception NullPointerException thrown if one of the input accessors is NULL. + @exception InvalidParameterException thrown if the geometricInfo of the given accessors does not match. + */ + template + void arithmetic(const DoseAccessorPointer dose1, const DoseAccessorPointer dose2, + MutableDoseAccessorPointer result, TDoseOperation op); + + ///*! Applies the given dose operation to the mapped given doses (transformation given by MatchPoint) and stores the result in result + // @pre pointers to accessors are not NULL. + // @exception NullPointerException thrown if one of the input accessors is NULL. + // @exception TransformationOutsideImageException thrown if the transformation maps to a position outside the original image. + // */ + // template + // void arithmetic (const DoseAccessorPointer dose1, const MappableDoseAccessorPointer dose2, + // MutableDoseAccessorPointer result, TDoseOperation op); + + /*! Applies the given dose-mask operation to the given dose and mask and stores the result in result + @pre pointers to accessors are not NULL. The geometric Info of the individual accessors must be equal. + @exception NullPointerException thrown if one of the input accessors is NULL. + @exception InvalidParameterException thrown if the geometricInfo of the given accessors does not match. + */ + template + void arithmetic(const DoseAccessorPointer dose, const MaskAccessorPointer mask, + MutableDoseAccessorPointer result, TDoseMaskOperation op); + + /*! Applies the given mask operation to the given masks and stores the result in result + @pre pointers to accessors are not NULL. The geometric Info of the individual accessors must be equal. + @exception NullPointerException thrown if one of the input accessors is NULL. + @exception InvalidParameterException thrown if the geometricInfo of the given accessors does not match. + */ + template + void arithmetic(const MaskAccessorPointer mask1, const MaskAccessorPointer mask2, + MutableMaskAccessorPointer result, TMaskOperation op); + + //convenience functions + void add(const DoseAccessorPointer dose1, const DoseAccessorPointer dose2, + MutableDoseAccessorPointer result); + //void add(const DoseAccessorPointer dose1, const MappableDoseAccessorPointer dose2, + // MutableDoseAccessorPointer result); + void add(const MaskAccessorPointer mask1, const MaskAccessorPointer mask2, + MutableMaskAccessorPointer result); + void subtract(const MaskAccessorPointer mask1, const MaskAccessorPointer mask2, + MutableMaskAccessorPointer result); + void multiply(const DoseAccessorPointer dose, const MaskAccessorPointer mask, + MutableDoseAccessorPointer result); + + /*all operation classes need to implement the function calc() that performs the entry wise operation + The operations are sorted into name spaces according to useful application. If the input values are compatible + the operations can also be applied to accessors they were not meant for. + */ + + //Operations for binary-dose-operation template + namespace doseOp + { + class Add + { + public: + DoseTypeGy calc(const DoseTypeGy dose1Val, const DoseTypeGy dose2Val) const; + }; + class AddWeighted + { + private: + DoseCalcType weight1, weight2; + public: + /* ! Constructor initializes weights applied to individual doses values on adding. + */ + AddWeighted(const DoseCalcType w1 = 1, const DoseCalcType w2 = 1); + + DoseTypeGy calc(const DoseTypeGy dose1Val, const DoseTypeGy dose2Val) const; + }; + + } + + //Operations for binary-dose-mask-operation template + namespace doseMaskOp + { + class Multiply + { + public: + DoseTypeGy calc(const DoseTypeGy doseVal, const FractionType maskVal) const; + }; + + } + + //Operations for binary-mask-operation template + //the result of these operations needs to be between 0 and 1 + namespace maskOp + { + class Add + { + public: + FractionType calc(const FractionType mask1Val, const FractionType mask2Val) const; + }; + class Subtract + { + public: + FractionType calc(const FractionType mask1Val, const FractionType mask2Val) const; + }; + + } + }//end namespace arithmetic + + }//end namespace algorithms +}//end namespace core + + +#include "rttbArithmetic.tpp" + +#endif diff --git a/code/algorithms/rttbArithmetic.tpp b/code/algorithms/rttbArithmetic.tpp new file mode 100644 index 0000000..c494ca7 --- /dev/null +++ b/code/algorithms/rttbArithmetic.tpp @@ -0,0 +1,127 @@ +// ----------------------------------------------------------------------- +// 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 __ARITHMETIC_TPP +#define __ARITHMETIC_TPP + +namespace rttb +{ + namespace algorithms + { + namespace arithmetic + { + + template + void arithmetic(const DoseAccessorPointer dose1, const DoseAccessorPointer dose2, + MutableDoseAccessorPointer result, TDoseOperation op) + { + //handle null pointers + if (dose1 == NULL || dose2 == NULL || result == NULL) + { + throw core::NullPointerException("Pointers to input accessors cannot be NULL."); + } + + //handle differences in geometricInfo + if (!(dose1->getGeometricInfo() == dose2->getGeometricInfo() + && dose1->getGeometricInfo() == result->getGeometricInfo())) + { + throw core::InvalidParameterException("The geometricInfo of all given accessors needs to be equal."); + } + + //apply operation op to doses with equal geometricInfo (same grid) + GridSizeType numVoxels = dose1->getGridSize(); + + for (VoxelGridID id = 0; id < numVoxels; ++id) + { + DoseTypeGy opVal = op.calc(dose1->getDoseAt(id), dose2->getDoseAt(id)); + result->setDoseAt(id, opVal); + } + } + + template + void arithmetic(const DoseAccessorPointer dose, const MaskAccessorPointer mask, + MutableDoseAccessorPointer result, TDoseMaskOperation op) + { + //handle null pointers + if (dose == NULL || mask == NULL || result == NULL) + { + throw core::NullPointerException("Pointers to input accessors cannot be NULL."); + } + + //handle differences in geometricInfo + if (!(dose->getGeometricInfo() == mask->getGeometricInfo() + && dose->getGeometricInfo() == result->getGeometricInfo())) + { + throw core::InvalidParameterException("The geometricInfo of all given accessors needs to be equal."); + } + + //apply operation op to accessors with equal geometricInfo (same grid) + core::MaskVoxel mVoxel(0); + GridSizeType numVoxels = dose->getGridSize(); + + for (VoxelGridID id = 0; id < numVoxels; ++id) + { + mask->getMaskAt(id, mVoxel); + DoseTypeGy opVal = op.calc(dose->getDoseAt(id), mVoxel.getRelevantVolumeFraction()); + result->setDoseAt(id, opVal); + } + } + + template + void arithmetic(const MaskAccessorPointer mask1, const MaskAccessorPointer mask2, + MutableMaskAccessorPointer result, TMaskOperation op) + { + //handle null pointers + if (mask1 == NULL || mask2 == NULL || result == NULL) + { + throw core::NullPointerException("Pointers to input accessors cannot be NULL."); + } + + //handle differences in geometricInfo + if (!(mask1->getGeometricInfo() == mask2->getGeometricInfo() + && mask1->getGeometricInfo() == result->getGeometricInfo())) + { + throw core::InvalidParameterException("The geometricInfo of all given accessors needs to be equal."); + } + + //apply operation op to accessors with equal geometricInfo (same grid) + core::MaskVoxel m1Voxel(0); + //initialize results list with mask1 values + result->setRelevantVoxelVector(mask1->getRelevantVoxelVector()); + + MaskVoxelListPointer voxelListMask2 = mask2->getRelevantVoxelVector(); + MaskVoxelList::iterator it = voxelListMask2->begin(); + + while (it != voxelListMask2->end()) + { + mask1->getMaskAt(it->getVoxelGridID(), m1Voxel); + FractionType opVal = op.calc(m1Voxel.getRelevantVolumeFraction(), it->getRelevantVolumeFraction()); + result->setMaskAt(it->getVoxelGridID(), core::MaskVoxel(it->getVoxelGridID(), opVal)); + ++it; + } + + } + + } + } +} +#endif \ No newline at end of file diff --git a/code/algorithms/rttbBinaryFunctorDoseAccessor.h b/code/algorithms/rttbBinaryFunctorDoseAccessor.h new file mode 100644 index 0000000..c10dd23 --- /dev/null +++ b/code/algorithms/rttbBinaryFunctorDoseAccessor.h @@ -0,0 +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) +*/ +#ifndef __BINARY_FUNCTOR_DOSE_ACCESSOR_H +#define __BINARY_FUNCTOR_DOSE_ACCESSOR_H + +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + +namespace rttb +{ + namespace algorithms + { + + /*! @class BinaryFunctorDoseAccessor + @brief Class that allows to access the results of a binary dose operation. + @details this Accessor takes two dose accessors as operants (the operants must have the same geometry) + and computes a resulting dose value be using a given dose operation functor. + The resulting dose value will be returned from the accessor as its value upon request. + @remark this can be seen as a lazy filter pattern, thus the accessor is filtering/operating on dose values upon request. + */ + template + class BinaryFunctorDoseAccessor: public core::DoseAccessorInterface + { + public: + typedef boost::shared_ptr BinaryFunctorDoseAccessorPointer; + protected: + DoseAccessorPointer _spDose1; + DoseAccessorPointer _spDose2; + TDoseOperation _functor; + + public: + /*! @brief Constructor. + @param dose1 pointer to the 1st dose operand + @param dose2 pointer to the 2nd dose operand + @param functor Instance of the operation that should be used + @pre all input parameters have to be valid + @exception core::NullPointerException if one input parameter is NULL or if geometricInfos don't match + */ + BinaryFunctorDoseAccessor(const DoseAccessorPointer dose1, const DoseAccessorPointer dose2, + const TDoseOperation& functor); + + /*! @brief Virtual destructor + */ + virtual ~BinaryFunctorDoseAccessor() {}; + + /*! @pre: the geometricInfo of both doseAccessors are equal + */ + inline const core::GeometricInfo& getGeometricInfo() const + { + return _spDose1->getGeometricInfo(); + }; + + /*! @pre: the geometricInfo of both doseAccessors are equal + */ + inline GridSizeType getGridSize() const + { + return _spDose1->getGeometricInfo().getNumberOfVoxels(); + }; + + /*! @brief Returns the result dose computed by the functor. + It uses the dose values of both operand doses specified via the passed ID. + @return the dose value if inside, -1 else + @pre .calc(dose1,dose2) has to be implemented + */ + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + /*! @brief Returns the result dose computed by the functor. + It uses the dose values of both operand doses specified via the passed index. + @return the dose value if inside, -1 else + @pre .calc(dose1,dose2) has to be implemented + */ + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + //@todo: which UID should be used here? + const IDType getDoseUID() const + { + return IDType(); + } + }; + } +} + +#include "rttbBinaryFunctorDoseAccessor.tpp" + +#endif diff --git a/code/algorithms/rttbBinaryFunctorDoseAccessor.tpp b/code/algorithms/rttbBinaryFunctorDoseAccessor.tpp new file mode 100644 index 0000000..9e43e1a --- /dev/null +++ b/code/algorithms/rttbBinaryFunctorDoseAccessor.tpp @@ -0,0 +1,82 @@ +// ----------------------------------------------------------------------- +// 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 "rttbBinaryFunctorDoseAccessor.h" + +#ifndef __BINARY_FUNCTOR_DOSE_ACCESSOR_TPP +#define __BINARY_FUNCTOR_DOSE_ACCESSOR_TPP + +namespace rttb +{ + namespace algorithms + { + template + BinaryFunctorDoseAccessor::BinaryFunctorDoseAccessor(const DoseAccessorPointer + dose1, const DoseAccessorPointer dose2, + const TDoseOperation& functor) + { + if (dose1 == NULL || dose2 == NULL) + { + throw core::NullPointerException("Pointers to input accessors cannot be NULL."); + } + + if (!(dose1->getGeometricInfo() == dose2->getGeometricInfo())) + { + throw core::InvalidParameterException("The geometricInfo of all given accessors needs to be equal."); + } + + _spDose1 = dose1; + _spDose2 = dose2; + _functor = functor; + } + + template DoseTypeGy BinaryFunctorDoseAccessor::getDoseAt( + const VoxelGridID aID) const + { + if (getGeometricInfo().validID(aID)) + { + DoseTypeGy value = _functor.calc(_spDose1->getDoseAt(aID), _spDose2->getDoseAt(aID)); + return value; + } + else + { + return -1; + } + } + + template DoseTypeGy BinaryFunctorDoseAccessor::getDoseAt( + const VoxelGridIndex3D& aIndex) const + { + VoxelGridID aVoxelGridID; + + if (_spDose1->getGeometricInfo().convert(aIndex, aVoxelGridID)) + { + return getDoseAt(aVoxelGridID); + } + else + { + return -1; + } + } + + } +} +#endif \ No newline at end of file diff --git a/code/algorithms/rttbDoseStatistics.cpp b/code/algorithms/rttbDoseStatistics.cpp new file mode 100644 index 0000000..05bd378 --- /dev/null +++ b/code/algorithms/rttbDoseStatistics.cpp @@ -0,0 +1,387 @@ +// ----------------------------------------------------------------------- +// 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 + +#include "rttbDoseStatistics.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" + +namespace rttb{ + + namespace algorithms{ + DoseStatistics::DoseStatistics() + { + initSuccess=false; + } + + DoseStatistics::DoseStatistics(DoseIteratorPointer aDoseIterator) + { + _doseIterator=aDoseIterator; + initSuccess=false; + this->init(); + } + + void DoseStatistics::setDoseIterator(DoseIteratorPointer aDoseIterator) + { + _doseIterator=aDoseIterator; + initSuccess=false; + this->init(); + } + + DoseStatistics::DoseIteratorPointer DoseStatistics::getDoseIterator() const + { + return _doseIterator; + } + + + bool DoseStatistics::init() { + + if(!_doseIterator) + { + throw core::NullPointerException("_doseIterator must not be NULL!"); + } + + doseVector.clear(); + voxelProportionVector.clear(); + + std::multimap doseValueVSIndexMap; + std::vector voxelProportionVectorTemp; + + + this->_maximum=0; + this->_mean=0; + this->_stdDeviation=0; + this->_variance=0; + + float sum=0; + _numVoxels=0; + float squareSum=0; + + _doseIterator->reset(); + int i=0; + DoseTypeGy doseValue = 0; + while(_doseIterator->isPositionValid()) + { + doseValue = _doseIterator->getCurrentDoseValue(); + + if(i==0) + { + _minimum=doseValue; + } + + rttb::FractionType voxelProportion=_doseIterator->getCurrentRelevantVolumeFraction(); + sum+=doseValue*voxelProportion; + _numVoxels+=voxelProportion; + squareSum+=doseValue*doseValue*voxelProportion; + if(doseValue>this->_maximum) + { + _maximum=doseValue; + } + else if(doseValue_minimum) + { + _minimum=doseValue; + } + + voxelProportionVectorTemp.push_back(voxelProportion); + doseValueVSIndexMap.insert(std::pair(doseValue,i)); + + i++; + _doseIterator->next(); + } + if(_numVoxels!=0) + { + _mean=sum/_numVoxels; + _variance=(squareSum/_numVoxels-_mean*_mean); + if (_variance < errorConstant) + { + _stdDeviation = 0; + } + else + { + _stdDeviation=pow(_variance,0.5); + } + } + + //sort dose values and corresponding volume fractions in member variables + std::multimap::iterator it; + for(it=doseValueVSIndexMap.begin();it!=doseValueVSIndexMap.end();++it) + { + doseVector.push_back((float)(*it).first); + voxelProportionVector.push_back(voxelProportionVectorTemp.at((*it).second)); + } + + initSuccess=true; + + return true; + } + + double DoseStatistics::getNumberOfVoxels(){ + if(!initSuccess) + { + throw core::InvalidDoseException("DoseStatistics is not initialized: set dose using setDoseIterator()! "); + } + return _numVoxels; + } + + DoseStatisticType DoseStatistics::getMaximum(ResultListPointer maxVoxelVector) const{ + if(!initSuccess){ + throw core::InvalidDoseException("DoseStatistics is not initialized: set dose using setDoseIterator()! "); + + } + if (maxVoxelVector==NULL){ + throw core::NullPointerException("resultsVector must not be NULL! "); + } + if(maxVoxelVector->size()==0){ + this->_doseIterator->reset(); + DoseTypeGy doseValue=0; + while(_doseIterator->isPositionValid()) + { + doseValue = _doseIterator->getCurrentDoseValue(); + if(doseValue==_maximum) + { + VoxelGridID currentID = _doseIterator->getCurrentVoxelGridID(); + std::pair voxel (doseValue,currentID); + maxVoxelVector->push_back(voxel); + } + _doseIterator->next(); + } + } + return _maximum; + } + + DoseStatisticType DoseStatistics::getMinimum(ResultListPointer minVoxelVector, int number) const{ + if(!initSuccess) + { + throw core::InvalidDoseException("DoseStatistics is not initialized: set dose using setDoseIterator()! "); + + } + if (minVoxelVector==NULL) + { + throw core::NullPointerException("resultsVector must not be NULL! "); + } + /*! @todo: Architecture Annotation: + Finding the positions for the minimum only once reduces computation time, + but will require sensible use by the programmers. To be save the output vector + minVoxelVector will be always cleared here to garantee that no false values are + presented. This change may be revoced to increase computation speed later on + (only compute if(minVoxelVector->size()==0)). + */ + minVoxelVector->clear(); + int count=0; + this->_doseIterator->reset(); + DoseTypeGy doseValue = 0; + while(_doseIterator->isPositionValid() && countgetCurrentDoseValue(); + if(doseValue==_minimum){ + VoxelGridID currentID = _doseIterator->getCurrentVoxelGridID(); + std::pair voxel (doseValue,currentID); + minVoxelVector->push_back(voxel); + count++; + } + _doseIterator->next(); + } + return _minimum; + } + + DoseStatisticType DoseStatistics::getMean() const{ + if(!initSuccess) + { + throw core::InvalidDoseException("DoseStatistics is not initialized: set dose using setDoseIterator()! "); + } + return _mean; + } + + DoseStatisticType DoseStatistics::getStdDeviation() const{ + if(!initSuccess) + { + throw core::InvalidDoseException("DoseStatistics is not initialized: set dose using setDoseIterator()! "); + } + return _stdDeviation; + } + + DoseStatisticType DoseStatistics::getVariance() const{ + if(!initSuccess) + { + throw core::InvalidDoseException("DoseStatistics is not initialized: set dose using setDoseIterator()! "); + } + return _variance; + } + + VolumeType DoseStatistics::getVx(DoseTypeGy xDoseAbsolute) const{ + rttb::FractionType count=0; + _doseIterator->reset(); + + DoseTypeGy currentDose = 0; + while(_doseIterator->isPositionValid()) + { + currentDose = _doseIterator->getCurrentDoseValue(); + if(currentDose>=xDoseAbsolute) + { + count+=_doseIterator->getCurrentRelevantVolumeFraction(); + } + _doseIterator->next(); + } + return count*this->_doseIterator->getCurrentVoxelVolume(); + } + + DoseTypeGy DoseStatistics::getDx(DoseTypeGy xVolumeAbsolute) const{ + double noOfVoxel=xVolumeAbsolute/_doseIterator->getCurrentVoxelVolume(); + DoseTypeGy resultDose=0; + + double countVoxels=0; + int i=doseVector.size()-1; + for(;i>=0;i--){ + countVoxels+=voxelProportionVector.at(i); + if(countVoxels>=noOfVoxel) + { + break; + } + } + if(i>=0) + { + resultDose=doseVector.at(i); + } + else + { + resultDose=_minimum; + } + + return resultDose; + } + + DoseTypeGy DoseStatistics::getMOHx(DoseTypeGy xVolumeAbsolute) const{ + double noOfVoxel=xVolumeAbsolute/_doseIterator->getCurrentVoxelVolume(); + double countVoxels=0; + double sum=0; + + if(noOfVoxel==0) + { + return 0; + } + else{ + for(int i=doseVector.size()-1;i>=0;i--) + { + double voxelProportion=voxelProportionVector.at(i); + countVoxels+=voxelProportion; + sum+=doseVector.at(i)*voxelProportion; + if(countVoxels>=noOfVoxel) + { + break; + } + } + return (DoseTypeGy)(sum/noOfVoxel); + } + } + + DoseTypeGy DoseStatistics::getMOCx(DoseTypeGy xVolumeAbsolute) const{ + double noOfVoxel=xVolumeAbsolute/_doseIterator->getCurrentVoxelVolume(); + double countVoxels=0; + double sum=0; + + if(noOfVoxel==0) + { + return 0; + } + else + { + std::vector::const_iterator it=doseVector.begin(); + std::vector::const_iterator itD=voxelProportionVector.begin(); + + for(;it!=doseVector.end();it++,itD++) + { + double voxelProportion=*itD; + countVoxels+=voxelProportion; + sum+=(*it)*voxelProportion; + if(countVoxels>=noOfVoxel) + { + break; + } + } + return (DoseTypeGy)(sum/noOfVoxel); + } + } + + DoseTypeGy DoseStatistics::getMaxOHx(DoseTypeGy xVolumeAbsolute) const{ + double noOfVoxel=xVolumeAbsolute/_doseIterator->getCurrentVoxelVolume(); + DoseTypeGy resultDose=0; + + double countVoxels=0; + int i=doseVector.size()-1; + for(;i>=0;i--) + { + countVoxels+=voxelProportionVector.at(i); + if(countVoxels>=noOfVoxel) + { + break; + } + } + if(i-1>=0) + { + resultDose=doseVector.at(i-1); + } + return resultDose; + } + + DoseTypeGy DoseStatistics::getMinOCx(DoseTypeGy xVolumeAbsolute) const{ + double noOfVoxel=xVolumeAbsolute/_doseIterator->getCurrentVoxelVolume(); + DoseTypeGy resultDose=0; + + double countVoxels=0; + std::vector::const_iterator it=doseVector.begin(); + std::vector::const_iterator itD=voxelProportionVector.begin(); + for(;itD!=voxelProportionVector.end();itD++,it++) + { + countVoxels+=*itD; + if(countVoxels>=noOfVoxel) + { + break; + } + } + if(it!=doseVector.end()) + { + it++; + if(it!=doseVector.end()) + { + resultDose=*it; + } + else + { + resultDose=(DoseTypeGy)_maximum; + } + } + else + { + resultDose=(DoseTypeGy)_maximum; + } + return resultDose; + } + + }//end namespace algorithms + }//end namespace rttb + diff --git a/code/algorithms/rttbDoseStatistics.h b/code/algorithms/rttbDoseStatistics.h new file mode 100644 index 0000000..cc7de52 --- /dev/null +++ b/code/algorithms/rttbDoseStatistics.h @@ -0,0 +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) +*/ + +#ifndef __DOSE_STATISTICS_H +#define __DOSE_STATISTICS_H + +#include + +#include "rttbBaseType.h" +#include "rttbDoseIteratorInterface.h" + +namespace rttb{ + + namespace algorithms{ + + /*! @class DoseStatistics + @brief This is a class calculating different statistical values from a rt dose distribution + These values range fram standard statistical values such as minimum, maximum and mean to more + dose specific properties such as Vx (volume iradiated with a dose >=x), Dx (minimal dose delivered + to x% of the VOI, and MOHx (mean in the hottest volume). + */ + class DoseStatistics + { + public: + typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; + typedef boost::shared_ptr > > ResultListPointer; + + private: + DoseIteratorPointer _doseIterator; + DoseStatisticType _maximum; + DoseStatisticType _minimum; + DoseStatisticType _mean; + DoseStatisticType _stdDeviation; + DoseStatisticType _variance; + DoseStatisticType _numVoxels; + + /* Contains relevant dose values sorted in descending order. + */ + std::vector doseVector; + + /*! Contains the corresponding voxel proprtions to the values in doseVector. + */ + std::vector voxelProportionVector; + + bool initSuccess; + + /*! @brief Calculation of basic dose statistics. It will be called in Constructor. + @exception NullPointerException Thrown if _doseIterator is NULL. + */ + bool init(); + + + + public: + /*! @brief Standard Constructor + */ + DoseStatistics(); + + /*! @brief Constructor + */ + DoseStatistics(DoseIteratorPointer aDoseIterator); + + /*! @brief Set new dose data for statistics. Statistics will be re-initialized. + */ + void setDoseIterator(DoseIteratorPointer aDoseIterator); + + /*! @brief Get dose iterator. + */ + DoseIteratorPointer getDoseIterator() const; + + + /*! @brief Get number of voxels in doseIterator, with sub-voxel accuracy. + @exception InvalidDoseException Thrown if statistic was not initialized. + */ + DoseStatisticType getNumberOfVoxels(); + + /*! @brief Get the maximum of the current dose distribution. + @param maxVoxelVector: vector of all voxel with the dose=maximum. + @return Return the maximum dose in Gy, or -1 if an error occured. + @exception InvalidDoseException Thrown if statistic was not initialized. + */ + DoseStatisticType getMaximum( ResultListPointer maxVoxelVector) const; + + /*! @brief Get the minimum of the current dose distribution. + @param minVoxelVector: vector of all voxel with the dose=minimum. + @param number: the number of dose voxels with dose=minimum that are stored in minVoxelVector. + Only the first occurences are stored. The default value is 100. + @return Return the minimum dose in Gy, or -1 if an error occured. + @exception InvalidDoseException Thrown if statistic was not initialized. + */ + DoseStatisticType getMinimum( ResultListPointer minVoxelVector, int number=100) const; + + /*! @brief Get the mean of the current dose distribution. + @return Return the mean dose in Gy, or -1 if an error occured. + @exception InvalidDoseException Thrown if statistic was not initialized. + */ + DoseStatisticType getMean() const; + + /*! @brief Get the standard deviation of the current dose distribution. + @return Return the standard deviation in Gy, or -1 if an error occured. + @exception InvalidDoseException Thrown if statistic was not initialized. + */ + DoseStatisticType getStdDeviation() const; + + /*! @brief Get the variance of of the current dose distribution. + @return Return the variance in Gy, or -1 if an error occured. + @exception InvalidDoseException Thrown if statistic was not initialized. + */ + DoseStatisticType getVariance() const; + + /*! @brief Get Vx: the volume irradiated with a dose >= x. + @return Return absolute volume in absolute cm3. + */ + VolumeType getVx(DoseTypeGy xDoseAbsolute) const; + + /*! @brief Get Dx: the minimal dose delivered to part x of the current volume. + @return Return dose value in Gy. + */ + DoseTypeGy getDx(VolumeType xVolumeAbsolute) const; + + /*! @brief Get MOHx: mean dose of the hottest x voxels. + @return Return dose value in Gy. + */ + DoseTypeGy getMOHx(VolumeType xVolumeAbsolute) const; + + /*! @brief Get MOCx: mean dose of the coldest x voxels. + @return Return dose value in Gy. + */ + DoseTypeGy getMOCx(VolumeType xVolumeAbsolute) const; + + /*! @brief Get MaxOHx: Maximum outside of the hottest x voxels. + @return Return dose value in Gy. + */ + DoseTypeGy getMaxOHx(VolumeType xVolumeAbsolute) const; + + /*! @brief Get MinOCx: Minimum outside of the coldest x voxels. + @return Return ose value in Gy. + */ + DoseTypeGy getMinOCx(VolumeType xVolumeAbsolute) const; + }; + + } + } + + +#endif diff --git a/code/core/CMakeLists.txt b/code/core/CMakeLists.txt new file mode 100644 index 0000000..5095f4e --- /dev/null +++ b/code/core/CMakeLists.txt @@ -0,0 +1,2 @@ +RTTB_CREATE_MODULE(RTTBCore PACKAGE_DEPENDS Boost) + diff --git a/code/core/files.cmake b/code/core/files.cmake new file mode 100644 index 0000000..89f189f --- /dev/null +++ b/code/core/files.cmake @@ -0,0 +1,56 @@ +SET(CPP_FILES + rttbDoseIteratorInterface.cpp + rttbDVH.cpp + rttbDVHCalculator.cpp + rttbDVHSet.cpp + rttbException.cpp + rttbGenericDoseIterator.cpp + rttbGenericMaskedDoseIterator.cpp + rttbGeometricInfo.cpp + rttbIndexOutOfBoundsException.cpp + rttbInvalidDoseException.cpp + rttbInvalidParameterException.cpp + rttbMappingOutsideOfImageException.cpp + rttbMaskedDoseIteratorInterface.cpp + rttbMaskVoxel.cpp + rttbNullPointerException.cpp + rttbPaddingException.cpp + rttbPhysicalInfo.cpp + rttbStructure.cpp + rttbStructureSet.cpp + rttbStrVectorStructureSetGenerator.cpp + ) + +SET(H_FILES + rttbBaseType.h + rttbDoseAccessorGeneratorBase.h + rttbDoseAccessorGeneratorInterface.h + rttbDoseAccessorInterface.h + rttbDoseIteratorInterface.h + rttbDVH.h + rttbDVHCalculator.h + rttbDVHGeneratorInterface.h + rttbDVHSet.h + rttbException.h + rttbExceptionMacros.h + rttbGenericDoseIterator.h + rttbGenericMaskedDoseIterator.h + rttbGeometricInfo.h + rttbIndexConversionInterface.h + rttbIndexOutOfBoundsException.h + rttbInvalidDoseException.h + rttbInvalidParameterException.h + rttbMappingOutsideOfImageException.h + rttbMaskAccessorInterface.h + rttbMaskedDoseIteratorInterface.h + rttbMaskVoxel.h + rttbMutableDoseAccessorInterface.h + rttbMutableMaskAccessorInterface.h + rttbNullPointerException.h + rttbPaddingException.h + rttbPhysicalInfo.h + rttbStructure.h + rttbStructureSet.h + rttbStructureSetGeneratorInterface.h + rttbStrVectorStructureSetGenerator.h +) diff --git a/code/core/rttbBaseType.h b/code/core/rttbBaseType.h new file mode 100644 index 0000000..4763c46 --- /dev/null +++ b/code/core/rttbBaseType.h @@ -0,0 +1,719 @@ +// ----------------------------------------------------------------------- +// 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 __BASE_TYPE_NEW_H +#define __BASE_TYPE_NEW_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace rttb +{ + + const double errorConstant = 1e-5; + + typedef unsigned short UnsignedIndex1D; + + /*! @class UnsignedIndex2D + @brief 2D index. + @todo Both UnsignedIndex2D and VoxelGridIndex2D required? + */ + class UnsignedIndex2D: public boost::numeric::ublas::vector + { + public: + UnsignedIndex2D() : boost::numeric::ublas::vector(2) {} + UnsignedIndex2D(const UnsignedIndex1D value) : boost::numeric::ublas::vector(2, + value) {} + + const UnsignedIndex1D x() const + { + return (*this)(0); + } + const UnsignedIndex1D y() const + { + return (*this)(1); + } + }; + + /*! @class UnsignedIndex3D + @brief 3D index. + @todo Both UnsignedIndex3D and VoxelGridIndex3D required? + */ + class UnsignedIndex3D: public boost::numeric::ublas::vector + { + public: + UnsignedIndex3D() : boost::numeric::ublas::vector(3) {} + UnsignedIndex3D(const UnsignedIndex1D value) : boost::numeric::ublas::vector(3, + value) {} + UnsignedIndex3D(const UnsignedIndex1D xValue, const UnsignedIndex1D yValue, + const UnsignedIndex1D zValue) + : boost::numeric::ublas::vector(3, xValue) + { + (*this)(1) = yValue; + (*this)(2) = zValue; + } + + const UnsignedIndex1D x() const + { + return (*this)(0); + } + const UnsignedIndex1D y() const + { + return (*this)(1); + } + const UnsignedIndex1D z() const + { + return (*this)(2); + } + friend bool operator==(const UnsignedIndex3D& gi1, const UnsignedIndex3D& gi2) + { + if (gi1.size() != gi2.size()) + { + return false; + } + + for (int i = 0; i < gi1.size(); i++) + { + if (gi1(i) != gi2(i)) + { + return false; + } + } + + return true; + } + + friend std::ostream& operator<<(std::ostream& s, const UnsignedIndex3D& aVector) + { + s << "[ " << aVector(0) << ", " << aVector(1) << ", " << aVector(2) << " ]"; + return s; + } + }; + + typedef std::list UnsignedIndexList; + + typedef std::string FileNameString; + + typedef std::string ContourGeometricTypeString; + + typedef double WorldCoordinate; + + + /*! @class WorldCoordinate2D + @brief 2D coordinate in real world coordinates. + */ + class WorldCoordinate2D: public boost::numeric::ublas::vector + { + public: + WorldCoordinate2D() : boost::numeric::ublas::vector (2) {} + WorldCoordinate2D(const WorldCoordinate value) : boost::numeric::ublas::vector(2, + value) {} + WorldCoordinate2D(const WorldCoordinate xValue, const WorldCoordinate yValue) + : boost::numeric::ublas::vector(2, xValue) + { + (*this)(1) = yValue; + } + + const WorldCoordinate x() const + { + return (*this)(0); + } + const WorldCoordinate y() const + { + return (*this)(1); + } + + const std::string toString() const + { + std::stringstream ss; + ss << x() << ' ' << y(); + return ss.str(); + } + + friend bool operator==(const WorldCoordinate2D& wc1, const WorldCoordinate2D& wc2) + { + if (wc1.size() != wc2.size()) + { + return false; + } + + for (int i = 0; i < wc1.size(); i++) + { + if (wc1(i) != wc2(i)) + { + return false; + } + } + + return true; + } + }; + + + /*! @class WorldCoordinate3D + @brief 3D coordinate in real world coordinates. + */ + class WorldCoordinate3D: public boost::numeric::ublas::vector + { + public: + WorldCoordinate3D() : boost::numeric::ublas::vector(3) {} + WorldCoordinate3D(const WorldCoordinate value) : boost::numeric::ublas::vector(3, + value) {} + WorldCoordinate3D(const WorldCoordinate xValue, const WorldCoordinate yValue, + const WorldCoordinate zValue) + : boost::numeric::ublas::vector(3, xValue) + { + (*this)(1) = yValue; + (*this)(2) = zValue; + } + WorldCoordinate3D(const WorldCoordinate3D& w): boost::numeric::ublas::vector(3) + { + (*this)(0) = w.x(); + (*this)(1) = w.y(); + (*this)(2) = w.z(); + } + + const WorldCoordinate x() const + { + return (*this)(0); + } + const WorldCoordinate y() const + { + return (*this)(1); + } + const WorldCoordinate z() const + { + return (*this)(2); + } + + //vector cross product (not included in boost.ublas) + WorldCoordinate3D cross(WorldCoordinate3D aVector) const + { + WorldCoordinate3D result; + WorldCoordinate x = (*this)(0); + WorldCoordinate y = (*this)(1); + WorldCoordinate z = (*this)(2); + + result(0) = y * aVector(2) - z * aVector(1); + result(1) = z * aVector(0) - x * aVector(2); + result(2) = x * aVector(1) - y * aVector(0); + + return result; + } + + WorldCoordinate2D getXY() const + { + WorldCoordinate2D result; + + result(0) = (*this)(0); + result(1) = (*this)(1); + + return result; + } + + const std::string toString() const + { + std::stringstream ss; + ss << x() << ' ' << y() << ' ' << z(); + return ss.str(); + } + + WorldCoordinate3D& operator=(const WorldCoordinate3D& wc) + { + (*this)(0) = wc.x(); + (*this)(1) = wc.y(); + (*this)(2) = wc.z(); + return (*this); + } + + WorldCoordinate3D& operator=(const boost::numeric::ublas::vector wc) + { + (*this)(0) = wc(0); + (*this)(1) = wc(1); + (*this)(2) = wc(2); + return (*this); + } + + WorldCoordinate3D operator-(const boost::numeric::ublas::vector wc) + { + return WorldCoordinate3D((*this)(0) - wc(0), (*this)(1) - wc(1), (*this)(2) - wc(2)); + } + + WorldCoordinate3D operator+(const boost::numeric::ublas::vector wc) + { + return WorldCoordinate3D((*this)(0) + wc(0), (*this)(1) + wc(1), (*this)(2) + wc(2)); + } + + friend bool operator==(const WorldCoordinate3D& wc1, const WorldCoordinate3D& wc2) + { + if (wc1.size() != wc2.size()) + { + return false; + } + + for (int i = 0; i < wc1.size(); i++) + { + if (wc1(i) != wc2(i)) + { + return false; + } + } + + return true; + } + + friend std::ostream& operator<<(std::ostream& s, const WorldCoordinate3D& aVector) + { + s << "[ " << aVector(0) << ", " << aVector(1) << ", " << aVector(2) << " ]"; + return s; + } + + }; + + typedef UnsignedIndex3D ImageSize; + + /*! @deprecated Use OrientationMatrix instead. + */ + typedef WorldCoordinate3D ImageOrientation; + + typedef double GridVolumeType; + + + + /*! @class SpacingVectorType3D + @brief 3D spacing vector. + @pre values of this vector may not be negative. + @todo should SpacingVectorType/GridVolumeType be forced to be non-negative? If yes add corresponding tests + */ + class SpacingVectorType3D: public boost::numeric::ublas::vector + { + public: + SpacingVectorType3D() : boost::numeric::ublas::vector(3) {} + SpacingVectorType3D(const GridVolumeType value) : boost::numeric::ublas::vector(3, + value) {} + SpacingVectorType3D(const GridVolumeType xValue, const GridVolumeType yValue, + const GridVolumeType zValue) + : boost::numeric::ublas::vector(3, xValue) + { + (*this)(1) = yValue; + (*this)(2) = zValue; + } + SpacingVectorType3D(const SpacingVectorType3D& w): boost::numeric::ublas::vector(3) + { + (*this)(0) = w.x(); + (*this)(1) = w.y(); + (*this)(2) = w.z(); + } + + const GridVolumeType x() const + { + return (*this)(0); + } + + const GridVolumeType y() const + { + return (*this)(1); + } + + const GridVolumeType z() const + { + return (*this)(2); + } + + const std::string toString() const + { + std::stringstream ss; + ss << x() << ' ' << y() << ' ' << z(); + return ss.str(); + } + + SpacingVectorType3D& operator=(const SpacingVectorType3D& wc) + { + (*this)(0) = wc.x(); + (*this)(1) = wc.y(); + (*this)(2) = wc.z(); + return (*this); + } + + SpacingVectorType3D& operator=(const WorldCoordinate3D& wc) + { + (*this)(0) = GridVolumeType(wc.x()); + (*this)(1) = GridVolumeType(wc.y()); + (*this)(2) = GridVolumeType(wc.z()); + return (*this); + } + + SpacingVectorType3D& operator=(const boost::numeric::ublas::vector wc) + { + (*this)(0) = wc(0); + (*this)(1) = wc(1); + (*this)(2) = wc(2); + return (*this); + } + + friend bool operator==(const SpacingVectorType3D& wc1, const SpacingVectorType3D& wc2) + { + if (wc1.size() != wc2.size()) + { + return false; + } + + for (int i = 0; i < wc1.size(); i++) + { + if (wc1(i) != wc2(i)) + { + return false; + } + } + + return true; + } + + friend std::ostream& operator<<(std::ostream& s, const SpacingVectorType3D& aVector) + { + s << "[ " << aVector(0) << ", " << aVector(1) << ", " << aVector(2) << " ]"; + return s; + } + }; + + /*! @class OrientationMatrix + @brief Used to store image orientation information + @todo should OrientationMatrix be forced to be non-negative? If yes add corresponding tests + */ + class OrientationMatrix : public boost::numeric::ublas::matrix + { + public: + /*! The default constructor generates a 3x3 unit matrix + */ + OrientationMatrix() : boost::numeric::ublas::matrix(3, 3, 0) + { + for (std::size_t m = 0; m < (*this).size1(); m++) + { + (*this)(m, m) = 1; + } + } + OrientationMatrix(const WorldCoordinate value) : boost::numeric::ublas::matrix(3, + 3, value) {} + + bool equal(const OrientationMatrix& anOrientationMatrix) const + { + if (anOrientationMatrix.size1() == (*this).size1()) + { + if (anOrientationMatrix.size2() == (*this).size2()) + { + for (std::size_t m = 0; m < anOrientationMatrix.size1(); m++) + { + for (std::size_t n = 0; n < anOrientationMatrix.size2(); n++) + { + if (!((*this)(m, n) == anOrientationMatrix(m, n))) + { + return false; + } + } + }// end element comparison + } + else + { + return false; + } + } + else + { + return false; + } + + return true; + } + + friend bool operator==(const OrientationMatrix& om1, const OrientationMatrix& om2) + { + return om1.equal(om2); + } + + friend std::ostream& operator<<(std::ostream& s, const OrientationMatrix& anOrientationMatrix) + { + s << "[ "; + + for (std::size_t m = 0; m < anOrientationMatrix.size1(); m++) + { + s << "[ "; + + for (std::size_t n = 0; n < anOrientationMatrix.size2(); n++) + { + if (n == 0) + { + s << anOrientationMatrix(m, n); + } + else + { + s << ", " << anOrientationMatrix(m, n); + } + } + + s << " ]"; + } + + s << " ]"; + return s; + } + }; + + + /*! base for 2D and 3D VoxelIndex; Therefore required beside VoxelGridID + */ + typedef unsigned int GridIndexType; + + /*! @class VoxelGridIndex3D + @brief 3D voxel grid index. + */ + class VoxelGridIndex3D: public boost::numeric::ublas::vector + { + public: + VoxelGridIndex3D() : boost::numeric::ublas::vector(3) {} + VoxelGridIndex3D(const GridIndexType value) : boost::numeric::ublas::vector(3, + value) {} + VoxelGridIndex3D(const GridIndexType xValue, const GridIndexType yValue, const GridIndexType zValue) + : boost::numeric::ublas::vector(3, xValue) + { + (*this)(1) = yValue; + (*this)(2) = zValue; + } + + const GridIndexType x() const + { + return (*this)(0); + } + const GridIndexType y() const + { + return (*this)(1); + } + const GridIndexType z() const + { + return (*this)(2); + } + + const std::string toString() const + { + std::stringstream ss; + ss << x() << ' ' << y() << ' ' << z(); + return ss.str(); + } + + VoxelGridIndex3D& operator=(const UnsignedIndex3D ui) + { + (*this)(0) = ui(0); + (*this)(1) = ui(1); + (*this)(2) = ui(2); + return (*this); + } + + friend bool operator==(const VoxelGridIndex3D& gi1, const VoxelGridIndex3D& gi2) + { + if (gi1.size() != gi2.size()) + { + return false; + } + + for (int i = 0; i < gi1.size(); i++) + { + if (gi1(i) != gi2(i)) + { + return false; + } + } + + return true; + } + + friend std::ostream& operator<<(std::ostream& s, const VoxelGridIndex3D& aVector) + { + s << "[ " << aVector(0) << ", " << aVector(1) << ", " << aVector(2) << " ]"; + return s; + } + }; + + + /*! @class VoxelGridIndex3D + @brief 2D voxel grid index. + */ + class VoxelGridIndex2D: public boost::numeric::ublas::vector + { + public: + VoxelGridIndex2D() : boost::numeric::ublas::vector(2) {} + VoxelGridIndex2D(const GridIndexType value) : boost::numeric::ublas::vector(2, + value) {} + VoxelGridIndex2D(const GridIndexType xValue, const GridIndexType yValue) + : boost::numeric::ublas::vector(2, xValue) + { + (*this)(1) = yValue; + } + + const GridIndexType x() const + { + return (*this)(0); + } + const GridIndexType y() const + { + return (*this)(1); + } + + const std::string toString() const + { + std::stringstream ss; + ss << x() << ' ' << y(); + return ss.str(); + } + + friend bool operator==(const VoxelGridIndex2D& gi1, const VoxelGridIndex2D& gi2) + { + if (gi1.size() != gi2.size()) + { + return false; + } + + for (int i = 0; i < gi1.size(); i++) + { + if (gi1(i) != gi2(i)) + { + return false; + } + } + + return true; + } + + friend std::ostream& operator<<(std::ostream& s, const VoxelGridIndex2D& aVector) + { + s << "[ " << aVector(0) << ", " << aVector(1) << " ]"; + return s; + } + }; + + typedef long GridSizeType; + + typedef int VoxelGridID; //starts from 0 and is continuously counting all positions on the grid + typedef int VoxelGridDimensionType; + + typedef boost::numeric::ublas::vector VoxelGridDimensionVectorType; + + typedef double FractionType, VoxelSizeType, DVHVoxelNumber; + + typedef double DoseCalcType, DoseTypeGy, DoseVoxelVolumeType, VolumeType, GridVolumeType; + + typedef std::string IDType; + + typedef std::string StructureLabel; + + struct DVHRole + { + enum Type + { + TargetVolume = 1, + HealthyTissue = 2, + WholeVolume = 4, + UserDefined = 128 + } Type; + }; + + struct DVHType + { + enum Type + { + Differential = 1, + Cumulative = 2 + } Type; + }; + + typedef std::string PatientInfoString; + + typedef std::string TimeString; + + typedef std::string FileNameType; + + typedef std::vector PolygonType; + + typedef std::vector PolygonSequenceType; + + typedef double IndexValueType; + + typedef double DoseStatisticType; + + typedef std::string DBStringType; + + typedef std::string VirtuosFileNameString, DICOMRTFileNameString; + + typedef unsigned short Uint16; + + struct Area2D + { + + WorldCoordinate x_begin; + WorldCoordinate x_end; + WorldCoordinate y_begin; + WorldCoordinate y_end; + VoxelGridIndex2D index_begin; + VoxelGridIndex2D index_end; + + void Init() + { + x_begin = -1000000; + x_end = -1000000; + y_begin = -1000000; + y_end = -1000000; + index_begin(0) = 0; + index_begin(1) = 0; + index_end(0) = 0; + index_end(1) = 0; + } + }; + + struct Segment2D + { + WorldCoordinate2D begin; + WorldCoordinate2D end; + }; + + struct Segment3D + { + WorldCoordinate3D begin; + WorldCoordinate3D end; + }; + + typedef int DistributionType; + + typedef std::string PathType; + + typedef std::string XMLString, StatisticsString; + + +}//end: namespace rttb + +#endif + diff --git a/code/core/rttbDVH.cpp b/code/core/rttbDVH.cpp new file mode 100644 index 0000000..a12b7c3 --- /dev/null +++ b/code/core/rttbDVH.cpp @@ -0,0 +1,372 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDVH.h" +#include "rttbException.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace core{ + + DVH::~DVH(){} + + DVH::DVH(const DataDifferentialType& aDataDifferential, const DoseTypeGy& aDeltaD, const DoseVoxelVolumeType& aDeltaV, + IDType aStructureID, IDType aDoseID): _deltaD(aDeltaD), _deltaV(aDeltaV), _structureID(aStructureID), + _doseID(aDoseID), _voxelizationID("NONE") + { + _dataDifferential.clear(); + _dataDifferential = aDataDifferential; + + this->init(); + } + + DVH::DVH(const DataDifferentialType& aDataDifferential, DoseTypeGy aDeltaD, DoseVoxelVolumeType aDeltaV, + IDType aStructureID, IDType aDoseID, IDType aVoxelizationID): _deltaD(aDeltaD), _deltaV(aDeltaV), + _structureID(aStructureID), _doseID(aDoseID), _voxelizationID(aVoxelizationID) + { + _dataDifferential.clear(); + _dataDifferential = aDataDifferential; + + this->init(); + } + + DVH::DVH(const DVH ©){ + _deltaD=copy._deltaD; + _deltaV=copy._deltaV; + _structureID=copy._structureID; + _doseID=copy._doseID; + _voxelizationID=copy._voxelizationID; + _label=copy._label; + + _dataDifferential.clear(); + _dataDifferential = copy._dataDifferential; + + this->init(); + } + + DVH &DVH::operator=(const DVH ©) + { + if (this != ©) + { + _deltaD=copy._deltaD; + _deltaV=copy._deltaV; + _structureID=copy._structureID; + _doseID=copy._doseID; + _voxelizationID=copy._voxelizationID; + _label=copy._label; + + _dataDifferential.clear(); + _dataDifferential = copy._dataDifferential; + + } + this->init(); + return *this; + } + + bool operator==(const DVH &aDVH, const DVH &otherDVH){ + if (aDVH.getStructureID() != otherDVH.getStructureID()) + { + return false; + } + if (aDVH.getDoseID() != otherDVH.getDoseID()) + { + return false; + } + if (aDVH.getVoxelizationID() != otherDVH.getVoxelizationID()) + { + return false; + } + if (aDVH.getNumberOfVoxels() != otherDVH.getNumberOfVoxels()) + { + return false; + } + return true; + } + + std::ostream& operator<<(std::ostream& s, const DVH &aDVH){ + s << "[ " << aDVH.getStructureID() << ", " << aDVH.getDoseID() << ", " << aDVH.getVoxelizationID() << ", " < DVH::getDataDifferential(bool relativeVolume) const + { + if(!relativeVolume) + { + return _dataDifferential; + } + else + { + return _dataDifferentialRelative; + } + } + + DoseVoxelVolumeType DVH::getDeltaV() const{ + return _deltaV; + } + + DoseTypeGy DVH::getDeltaD() const{ + return _deltaD; + } + + IDType DVH::getDoseID() const{ + return this->_doseID; + } + + IDType DVH::getStructureID() const{ + return this->_structureID; + } + + IDType DVH::getVoxelizationID() const{ + return this->_voxelizationID; + } + + void DVH::setDoseID(IDType aDoseID){_doseID=aDoseID;} + + void DVH::setStructureID(IDType aStrID){_structureID=aStrID;} + + DoseStatisticType DVH::getMaximum() const{ + return _maximum; + } + + DoseStatisticType DVH::getMinimum() const{ + return _minimum; + } + + DoseStatisticType DVH::getMean() const{ + return _mean; + } + + DVHVoxelNumber DVH::getNumberOfVoxels() const{ + return _numberOfVoxels; + } + + DoseStatisticType DVH::getStdDeviation() const{ + return _stdDeviation; + } + + DoseStatisticType DVH::getVariance() const{ + return _variance; + } + + void DVH::init() { + if(_deltaD==0 || _deltaV==0) + { + throw InvalidParameterException("DVH init error: neither _deltaD nor _deltaV must be zero!"); + } + if(this->_dataDifferential.empty()) + { + throw InvalidParameterException("DVH init error: data differential is empty!"); + } + + double sum=0; + double squareSum=0; + _numberOfVoxels=0; + _maximum=0; + _minimum=0; + _dataCumulative.clear(); + this->_dataCumulativeRelative.clear(); + this->_dataDifferentialRelative.clear(); + + DataDifferentialType::iterator it; + + int i=0; + for(it=_dataDifferential.begin();it!=_dataDifferential.end(); it++) + { + _numberOfVoxels+=(*it); + + if((*it)>0) + { + _maximum=(i+0.5)*this->_deltaD; + } + + if((_minimum==0.0f) && ((*it)>0)) + { + _minimum=(i+0.5)*this->_deltaD; + } + + sum+=(*it)*(i+0.5)*this->_deltaD; + + squareSum+=(*it)*pow((i+0.5)*this->_deltaD,2); + + i++; + } + + _mean=sum/_numberOfVoxels; + + for(it=_dataDifferential.begin();it!=_dataDifferential.end(); it++) + { + DoseCalcType datai=((*it)*1.0/_numberOfVoxels); + _dataDifferentialRelative.push_back(datai); + } + + _variance=(squareSum/_numberOfVoxels-_mean*_mean); + _stdDeviation=pow(_variance,0.5); + + _dataCumulative=this->calcCumulativeDVH(); + } + + std::deque DVH::calcCumulativeDVH(bool relativeVolume) + { + + _dataCumulative.clear(); + _dataCumulativeRelative.clear(); + + DoseCalcType cumulativeDVHi = 0; + DataDifferentialType::iterator it; + + size_t size=_dataDifferential.size(); + for(int i=0;igetNumberOfVoxels()); + } + } + if(!relativeVolume) + { + return _dataCumulative; + } + else + { + return _dataCumulativeRelative; + } + } + + DoseStatisticType DVH::getMedian() const{ + + double median_voxel=0; + int median_i=0; + + for(GridIndexType i=0;i_dataDifferential.size();i++) + { + if(median_voxel<(_numberOfVoxels-median_voxel)) + { + median_voxel+=_dataDifferential[i]; + median_i=i; + } + } + double median=(median_i+0.5)*this->_deltaD; + return median; + } + + DoseStatisticType DVH::getModal() const{ + + double modal_voxel=0; + int modal_i=0; + + for(GridIndexType i=0;i_dataDifferential.size();i++) + { + if(modal_voxel<_dataDifferential[i]) + { + modal_voxel=_dataDifferential[i]; + modal_i=i; + } + } + double modal=(modal_i+0.5)*this->_deltaD; + return modal; + } + + VolumeType DVH::getVx(DoseTypeGy xDoseAbsolute){ + + GridIndexType i=static_cast(xDoseAbsolute/_deltaD); + + if(i<_dataCumulative.size()) + { + VolumeType vx=(_dataCumulative.at(i)); + vx = (vx*this->_deltaV); + return vx; + } + else if(i<_dataCumulativeRelative.size()) + { + VolumeType vx=(_dataCumulativeRelative.at(i)); + vx = (vx*this->_deltaV); + return vx; + } + else + { + return 0; + } + } + + DoseTypeGy DVH::getDx(VolumeType xVolumeAbsolute){ + + GridIndexType i=0; + if (!_dataCumulative.empty()) + { + for(; i<_dataCumulative.size();i++) + { + double volumeAbsoluteI=_dataCumulative[i]*this->_deltaV; + if(xVolumeAbsolute>volumeAbsoluteI) + { + break; + } + } + } + else + { + for(; i<_dataCumulativeRelative.size();i++) + { + double volumeAbsoluteI=_dataCumulativeRelative[i]*this->_deltaV; + if(xVolumeAbsolute/this->getNumberOfVoxels()>volumeAbsoluteI) + { + break; + } + } + } + if(i<=_dataCumulative.size() && i>0) + { + DoseTypeGy dx=(i-1)*this->_deltaD; + return dx; + } + else if(i<_dataCumulativeRelative.size()&& i>0) + { + DoseTypeGy dx=(i-1)*this->_deltaD; + return dx; + } + else + { + return 0; + } + } + + VolumeType DVH::getAbsoluteVolume(int relativePercent){ + return (relativePercent*getNumberOfVoxels()*getDeltaV()/100.0); + } + + void DVH::setLabel(StructureLabel aLabel){ + _label=aLabel; + } + + StructureLabel DVH::getLabel() const{ + return _label; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbDVH.h b/code/core/rttbDVH.h new file mode 100644 index 0000000..214e50b --- /dev/null +++ b/code/core/rttbDVH.h @@ -0,0 +1,193 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_H +#define __DVH_H + +#include +#include + +#include "rttbBaseType.h" +#include "rttbStructure.h" + +namespace rttb{ + namespace core{ + + class Structure; + + /*! @class DVH + @brief This is a class representing a dose volume histogram (DVH) + */ + class DVH + { + public: + typedef std::deque DataDifferentialType; + typedef boost::shared_ptr DVHPointer; + + private: + IDType _structureID; + IDType _doseID; + IDType _voxelizationID; + /*! @brief Differential dvh data index is the dose bin, value is the voxel number (sub voxel accuracy) + of the dose bin + */ + DataDifferentialType _dataDifferential; + /*! @brief Differential dvh data relative to the total number of voxels + */ + DataDifferentialType _dataDifferentialRelative; + /*! @brief Volume of a voxel in cm3 + */ + DoseVoxelVolumeType _deltaV; + /*! @brief Absolute dose value of a dose-bin in Gy + */ + DoseTypeGy _deltaD; + + StructureLabel _label; + + DoseStatisticType _maximum; + DoseStatisticType _minimum; + DoseStatisticType _mean; + DoseStatisticType _modal; + DVHVoxelNumber _numberOfVoxels; + DoseStatisticType _median; + DoseStatisticType _stdDeviation; + DoseStatisticType _variance; + DataDifferentialType _dataCumulative; + DataDifferentialType _dataCumulativeRelative; + + /*! @brief DVH initialisation + The DVH is initialized and all statistical values are calculated. + @throw if _deltaV or _deltaD are zero + @throw is _data differential is empty + */ + void init(); + + + public: + ~DVH(); + + /*! + @throw if _deltaV or _deltaD are zero + @throw is _data differential is empty + */ + DVH(const DataDifferentialType& aDataDifferential, const DoseTypeGy& aDeltaD, const DoseVoxelVolumeType& aDeltaV, + IDType aStructureID, IDType aDoseID); + + /*! + @throw if _deltaV or _deltaD are zero + @throw is _data differential is empty + */ + DVH(const DataDifferentialType& aDataDifferential, DoseTypeGy aDeltaD, DoseVoxelVolumeType aDeltaV, + IDType aStructureID, IDType aDoseID, IDType aVoxelizationID); + + DVH(const DVH ©); + + /*! + @throw if _deltaV or _deltaD are zero + @throw is _data differential is empty + */ + DVH &operator=(const DVH ©); + + /*! equality operator + DVHs are considered equal if they have the same structure, dose and voxelization ID + and the same number of voxels. + */ + bool friend operator==(const DVH &aDVH, const DVH &otherDVH); + + friend std::ostream& operator<<(std::ostream& s, const DVH &aDVH); + + void setLabel(StructureLabel aLabel); + StructureLabel getLabel() const; + + /*! @param relativeVolume default false-> Value is the voxel number of the dose bin; + if true-> value is the relative volume % between 0 and 1, + (the voxel number of this dose bin)/(number of voxels) + @return Return differential data of the dvh (relative or abolute depending on the + input parameter). + */ + DataDifferentialType getDataDifferential(bool relativeVolume=false) const; + + DoseVoxelVolumeType getDeltaV() const; + DoseTypeGy getDeltaD() const; + IDType getStructureID() const; + IDType getDoseID() const; + IDType getVoxelizationID() const; + + /*! @todo Should it be possible to set the DoseID externally? Should this be only possible in the + DVHCalculator and corresponding IO classes? + */ + void setDoseID(IDType aDoseID); + /*! @todo Should it be possible to set the StructureID externally? + */ + void setStructureID(IDType aStrID); + + /*! @brief Calculate number of the voxels (with sub voxel accuracy) + @return Return -1 if not initialized + */ + DVHVoxelNumber getNumberOfVoxels() const; + + /*! @brief Get the maximum dose in Gy from dvh + @return Return the maximum dose in Gy (i+0.5)*deltaD, i-the maximal dose-bin with volume>0 + Return -1 if not initialized + */ + DoseStatisticType getMaximum() const; + + /*! @brief Get the minimum dose in Gy from dvh + @return Return the minimum dose (i+0.5)*deltaD, i-the minimal dose-bin with volume>0 + Return -1 if not initialized + */ + DoseStatisticType getMinimum() const; + + DoseStatisticType getMean() const; + DoseStatisticType getMedian() const; + DoseStatisticType getModal() const; + DoseStatisticType getStdDeviation() const; + DoseStatisticType getVariance() const; + + /*! @brief Calculate the cumulative data of dvh + @param relativeVolume default false-> Value is the voxel number of the dose bin; + if true-> value is the relative volume % between 0 and 1, + (the voxel number of this dose bin)/(number of voxels) + @return Return cumulative dvh value i-voxel number in dose-bin i + */ + DataDifferentialType calcCumulativeDVH(bool relativeVolume=false); + + /*! @brief Get Vx the volume irradiated to >= x + @return Return absolute Volume in absolute cm3 + Return -1 if not initialized + */ + VolumeType getVx(DoseTypeGy xDoseAbsolute); + /*! @brief Get Dx the minimal dose delivered to x + @return Return absolute dose value in Gy + Return -1 if not initialized + */ + DoseTypeGy getDx(VolumeType xVolumeAbsolute); + + /*! @brief Calculate the absolute volume in cm3 + @param relativePercent 0~100, the percent of the whole volume + */ + VolumeType getAbsoluteVolume(int relativePercent); + + }; + } + } + + +#endif diff --git a/code/core/rttbDVHCalculator.cpp b/code/core/rttbDVHCalculator.cpp new file mode 100644 index 0000000..17e60ec --- /dev/null +++ b/code/core/rttbDVHCalculator.cpp @@ -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$ (last changed revision) +// @date $Date$ (last change date) +// @author $Author$ (last changed by) +*/ + +#include +#include + +#include "rttbDVHCalculator.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbMaskedDoseIteratorInterface.h" + + +namespace rttb{ + namespace core{ + + DVHCalculator::DVHCalculator(DoseIteratorPointer aDoseIterator,const IDType aStructureID,const IDType aDoseID, + DoseTypeGy aDeltaD, const int aNumberOfBins){ + if(!aDoseIterator) + { + throw NullPointerException("aDoseIterator must not be NULL! "); + } + _doseIteratorPtr=aDoseIterator; + _structureID=aStructureID; + _doseID=aDoseID; + + if(aNumberOfBins<=0 || aDeltaD<0 ) + { + throw InvalidParameterException("aNumberOfBins/aDeltaD must be >0! "); + } + + _numberOfBins=aNumberOfBins; + _deltaD=aDeltaD; + + if(_deltaD==0) + { + aDoseIterator->reset(); + DoseTypeGy max=0; + DoseTypeGy currentVal = 0; + while(aDoseIterator->isPositionValid()) + { + currentVal = aDoseIterator->getCurrentDoseValue(); + if(currentVal>max) + { + max=currentVal; + } + aDoseIterator->next(); + } + _deltaD=(max*1.5/_numberOfBins); + + if(_deltaD==0) + { + _deltaD=0.1; + } + } + + } + + DVHCalculator::~DVHCalculator(){} + + DVHCalculator::DVHPointer DVHCalculator::generateDVH(){ + + std::deque dataDifferential(_numberOfBins,0); + + // calculate DVH + _doseIteratorPtr->reset(); + DoseTypeGy currentVal = 0; + while(_doseIteratorPtr->isPositionValid()) + { + FractionType voxelProportion=_doseIteratorPtr->getCurrentRelevantVolumeFraction(); + currentVal = _doseIteratorPtr->getCurrentDoseValue(); + + int dose_bin = static_cast(currentVal/_deltaD); + + if(dose_bin < _numberOfBins) + { + dataDifferential[dose_bin]+=voxelProportion; + } + else{ + throw InvalidParameterException("_numberOfBins is too small: dose bin out of bounds! "); + } + _doseIteratorPtr->next(); + } + if (boost::dynamic_pointer_cast(_doseIteratorPtr)) + { + _dvh=boost::make_shared(dataDifferential,_deltaD,_doseIteratorPtr->getCurrentVoxelVolume(),_structureID,_doseID, _doseIteratorPtr->getVoxelizationID()); + } + else{ + _dvh=boost::make_shared(dataDifferential,_deltaD,_doseIteratorPtr->getCurrentVoxelVolume(),_structureID,_doseID); + } + + return _dvh; + } + + }//end namespace core +}//end namespace rttb + diff --git a/code/core/rttbDVHCalculator.h b/code/core/rttbDVHCalculator.h new file mode 100644 index 0000000..55d774a --- /dev/null +++ b/code/core/rttbDVHCalculator.h @@ -0,0 +1,74 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_CALCULATOR_H +#define __DVH_CALCULATOR_H + +#include +#include +#include + +#include "rttbBaseType.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbMaskedDoseIteratorInterface.h" +#include "rttbDVH.h" +#include "rttbDVHGeneratorInterface.h" + + +namespace rttb{ + namespace core{ + + /*! @class DVHCalculator + @brief Calculates a DVH for a given DoseIterator. + */ + class DVHCalculator: public DVHGeneratorInterface + { + public: + typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; + typedef core::MaskedDoseIteratorInterface::MaskedDoseIteratorPointer MaskedDoseIteratorPointer; + + DoseIteratorPointer _doseIteratorPtr; + IDType _structureID; + IDType _doseID; + DoseTypeGy _deltaD; + int _numberOfBins; + + /*! @brief Constructor. + @param aDeltaD the absolute dose value in Gy for dose_bin [i,i+1). Optional, if aDeltaD==0, + it will be calculated using aDeltaD=max(aDoseIterator)*1.5/aNumberOfBins + @exception InvalidParameterException throw if _numberOfBins<=0 or _deltaD<0 + */ + DVHCalculator(DoseIteratorPointer aDoseIterator, const IDType aStructureID,const IDType aDoseID, const DoseTypeGy aDeltaD=0, const int aNumberOfBins=201); + + ~DVHCalculator(); + + /*! @brief Generate DVH + @return Return new shared pointer of DVH. + @exception InvalidParameterException throw if _numberOfBins invalid: + _numberOfBins must be > max(aDoseIterator)/aDeltaD! + */ + DVHPointer generateDVH(); + + }; + } + + } + +#endif diff --git a/code/core/rttbDVHGeneratorInterface.h b/code/core/rttbDVHGeneratorInterface.h new file mode 100644 index 0000000..05e3aaf --- /dev/null +++ b/code/core/rttbDVHGeneratorInterface.h @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_GENERATOR_INTERFACE_H +#define __DVH_GENERATOR_INTERFACE_H + +#include + +#include "rttbDVH.h" + +namespace rttb{ + namespace core + { + /*! @class DVHGeneratorInterface + @brief Interface for all DVH generating classes + */ + class DVHGeneratorInterface + { + public: + typedef core::DVH::DVHPointer DVHPointer; + protected: + DVHPointer _dvh; + public: + /*! @brief Generate DVH + @return Return new shared pointer of DVH. + */ + virtual DVHPointer generateDVH() = 0; + }; + } + } + +#endif diff --git a/code/core/rttbDVHSet.cpp b/code/core/rttbDVHSet.cpp new file mode 100644 index 0000000..67ea484 --- /dev/null +++ b/code/core/rttbDVHSet.cpp @@ -0,0 +1,175 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDVHSet.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace core{ + + DVHSet::DVHSet(IDType aStructureSetID, IDType aDoseID){ + this->_structureSetID=aStructureSetID; + this->_doseID=aDoseID; + } + + DVHSet::DVHSet(DVHSetType aDVHTVSet, DVHSetType aDVHHTSet, IDType aStructureSetID, IDType aDoseID){ + _dvhHTSet=aDVHHTSet; + _dvhTVSet=aDVHTVSet; + this->_structureSetID=aStructureSetID; + this->_doseID=aDoseID; + } + + DVHSet::DVHSet(DVHSetType aDVHTVSet, DVHSetType aDVHHTSet, DVHSetType aDVHWVSet, IDType aStructureSetID, IDType aDoseID){ + _dvhHTSet=aDVHHTSet; + _dvhTVSet=aDVHTVSet; + _dvhWVSet=aDVHWVSet; + this->_structureSetID=aStructureSetID; + this->_doseID=aDoseID; + } + + std::size_t DVHSet::size() const{ + return _dvhHTSet.size()+_dvhTVSet.size()+_dvhWVSet.size(); + } + + void DVHSet::setStrSetID(IDType aStrSetID){_structureSetID=aStrSetID;} + void DVHSet::setDoseID(IDType aDoseID){_doseID=aDoseID;} + + IDType DVHSet::getStrSetID() const{return _structureSetID;} + IDType DVHSet::getDoseID() const{return _doseID;} + + DVH* DVHSet::getDVH(IDType structureID){ + DVHSetType::iterator itTV = _dvhTVSet.begin(); + for(;itTV!=_dvhTVSet.end();itTV++){ + if((*(itTV)).getStructureID()==structureID) + { + return &(*itTV); + } + } + + DVHSetType::iterator itHT = _dvhHTSet.begin(); + for(;itHT!=_dvhHTSet.end();itHT++){ + if((*(itHT)).getStructureID()==structureID) + { + return &(*itHT); + } + } + + DVHSetType::iterator itWV = _dvhWVSet.begin(); + for(;itWV!=_dvhWVSet.end();itWV++){ + if((*(itWV)).getStructureID()==structureID) + { + return &(*itWV); + } + } + std::cout << "No DVH with the structure id: "<getHTVolume(aDoseAbsolute)+this->getTVVolume(aDoseAbsolute); + return volume; + } + + VolumeType DVHSet::getHTVolume(DoseTypeGy aDoseAbsolute) const{ + DVHSetType::const_iterator itHT = _dvhHTSet.begin(); + VolumeType volume=0; + VolumeType testVol=0; + while(itHT!=_dvhHTSet.end()){ + + DVH dvh=*(itHT); + testVol = dvh.getVx(aDoseAbsolute); + if (testVol>=0) + { + volume+=testVol; + } + itHT++; + } + return volume; + + } + + VolumeType DVHSet::getTVVolume(DoseTypeGy aDoseAbsolute) const{ + DVHSetType::const_iterator itTV = _dvhTVSet.begin(); + VolumeType volume=0; + VolumeType testVol=0; + while(itTV!=_dvhTVSet.end()){ + DVH dvh=*(itTV); + testVol = dvh.getVx(aDoseAbsolute); + if (testVol>=0) + { + volume+=testVol; + } + itTV++; + } + return volume; + } + + bool operator==(const DVHSet &aDVHSet, const DVHSet &otherDVHSet){ + if (aDVHSet.getStrSetID() != otherDVHSet.getStrSetID()) + { + return false; + } + if (aDVHSet.getDoseID() != otherDVHSet.getDoseID()) + { + return false; + } + if (aDVHSet.size() != otherDVHSet.size()) + { + return false; + } + return true; + } + + std::ostream& operator<<(std::ostream& s, const DVHSet &aDVHSet){ + s << "[ " << aDVHSet.getStrSetID() << ", " << aDVHSet.getDoseID() << " ]"; + return s; + } + + std::ostream& operator<<(std::ostream& s, const DVHSet::DVHSetType &aDVHSet){ + s << "[ "; + for( int i = 0; i < aDVHSet.size();i++){ + s << aDVHSet.at(i); + } + s << " ]"; + return s; + } + + } + } + diff --git a/code/core/rttbDVHSet.h b/code/core/rttbDVHSet.h new file mode 100644 index 0000000..95a6d58 --- /dev/null +++ b/code/core/rttbDVHSet.h @@ -0,0 +1,125 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_SET_H +#define __DVH_SET_H + +#include +#include +#include + +#include "rttbBaseType.h" +#include "rttbDVH.h" + +namespace rttb{ + namespace core{ + + /*! @class DVHSet + @brief This is a class representing a RT DVHSet including Target Volume and Organ at Risk + A DVHSet consists of three subsets: one for the target volume (_dvhTVSet), one for healthy tissue (_dvhHTSet), + and one for the whole volume (_dvhWVSet). + */ + class DVHSet + { + public: + typedef std::vector DVHSetType; + + private: + IDType _structureSetID; + IDType _doseID; + DVHSetType _dvhTVSet; + DVHSetType _dvhHTSet; + DVHSetType _dvhWVSet; + + public: + DVHSet(IDType aStructureSetID="", IDType aDoseID=""); + DVHSet(DVHSetType aDVHTVSet, DVHSetType aDVHHTSet, IDType aStructureSetID="", IDType aDoseID=""); + DVHSet(DVHSetType aDVHTVSet, DVHSetType aDVHHTSet, DVHSetType aDVHWVSet, IDType aStructureSetID="", + IDType aDoseID=""); + + /*! @brief Get the size of the DVHSet, that is the sum of the numbers of DVHs in all sub-sets. + */ + std::size_t size() const; + + void setStrSetID(IDType aStrSetID); + void setDoseID(IDType aDoseID); + + IDType getStrSetID() const; + IDType getDoseID() const; + + /*! @brief Get the DVH according to the structure ID + @return Return NULL if not found + */ + DVH* getDVH(IDType aStructureID); + + /*! @brief Insert a DVH object. + @brief param aDVHType "TV" for target volume or "HT" for healthy tissue or "WV" for whole volume + @exception InvalidParameterException Thrown if no valid DVHRole was given. + */ + void insert(DVH& aDvh, DVHRole aDVHRole); + + /*! @brief Get DVH subset for target volume + */ + const DVHSetType& getDVHTVSet() const{return _dvhTVSet;}; + + /*! @brief Get DVH subset for healthy tissue + */ + const DVHSetType& getDVHHTSet() const{return _dvhHTSet;}; + + /*! @brief Get DVH subset for whole volume + */ + const DVHSetType& getDVHWVSet() const{return _dvhWVSet;}; + + + /*! @brief Get the whole volume irradiated to >= aDoseAbsolute + */ + VolumeType getWholeVolume(DoseTypeGy aDoseAbsolute) const; + + /*! @brief Get the healthy tissue volume irradiated to >= aDoseAbsolute + @return Return -1 if DVH of _dvhHTSet init() failed + */ + VolumeType getHTVolume(DoseTypeGy aDoseAbsolute) const; + + /*! @brief Get the target volume irradiated to >= aDoseAbsolute + @return Return -1 if DVH of _dvhTVSet init() failed + */ + VolumeType getTVVolume(DoseTypeGy aDoseAbsolute) const; + + /*! DVHSets are considered equal if they have the same structureSet, dose and voxelization ID + and the number of DVHs are equal. + */ + bool friend operator==(const DVHSet &aDVHSet, const DVHSet &otherDVHSet); + + friend std::ostream& operator<<(std::ostream& s, const DVHSet &aDVHSet); + + friend std::ostream& operator<<(std::ostream& s, const DVHSetType &aDVHSet); + }; + + bool operator==(const DVHSet &aDVHSet, const DVHSet &otherDVHSet); + + std::ostream& operator<<(std::ostream& s, const DVHSet &aDVHSet); + + std::ostream& operator<<(std::ostream& s, const DVHSet::DVHSetType &aDVHSet); + + } + } + +#endif diff --git a/code/core/rttbDoseAccessorGeneratorBase.h b/code/core/rttbDoseAccessorGeneratorBase.h new file mode 100644 index 0000000..1fe3251 --- /dev/null +++ b/code/core/rttbDoseAccessorGeneratorBase.h @@ -0,0 +1,54 @@ +// ----------------------------------------------------------------------- +// 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_GENERATOR_BASE_H +#define __DOSE_ACCESSOR_GENERATOR_BASE_H + +#include + +#include "rttbDoseAccessorGeneratorInterface.h" + +namespace rttb{ + namespace core + { + /*! @class DoseAccessorGeneratorBase + @brief Abstract class for all DoseAccessor generating classes + */ + class DoseAccessorGeneratorBase: public DoseAccessorGeneratorInterface + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + + private: + + + protected: + /*! @brief Dose accessor which should be generated */ + DoseAccessorPointer _doseAccessor; + + + public: + + }; + } + } + +#endif diff --git a/code/core/rttbDoseAccessorGeneratorInterface.h b/code/core/rttbDoseAccessorGeneratorInterface.h new file mode 100644 index 0000000..cbd32b4 --- /dev/null +++ b/code/core/rttbDoseAccessorGeneratorInterface.h @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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_GENERATOR_INTERFACE_H +#define __DOSE_ACCESSOR_GENERATOR_INTERFACE_H + +#include + +#include "rttbDoseAccessorInterface.h" + +namespace rttb{ + namespace core + { + /*! @class DoseAccessorGeneratorInterface + @brief Interface for all DoseAccessor generating classes + */ + class DoseAccessorGeneratorInterface + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + + private: + DoseAccessorGeneratorInterface(const DoseAccessorGeneratorInterface&); //not implemented on purpose -> non-copyable + DoseAccessorGeneratorInterface& operator=(const DoseAccessorGeneratorInterface&);//not implemented on purpose -> non-copyable + + + protected: + DoseAccessorGeneratorInterface() {}; + virtual ~DoseAccessorGeneratorInterface() {}; + + public: + + + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + */ + virtual DoseAccessorPointer generateDoseAccessor() = 0; + }; + } + } + +#endif diff --git a/code/core/rttbDoseAccessorInterface.h b/code/core/rttbDoseAccessorInterface.h new file mode 100644 index 0000000..e5f8e24 --- /dev/null +++ b/code/core/rttbDoseAccessorInterface.h @@ -0,0 +1,81 @@ +// ----------------------------------------------------------------------- +// 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_INTERFACE_NEW_H +#define __DOSE_ACCESSOR_INTERFACE_NEW_H + +#include + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbIndexConversionInterface.h" + +namespace rttb +{ + namespace core + { + + /*! @class IndexConversionInterface + @brief This class represents the conversion of 3D grid indices to 1D grid IDs. + */ + class DoseAccessorInterface: public IndexConversionInterface + { + public: + typedef boost::shared_ptr DoseAccessorPointer; + private: + DoseAccessorInterface(const DoseAccessorInterface&); //not implemented on purpose -> non-copyable + DoseAccessorInterface& operator=(const + DoseAccessorInterface&);//not implemented on purpose -> non-copyable + + public: + DoseAccessorInterface() {}; + virtual ~DoseAccessorInterface() {}; + + inline const core::GeometricInfo& getGeometricInfo() const + { + return _geoInfo; + }; + + inline GridSizeType getGridSize() const + { + return _geoInfo.getNumberOfVoxels(); + }; + + virtual DoseTypeGy getDoseAt(const VoxelGridID aID) const = 0; + + virtual DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const = 0; + + /*! @brief is true if dose is on a homogeneous grid + @remarks Inhomogeneous grids are not supported at the moment, but if they will be supported in the future + the interface does not need to change. + */ + virtual bool isGridHomogeneous() const + { + return true; + } + + virtual const IDType getDoseUID() const = 0; + protected: + core::GeometricInfo _geoInfo; + }; + } +} + +#endif diff --git a/code/core/rttbDoseIteratorInterface.cpp b/code/core/rttbDoseIteratorInterface.cpp new file mode 100644 index 0000000..d3da9b1 --- /dev/null +++ b/code/core/rttbDoseIteratorInterface.cpp @@ -0,0 +1,36 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDoseIteratorInterface.h" +#include "rttbNullPointerException.h" + +namespace rttb{ + namespace core{ + + DoseIteratorInterface::DoseIteratorInterface(DoseAccessorPointer aDoseAccessor){ + if(! aDoseAccessor){ + throw NullPointerException(" dose accessor pointer must not be NULL!"); + } + _spDoseAccessor = aDoseAccessor; + } + }//end: namespace core + }//end: namespace rttb + diff --git a/code/core/rttbDoseIteratorInterface.h b/code/core/rttbDoseIteratorInterface.h new file mode 100644 index 0000000..2e8ec25 --- /dev/null +++ b/code/core/rttbDoseIteratorInterface.h @@ -0,0 +1,104 @@ +// ----------------------------------------------------------------------- +// 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_ITERATOR_INTERFACE_NEW_H +#define __DOSE_ITERATOR_INTERFACE_NEW_H + + +#include + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbDoseAccessorInterface.h" + +namespace rttb{ + namespace core + { + + /*! @class DoseIteratorInterface + @brief This class represents the dose iterator interface. + */ + class DoseIteratorInterface + { + public: + typedef boost::shared_ptr DoseAccessorPointer; + typedef boost::shared_ptr DoseIteratorPointer; + + private: + DoseIteratorInterface(const DoseIteratorInterface&); //not implemented on purpose -> non-copyable + DoseIteratorInterface& operator=(const DoseIteratorInterface&);//not implemented on purpose -> non-copyable + DoseIteratorInterface(){}; + + protected: + /*! @brief DoseAccessor to get access to actual dose data */ + DoseAccessorPointer _spDoseAccessor; + + public: + /*! @brief Constructor with a DoseIterator this should be the default for all implememntations. + */ + DoseIteratorInterface(DoseAccessorPointer aDoseAccessor); + + virtual ~DoseIteratorInterface() {}; + + /*! @brief Set the itterator to the start of the dose. + */ + virtual bool reset()=0; + + /*! @brief Move to next position. If this position is valid is not necessarily tested. + */ + virtual void next() = 0; + + virtual bool isPositionValid() const = 0; + + /*! @brief Return volume of one voxel (in cm3)*/ //previously getDeltaV() + virtual DoseVoxelVolumeType getCurrentVoxelVolume() const = 0; + + + virtual DoseTypeGy getCurrentDoseValue() const = 0; + + inline const core::GeometricInfo& getGeometricInfo() const + { + return _spDoseAccessor->getGeometricInfo(); + }; + + /*! @return If this is a masked dose iterator, return the voxel proportion inside a given structure, + value 0~1; Otherwise, 1 + */ + virtual FractionType getCurrentRelevantVolumeFraction() const=0; + + inline const DoseAccessorPointer getDoseAccessor() const + { + return _spDoseAccessor; + }; + + virtual VoxelGridID getCurrentVoxelGridID() const=0; + + virtual IDType getVoxelizationID() const { return "";}; + + IDType getDoseUID() const + { + return _spDoseAccessor->getDoseUID(); + }; + + }; //end class + }//end: namespace core + }//end: namespace rttb + +#endif diff --git a/code/core/rttbException.cpp b/code/core/rttbException.cpp new file mode 100644 index 0000000..0f0163d --- /dev/null +++ b/code/core/rttbException.cpp @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 "rttbException.h" + +namespace rttb{ + namespace core{ + + const char* Exception::what() const throw() { + return rttb_what.c_str(); + } + + const char* Exception::GetNameOfClass() const{ + return "Exception"; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbException.h b/code/core/rttbException.h new file mode 100644 index 0000000..4af8777 --- /dev/null +++ b/code/core/rttbException.h @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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 __EXCEPTION_H +#define __EXCEPTION_H + +#include +#include +#include + + +namespace rttb{ + namespace core{ + + /*! @class Exception + @brief Exception interface used by all RTToolbox exceptions. + */ + + class Exception: public std::exception + { + protected: + std::string rttb_what; + + public: + explicit Exception(const std::string& aWhat) + :rttb_what(aWhat) + {} + virtual ~Exception() throw() {} + + /*! @brief Get the exception description + */ + const char * what() const throw(); + + /*! @brief Get the name of the exception class that was thrown + */ + const char* GetNameOfClass() const; + }; + + } +} + +#endif diff --git a/code/core/rttbExceptionMacros.h b/code/core/rttbExceptionMacros.h new file mode 100644 index 0000000..70a0be8 --- /dev/null +++ b/code/core/rttbExceptionMacros.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 "rttbException.h" + +/*!@def rttbDefaultExceptionMacro +* This macro is used to throw a basic ExceptionObject within an object method. +* It will generate the exception message and throw the exception.\n +* Use mapGenericDefaultExceptionMacro() for other objects that do not compli with the api or for +* static functions.\n +* Use rttbExceptionMacro() if you want to specifiy a arbitrary exception class that should be thrown. +* Example usage looks like: +* rttbDefaultExceptionMacro(<< "this is an exception" << this->SomeVariable); +* @ingroup Exception +* @todo for future extension of the exception classes: Use if file and line are supported in Exception +* ::rttb::core::Exception e_(__FILE__, __LINE__, message.str().c_str()); +*/ +#define rttbDefaultExceptionMacro(x) \ + { \ + std::ostringstream message; \ + message << "Exception: " \ + << "(" << this << "): " x; \ + ::rttb::core::Exception e_(message.str().c_str()); \ + throw e_; /* Explicit naming to work around Intel compiler bug. */ \ + } + +/*!@def mapExceptionMacro +* This macro is used to throw the passed exception class within an object method. +* The macro presumes that the object owns a method this->GetNameOfClass().\n +* The macro will set file name, line number and function signiture to the exception +* and log the exception as error in the logbook before throwing it.\n +* Use mapGenericExceptionMacro() for other objects that do not compli with the api or for +* static functions.\n +* @sa mapGenericExceptionMacro +* +* Example usage looks like: +* rttbExceptionMacro(AnExceptionClass, << "this is an exception" << this->SomeVariable); +* @ingroup Exception +* @todo for future extension of the exception classes: Use if file and line are supported in Exception +* EType e_(__FILE__, __LINE__, message.str().c_str()); +*/ +#define rttbExceptionMacro( EType, x) \ + { \ + std::ostringstream message; \ + message << "Exception: "\ + << "(" << this << "): " x; \ + EType e_(message.str().c_str()); \ + throw e_; /* Explicit naming to work around Intel compiler bug. */ \ + } diff --git a/code/core/rttbGenericDoseIterator.cpp b/code/core/rttbGenericDoseIterator.cpp new file mode 100644 index 0000000..b45116c --- /dev/null +++ b/code/core/rttbGenericDoseIterator.cpp @@ -0,0 +1,94 @@ +// ----------------------------------------------------------------------- +// 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 "rttbGenericDoseIterator.h" +#include "rttbInvalidParameterException.h" + +namespace rttb +{ + namespace core + { + + GenericDoseIterator::GenericDoseIterator(DoseAccessorPointer aDoseAccessor) : DoseIteratorInterface( + aDoseAccessor) + { + _currentDoseVoxelGridID = 0; + _currentVoxelVolume = 0; + } + + bool GenericDoseIterator::reset() + { + _currentDoseVoxelGridID = 0; + + if (_spDoseAccessor->isGridHomogeneous()) + { + SpacingVectorType3D voxelSizeVec = (_spDoseAccessor->getGeometricInfo()).getSpacing(); + _currentVoxelVolume = voxelSizeVec(0) * voxelSizeVec(1) * voxelSizeVec(2) / 1000; + } + else + { + _currentVoxelVolume = 0; + throw InvalidParameterException("Inhomogeneous grids are currently not supported!"); + } + + return true; + } + + void GenericDoseIterator::next() + { + if (_spDoseAccessor->getGeometricInfo().getNumberOfVoxels() > _currentDoseVoxelGridID) + { + ++_currentDoseVoxelGridID; + } + } + + bool GenericDoseIterator::isPositionValid() const + { + return _spDoseAccessor->getGeometricInfo().validID(_currentDoseVoxelGridID); + } + + DoseVoxelVolumeType GenericDoseIterator::getCurrentVoxelVolume() const + { + if (_spDoseAccessor->isGridHomogeneous()) + { + return _currentVoxelVolume; + } + else + { + throw InvalidParameterException("Inhomogeneous grids are currently not supported!"); + return _currentVoxelVolume; + } + } + + DoseTypeGy GenericDoseIterator::getCurrentDoseValue() const + { + if (isPositionValid()) + { + return _spDoseAccessor->getDoseAt(_currentDoseVoxelGridID); + } + else + { + return 0; + } + } + + }//end: namespace core +}//end: namespace rttb \ No newline at end of file diff --git a/code/core/rttbGenericDoseIterator.h b/code/core/rttbGenericDoseIterator.h new file mode 100644 index 0000000..9ee0bff --- /dev/null +++ b/code/core/rttbGenericDoseIterator.h @@ -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$ (last changed revision) +// @date $Date$ (last change date) +// @author $Author$ (last changed by) +*/ +#ifndef __GENERIC_DOSE_ITERATOR_INTERFACE_NEW_H +#define __GENERIC_DOSE_ITERATOR_INTERFACE_NEW_H + + +#include +#include +#include + +#include "rttbBaseType.h" +#include "rttbDoseIteratorInterface.h" + + +namespace rttb{ + namespace core{ + + /*! @class GenericDoseIterator + @brief Standard implementation of the dose iterator interface. + */ + class GenericDoseIterator: public DoseIteratorInterface + { + public: + typedef DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; + + private: + + VoxelGridID _currentDoseVoxelGridID; + DoseVoxelVolumeType _currentVoxelVolume; + + GenericDoseIterator(const GenericDoseIterator&); //not implemented on purpose -> non-copyable + GenericDoseIterator& operator=(const GenericDoseIterator&);//not implemented on purpose -> non-copyable + + public: + + /*! @brief Constructor + @param aDoseAccessor contains the corresponding dose data + */ + GenericDoseIterator(DoseAccessorPointer aDoseAccessor); + + /*! @brief Set the itterator to the start of the Dose. + @exception InvalidParameterException if a inhomogeneous grid is defined in the dose accessors, because + these grids are currently not supported. + */ + bool reset(); + + /*! @brief Test if next voxel position is still on the data grid, if so move to next position. + */ + void next(); + + /*! @brief Determine if the current voxel position is valid. + */ + bool isPositionValid() const; + + /*! @brief Return volume of one voxel (in cm3) + @exception InvalidParameterException if a inhomogeneous grid is defined in the dose accessors, because + these grids are currently not supported. + */ + DoseVoxelVolumeType getCurrentVoxelVolume() const; + + DoseTypeGy getCurrentDoseValue() const; + + /*! @brief For DoseIterators this function returns 1, always, because no mask is applied. + */ + inline FractionType getCurrentRelevantVolumeFraction() const + { + return 1; + }; + + inline VoxelGridID getCurrentVoxelGridID() const + { + return _currentDoseVoxelGridID; + }; + + }; + } + } + +#endif diff --git a/code/core/rttbGenericMaskedDoseIterator.cpp b/code/core/rttbGenericMaskedDoseIterator.cpp new file mode 100644 index 0000000..0390307 --- /dev/null +++ b/code/core/rttbGenericMaskedDoseIterator.cpp @@ -0,0 +1,101 @@ +// ----------------------------------------------------------------------- +// 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 "rttbGenericMaskedDoseIterator.h" +#include "rttbInvalidParameterException.h" + +namespace rttb +{ + namespace core + { + + bool GenericMaskedDoseIterator::reset() + { + _maskVoxelVec = _spMask->getRelevantVoxelVector(); + _currentMaskPos = _maskVoxelVec->begin(); + + core::GeometricInfo geoInfo = _spDoseAccessor->getGeometricInfo(); + _currentVoxelVolume = geoInfo.getSpacing()(0) * geoInfo.getSpacing()(1) * geoInfo.getSpacing()( + 2) / 1000; + + return true; + } + + void GenericMaskedDoseIterator::next() + { + ++_currentMaskPos; + } + + DoseVoxelVolumeType GenericMaskedDoseIterator::getCurrentVoxelVolume() const + { + if (_spDoseAccessor->isGridHomogeneous()) + { + return _currentVoxelVolume; + } + else + { + throw InvalidParameterException("Inhomogeneous grids are currently not supported! "); + return _currentVoxelVolume; + } + } + + FractionType GenericMaskedDoseIterator::getCurrentRelevantVolumeFraction() const + { + if (!(_currentMaskPos == _maskVoxelVec->end())) + { + assert(_spMask->getGeometricInfo().validID(_currentMaskPos->getVoxelGridID())); + return _currentMaskPos->getProportionInStr(); + } + + return 0; + } + + bool GenericMaskedDoseIterator::isPositionValid() const + { + if (_currentMaskPos == _maskVoxelVec->end()) + { + return false; + } + + return _spDoseAccessor->getGeometricInfo().validID(_currentMaskPos->getVoxelGridID()) && + _spMask->getGeometricInfo().validID(_currentMaskPos->getVoxelGridID()); + } + + VoxelGridID GenericMaskedDoseIterator::getCurrentVoxelGridID() const + { + return _currentMaskPos->getVoxelGridID(); + } + + + DoseTypeGy GenericMaskedDoseIterator::getCurrentMaskedDoseValue() const + { + assert(isPositionValid()); + return getCurrentDoseValue() * getCurrentRelevantVolumeFraction(); + } + + DoseTypeGy GenericMaskedDoseIterator::getCurrentDoseValue() const + { + assert(_spDoseAccessor->getGeometricInfo().validID(_currentMaskPos->getVoxelGridID())); + return _spDoseAccessor->getDoseAt(_currentMaskPos->getVoxelGridID()); + } + + }//end namespace core +}//end namespace rttb \ No newline at end of file diff --git a/code/core/rttbGenericMaskedDoseIterator.h b/code/core/rttbGenericMaskedDoseIterator.h new file mode 100644 index 0000000..065d939 --- /dev/null +++ b/code/core/rttbGenericMaskedDoseIterator.h @@ -0,0 +1,105 @@ +// ----------------------------------------------------------------------- +// 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 __GENERIC_MASKED_DOSE_ITERATOR_NEW_H +#define __GENERIC_MASKED_DOSE_ITERATOR_NEW_H + +#include + +#include "rttbBaseType.h" +#include "rttbMaskedDoseIteratorInterface.h" +#include "rttbMaskVoxel.h" + + +namespace rttb{ + namespace core + { + /*! @class GenericMaskedDoseIterator + @brief This is a templated class representing a generic masked dose iterator for a VoxelizationPolicy. + @see testing/GenericMaskedDoseIteratorTest.cpp for more information. + */ + class GenericMaskedDoseIterator: public MaskedDoseIteratorInterface + { + public: + typedef MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + typedef MaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef MaskedDoseIteratorInterface::MaskAccessorPointer MaskAccessorPointer; + typedef MaskedDoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + + private: + + typedef MaskVoxelList::const_iterator MaskVoxelListIterator; + + /*! The current index position of the vector _maskVoxelVec*/ + MaskVoxelListIterator _currentMaskPos; + + /*! vector of MaskVoxel, as defined in the voxelization*/ + MaskVoxelListPointer _maskVoxelVec; + + /*! the volume in cm^3 of the current dose voxel*/ + DoseVoxelVolumeType _currentVoxelVolume; + + + public: + + GenericMaskedDoseIterator(MaskAccessorPointer aSpMask, DoseAccessorPointer aDoseAccessor) + :MaskedDoseIteratorInterface(aSpMask, aDoseAccessor){}; + + /*! @brief Set the position on the first index. Use also as initialization. + */ + bool reset(); + + /*! move to next mask position. The validity of the position is not checked here. + */ + void next(); + + /*! @brief Volume of one voxel (in cm3) + @exception InvalidParameterException if a inhomogeneous grid is defined in the dose accessors, because + these grids are currently not supported. + @todo inhomogeneous grids are not supported at the moment. + */ + DoseVoxelVolumeType getCurrentVoxelVolume() const; + + FractionType getCurrentRelevantVolumeFraction() const; + + inline MaskVoxelListPointer getMaskVoxelVec() const + { + return _maskVoxelVec; + }; + + /*! Check first if the position inside the maskedVoxelVector is valid. If so, check if the gridID at the + current position in the MaskedVoxelVector is valid in the dose and mask grid. + @todo: Equality of grids needs to be further defined to determine if checking one grid is sufficient. + */ + bool isPositionValid() const; + + /*! @brief get current VoxelGridID (on dose voxel grid)*/ + VoxelGridID getCurrentVoxelGridID() const; + + /*! @return current dose value multiplied by current volume fraction*/ + DoseTypeGy getCurrentMaskedDoseValue() const; + + /*! @return current dose value without masking*/ + DoseTypeGy getCurrentDoseValue() const; + }; + } + } + +#endif diff --git a/code/core/rttbGeometricInfo.cpp b/code/core/rttbGeometricInfo.cpp new file mode 100644 index 0000000..f757a99 --- /dev/null +++ b/code/core/rttbGeometricInfo.cpp @@ -0,0 +1,302 @@ +// ----------------------------------------------------------------------- +// 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::worldCoordinateToIndex(const WorldCoordinate3D& aWorldCoordinate, + VoxelGridIndex3D& aIndex) + const + { + WorldCoordinate3D temp; + temp = aWorldCoordinate - _imagePositionPatient; + + boost::numeric::ublas::vector temp2 = boost::numeric::ublas::prod( + _invertedOrientationMatrix, + temp); + + boost::numeric::ublas::vector resultS = boost::numeric::ublas::element_div(temp2, + _spacing); + + aIndex = VoxelGridIndex3D(GridIndexType(resultS(0) + 0.5), GridIndexType(resultS(1) + 0.5), + GridIndexType(resultS(2) + 0.5)); + + return isInside(aIndex); + } + + bool GeometricInfo::indexToWorldCoordinate(const VoxelGridIndex3D& aIndex, + WorldCoordinate3D& aWorldCoordinate) + const + { + WorldCoordinate3D centerVoxel(aIndex(0) - 0.5, aIndex(1) - 0.5, aIndex(2) - 0.5); + + boost::numeric::ublas::vector resultS = boost::numeric::ublas::element_prod( + centerVoxel, + _spacing); + + boost::numeric::ublas::vector result = boost::numeric::ublas::prod( + _orientationMatrix, + resultS); + + aWorldCoordinate = result + _imagePositionPatient; + + return isInside(aIndex); + } + + bool GeometricInfo::isInside(const VoxelGridIndex3D& aIndex) const + { + return (aIndex(0) >= 0 && aIndex(1) >= 0 && aIndex(2) >= 0 + && aIndex(0) < unsigned int(_numberOfColumns) && aIndex(1) < unsigned int(_numberOfRows) + && aIndex(2) < unsigned int(_numberOfFrames)); + } + + bool GeometricInfo::isInside(const WorldCoordinate3D& aWorldCoordinate) + { + VoxelGridIndex3D currentIndex; + return (worldCoordinateToIndex(aWorldCoordinate, currentIndex)); + } + + const GridSizeType GeometricInfo::getNumberOfVoxels() const + { + return _numberOfRows * _numberOfColumns * _numberOfFrames; + } + + 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() >= (unsigned int)getNumColumns()) + || (gridIndex.y() >= (unsigned int)getNumRows()) + || (gridIndex.z() >= (unsigned int)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 new file mode 100644 index 0000000..3470ea5 --- /dev/null +++ b/code/core/rttbGeometricInfo.h @@ -0,0 +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) +*/ + +#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. + @todo Similarity for core::GeometricInfo needs to be defined. Orientation of the world matrix needs to be the same, + but the extend of the grid may differ. This is for example the case if the slice thickness is different in + Dose and Mask. This may cause a difference in the grid extend. + + */ + 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() : _orientationMatrix(0), _spacing(0), _imagePositionPatient(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. + @todo Should equality be less restrictive and allow similar Orientation (different values) + or equal orientation and different extend in world coordinates/different grid size + */ + friend bool operator==(const GeometricInfo& gInfo, const GeometricInfo& gInfo1); + + /*! @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 voxel grid index to convert world coordinates. + 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 \ No newline at end of file diff --git a/code/core/rttbIndexConversionInterface.h b/code/core/rttbIndexConversionInterface.h new file mode 100644 index 0000000..e0ff840 --- /dev/null +++ b/code/core/rttbIndexConversionInterface.h @@ -0,0 +1,51 @@ +// ----------------------------------------------------------------------- +// 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 __INDEX_CONVERSION_INTERFACE_NEW_H +#define __INDEX_CONVERSION_INTERFACE_NEW_H + +#include "rttbBaseType.h" + + +namespace rttb +{ + + namespace core + { + /*! @class IndexConversionInterface + @brief This class represents the conversion of 3D grid indices to 1D grid IDs. + */ + class IndexConversionInterface + { + private: + IndexConversionInterface(const + IndexConversionInterface&); //not implemented on purpose -> non-copyable + IndexConversionInterface& operator=(const + IndexConversionInterface&);//not implemented on purpose -> non-copyable + + public: + IndexConversionInterface() {}; + virtual ~IndexConversionInterface() {}; + + }; + } +} + +#endif diff --git a/code/core/rttbIndexOutOfBoundsException.cpp b/code/core/rttbIndexOutOfBoundsException.cpp new file mode 100644 index 0000000..c0c6452 --- /dev/null +++ b/code/core/rttbIndexOutOfBoundsException.cpp @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 "rttbIndexOutOfBoundsException.h" + +namespace rttb{ + namespace core{ + + const char* IndexOutOfBoundsException::what() const throw(){ + return rttb_what.c_str(); + } + + const char* IndexOutOfBoundsException::GetNameOfClass() const{ + return "IndexOutOfBoundsException"; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbIndexOutOfBoundsException.h b/code/core/rttbIndexOutOfBoundsException.h new file mode 100644 index 0000000..0e21a46 --- /dev/null +++ b/code/core/rttbIndexOutOfBoundsException.h @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// 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 __INDEX_OUT_OF_BOUNDS_EXCEPTION_H +#define __INDEX_OUT_OF_BOUNDS_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + +namespace rttb{ + + namespace core{ + + /*! @class IndexOutOfBoundsException + @brief This exception will be thrown if any index out of bound. + */ + class IndexOutOfBoundsException: public Exception + { + public: + IndexOutOfBoundsException(const std::string& aWhat):Exception(aWhat){} + + virtual ~IndexOutOfBoundsException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char * what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + + } + } +#endif diff --git a/code/core/rttbInvalidDoseException.cpp b/code/core/rttbInvalidDoseException.cpp new file mode 100644 index 0000000..baa34fe --- /dev/null +++ b/code/core/rttbInvalidDoseException.cpp @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------- +// 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 "rttbInvalidDoseException.h" + +namespace rttb{ + + namespace core{ + + const char* InvalidDoseException::what() const throw(){ + return rttb_what.c_str(); + } + + const char* InvalidDoseException::GetNameOfClass() const{ + return "InvalidDoseException"; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbInvalidDoseException.h b/code/core/rttbInvalidDoseException.h new file mode 100644 index 0000000..dd9c239 --- /dev/null +++ b/code/core/rttbInvalidDoseException.h @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// 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 __INVALID_DOSE_EXCEPTION_H +#define __INVALID_DOSE_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + +namespace rttb{ + namespace core{ + + /*! @class InvalidDoseException + @brief This exception will be thrown if dose is invalid. + */ + class InvalidDoseException: public Exception + { + public: + InvalidDoseException(const std::string& aWhat):Exception(aWhat){} + + virtual ~InvalidDoseException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char * what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + + } + } + +#endif diff --git a/code/core/rttbInvalidParameterException.cpp b/code/core/rttbInvalidParameterException.cpp new file mode 100644 index 0000000..bf563c4 --- /dev/null +++ b/code/core/rttbInvalidParameterException.cpp @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 "rttbInvalidParameterException.h" + +namespace rttb{ + namespace core{ + + const char* InvalidParameterException::what() const throw() { + return rttb_what.c_str(); + } + + const char* InvalidParameterException::GetNameOfClass() const{ + return "InvalidParameterException"; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbInvalidParameterException.h b/code/core/rttbInvalidParameterException.h new file mode 100644 index 0000000..030ac83 --- /dev/null +++ b/code/core/rttbInvalidParameterException.h @@ -0,0 +1,54 @@ +// ----------------------------------------------------------------------- +// 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 __INVALID_PARAMETER_EXCEPTION_H +#define __INVALID_PARAMETER_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + +namespace rttb{ + namespace core{ + + /*! @class InvalidParameterException + @brief This exception will be thrown if any parameter is invalid. + */ + class InvalidParameterException: public Exception + { + public: + InvalidParameterException(const std::string& aWhat):Exception(aWhat){} + + virtual ~InvalidParameterException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char * what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + + } + } +#endif diff --git a/code/core/rttbMappingOutsideOfImageException.cpp b/code/core/rttbMappingOutsideOfImageException.cpp new file mode 100644 index 0000000..bddaaa8 --- /dev/null +++ b/code/core/rttbMappingOutsideOfImageException.cpp @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// 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 "rttbMappingOutsideOfImageException.h" + +namespace rttb +{ + namespace core + { + + const char* MappingOutsideOfImageException::what() const throw() + { + return rttb_what.c_str(); + } + + const char* MappingOutsideOfImageException::GetNameOfClass() const + { + return "MappingOutsideOfImageException"; + } + + }//end namespace core +}//end namespace rttb diff --git a/code/core/rttbMappingOutsideOfImageException.h b/code/core/rttbMappingOutsideOfImageException.h new file mode 100644 index 0000000..c91855b --- /dev/null +++ b/code/core/rttbMappingOutsideOfImageException.h @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// 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 __MAPPING_OUTSIDE_OF_IMAGE_EXCEPTION_H +#define __MAPPING_OUTSIDE_OF_IMAGE_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + + +namespace rttb +{ + namespace core + { + + /*! @class MappingOutsideOfImageException + @brief This exception will be thrown if a transformation leads to an invalid position outside of the image. + */ + class MappingOutsideOfImageException: public Exception + { + public: + MappingOutsideOfImageException(const std::string& aWhat): Exception(aWhat) {} + + virtual ~MappingOutsideOfImageException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char* what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + + } +} + +#endif diff --git a/code/core/rttbMaskAccessorInterface.h b/code/core/rttbMaskAccessorInterface.h new file mode 100644 index 0000000..e5e8d51 --- /dev/null +++ b/code/core/rttbMaskAccessorInterface.h @@ -0,0 +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 __MASK_ACCESSOR_INTERFACE_NEW_H +#define __MASK_ACCESSOR_INTERFACE_NEW_H + +#include + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbMaskVoxel.h" +#include "rttbIndexConversionInterface.h" + +namespace rttb +{ + namespace core + { + /*! @class MaskAccessorInterface + @brief This class triggers the voxelization and gives acess to the masked voxels. + */ + class MaskAccessorInterface: public IndexConversionInterface + { + public: + typedef std::vector MaskVoxelList; + typedef boost::shared_ptr MaskVoxelListPointer; + typedef boost::shared_ptr MaskAccessorPointer; + + private: + MaskAccessorInterface(const MaskAccessorInterface&); //not implemented on purpose -> non-copyable + MaskAccessorInterface& operator=(const + MaskAccessorInterface&);//not implemented on purpose -> non-copyable + + public: + MaskAccessorInterface() {}; + virtual ~MaskAccessorInterface() {}; + + /*! @brief Start generation of mask + @post mask is valid and acessible + */ + virtual void updateMask() = 0; + + virtual const GeometricInfo& getGeometricInfo() const = 0; + + /*! @brief Get vector containing all relevant voxels that are inside the given structure. + */ + virtual MaskVoxelListPointer getRelevantVoxelVector() = 0; + + /*! @brief get vector containing all relevant voxels that have a relevant volume above the given threshold + and are inside the given structure + @pre updateMask should have been called (at least once, to ensure a valid mask). + */ + virtual MaskVoxelListPointer getRelevantVoxelVector(float lowerThreshold) = 0; + + /*! @brief Get masked voxel value corresponding to a given VoxelGridID. + @post after a valid call voxel contains the mask information corresponding to aID. + If aID is not valid, voxel values are undefined. + @return Indicates if a MaskVoxel for the given ID exists and therefore if parameter voxel containes + valid values. + @pre updateMask should have been called (at least once, to ensure a valid mask). + */ + virtual bool getMaskAt(const VoxelGridID aID, MaskVoxel& voxel) const = 0; + + /*! @brief Get masked voxel value corresponding to a given VoxelGridIndex. + @post after a valid call voxel contains the mask information corresponding to gridIndex. + If gridIndex is not valid, voxel values are undefined. + @return Indicates if a MaskVoxel for the given index exists and therefore if parameter voxel containes + valid values. + @pre updateMask should have been called (at least once, to ensure a valid mask). + */ + virtual bool getMaskAt(const VoxelGridIndex3D& gridIndex, MaskVoxel& voxel) const = 0; + + /* @brief Is true if dose is on a homogeneous grid. + @todo Inhomogeneous grids are not supported at the moment, but if they will + be supported in the future the interface does not need to change. + */ + virtual bool isGridHomogeneous() const + { + return true; + } + + virtual IDType getMaskUID() const = 0; + }; + } +} + +#endif diff --git a/code/core/rttbMaskVoxel.cpp b/code/core/rttbMaskVoxel.cpp new file mode 100644 index 0000000..4ab5ab5 --- /dev/null +++ b/code/core/rttbMaskVoxel.cpp @@ -0,0 +1,81 @@ +// ----------------------------------------------------------------------- +// 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 "rttbMaskVoxel.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace core{ + + MaskVoxel::MaskVoxel(const rttb::VoxelGridID &aVoxelGridID){ + if(aVoxelGridID<0) + { + std::cout << aVoxelGridID<1) + { + std::cout << aVolumeFraction<1) + { + std::cout << aVolumeFraction<getGeometricInfo()==_spDoseAccessor->getGeometricInfo())) + { + throw Exception("Mask and Dose need to be defined on the same grid"); + } + } + } + } \ No newline at end of file diff --git a/code/core/rttbMaskedDoseIteratorInterface.h b/code/core/rttbMaskedDoseIteratorInterface.h new file mode 100644 index 0000000..c48c904 --- /dev/null +++ b/code/core/rttbMaskedDoseIteratorInterface.h @@ -0,0 +1,78 @@ +// ----------------------------------------------------------------------- +// 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 __MASKED_DOSE_ITERATOR_INTERFACE_NEW_H +#define __MASKED_DOSE_ITERATOR_INTERFACE_NEW_H + + +#include +#include +#include + +#include + +#include "rttbBaseType.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbMaskAccessorInterface.h" + +namespace rttb{ + namespace core{ + + /*! @class MaskedDoseIteratorInterface + @brief Give access to masked dose data. + */ + class MaskedDoseIteratorInterface: public DoseIteratorInterface + { + public: + typedef boost::shared_ptr MaskAccessorPointer; + typedef DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; + typedef boost::shared_ptr MaskedDoseIteratorPointer; + + private: + MaskedDoseIteratorInterface(const MaskedDoseIteratorInterface&); + MaskedDoseIteratorInterface& operator=(const MaskedDoseIteratorInterface&); + + protected: + /*! @brief Mask that is to be applied to currently loaded dose*/ + MaskAccessorPointer _spMask; + + public: + /* Constructor + @pre core::GeometricInfo represents the same geometric space for both mask and dose, + i.e. both live on the same data grid. Both accessors need to be valid. + */ + MaskedDoseIteratorInterface(MaskAccessorPointer aMaskAccessor, DoseAccessorPointer aDoseAccessor); + + virtual ~MaskedDoseIteratorInterface() {}; + + inline MaskAccessorPointer getMaskAccessor() const + { + return _spMask; + }; + + /* Return doseValue*voxelFraction for the current position + */ + virtual DoseTypeGy getCurrentMaskedDoseValue() const = 0; + }; + } + } +#endif \ No newline at end of file diff --git a/code/core/rttbMutableDoseAccessorInterface.h b/code/core/rttbMutableDoseAccessorInterface.h new file mode 100644 index 0000000..107895b --- /dev/null +++ b/code/core/rttbMutableDoseAccessorInterface.h @@ -0,0 +1,47 @@ +// ----------------------------------------------------------------------- +// 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 __MUTABLE_DOSE_ACCESSOR_INTERFACE_NEW_H +#define __MUTABLE_DOSE_ACCESSOR_INTERFACE_NEW_H + +#include "rttbDoseAccessorInterface.h" +#include "rttbBaseType.h" + +namespace rttb{ + namespace core{ + + /*! @class MutableAccessorInterface + @brief Extends the DoseAccessorInterface to provide writing access to the data. + */ + class MutableDoseAccessorInterface: public DoseAccessorInterface + { + + public: + typedef boost::shared_ptr MutableDoseAccessorPointer; + + virtual void setDoseAt(const VoxelGridID aID, DoseTypeGy value) = 0; + + virtual void setDoseAt(const VoxelGridIndex3D& aIndex, DoseTypeGy value) = 0; + + }; + } +} + +#endif diff --git a/code/core/rttbMutableMaskAccessorInterface.h b/code/core/rttbMutableMaskAccessorInterface.h new file mode 100644 index 0000000..e4a8eb0 --- /dev/null +++ b/code/core/rttbMutableMaskAccessorInterface.h @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// 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 __MUTABLE_MASK_ACCESSOR_INTERFACE_H +#define __MUTABLE_MASK_ACCESSOR_INTERFACE_H + +#include + +#include "rttbMaskAccessorInterface.h" +#include "rttbBaseType.h" +#include "rttbMaskVoxel.h" + +namespace rttb{ + namespace core{ + + /*! @class MutableMaskAccessorInterface + @brief Extends the MaskAccessorInterface to provide writing access to the data. + This interface is created for external manipulation of generated masks. For example to store + the results of arithmetic operations on other masks. + */ + class MutableMaskAccessorInterface: public MaskAccessorInterface + { + public: + typedef boost::shared_ptr MutableMaskAccessorPointer; + typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + + virtual void setRelevantVoxelVector(MaskVoxelListPointer aVoxelListPointer)= 0; + + virtual void setMaskAt(VoxelGridID aID, const MaskVoxel& voxel) = 0; + + virtual void setMaskAt(const VoxelGridIndex3D& gridIndex, const MaskVoxel& voxel) = 0; + + }; + } +} + +#endif diff --git a/code/core/rttbNullPointerException.cpp b/code/core/rttbNullPointerException.cpp new file mode 100644 index 0000000..10d1c8c --- /dev/null +++ b/code/core/rttbNullPointerException.cpp @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 "rttbNullPointerException.h" + +namespace rttb{ + namespace core{ + + const char* NullPointerException::what() const throw() { + return rttb_what.c_str(); + } + + const char* NullPointerException::GetNameOfClass() const{ + return "NullPointerException"; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbNullPointerException.h b/code/core/rttbNullPointerException.h new file mode 100644 index 0000000..bd377e2 --- /dev/null +++ b/code/core/rttbNullPointerException.h @@ -0,0 +1,56 @@ +// ----------------------------------------------------------------------- +// 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 __NULL_POINTER_EXCEPTION_H +#define __NULL_POINTER_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + + +namespace rttb{ + namespace core{ + + /*! @class NullPointerException + @brief This exception will be thrown if any pointer is NULL. + */ + class NullPointerException: public Exception + { + public: + NullPointerException(const std::string& aWhat):Exception(aWhat){} + + virtual ~NullPointerException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char * what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + + } + } + +#endif diff --git a/code/core/rttbPaddingException.cpp b/code/core/rttbPaddingException.cpp new file mode 100644 index 0000000..0d3e883 --- /dev/null +++ b/code/core/rttbPaddingException.cpp @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// 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 "rttbPaddingException.h" + +namespace rttb +{ + namespace core + { + + const char* PaddingException::what() const throw() + { + return rttb_what.c_str(); + } + + const char* PaddingException::GetNameOfClass() const + { + return "PaddingException"; + } + + }//end namespace core +}//end namespace rttb diff --git a/code/core/rttbPaddingException.h b/code/core/rttbPaddingException.h new file mode 100644 index 0000000..708d2c4 --- /dev/null +++ b/code/core/rttbPaddingException.h @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// 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 __PADDING_EXCEPTION_H +#define __PADDING_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + + +namespace rttb +{ + namespace core + { + + /*! @class PaddingException + @brief This exception will be thrown if it can't be guaranteed that a transformation covers only a part of the target space. + */ + class PaddingException: public Exception + { + public: + PaddingException(const std::string& aWhat): Exception(aWhat) {} + + virtual ~PaddingException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char* what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + + } +} + +#endif diff --git a/code/core/rttbPhysicalInfo.cpp b/code/core/rttbPhysicalInfo.cpp new file mode 100644 index 0000000..a024c92 --- /dev/null +++ b/code/core/rttbPhysicalInfo.cpp @@ -0,0 +1,29 @@ +// ----------------------------------------------------------------------- +// 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 "rttbPhysicalInfo.h" + +namespace rttb{ + namespace core{ + void PhysicalInfo::setFullPath(PathType aPath){_fullPath=aPath;} + PathType PhysicalInfo::getFullPath(){return _fullPath;} + } + } diff --git a/code/core/rttbPhysicalInfo.h b/code/core/rttbPhysicalInfo.h new file mode 100644 index 0000000..32a9a66 --- /dev/null +++ b/code/core/rttbPhysicalInfo.h @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// 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 __PHYSICAL_INFO_H +#define __PHYSICAL_INFO_H + + +#include "rttbBaseType.h" + + +namespace rttb{ + + namespace core{ + + /*! @class PhysicalInfo + */ + class PhysicalInfo + { + public: + void setFullPath(PathType aPath); + PathType getFullPath(); + private: + PathType _fullPath; + + }; + + } + } + +#endif diff --git a/code/core/rttbStrVectorStructureSetGenerator.cpp b/code/core/rttbStrVectorStructureSetGenerator.cpp new file mode 100644 index 0000000..44f8c2c --- /dev/null +++ b/code/core/rttbStrVectorStructureSetGenerator.cpp @@ -0,0 +1,42 @@ +// ----------------------------------------------------------------------- +// 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" + + +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); + } + } +}//end namespace rttb diff --git a/code/core/rttbStrVectorStructureSetGenerator.h b/code/core/rttbStrVectorStructureSetGenerator.h new file mode 100644 index 0000000..e647e81 --- /dev/null +++ b/code/core/rttbStrVectorStructureSetGenerator.h @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// 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 __STR_VECTOR_STRUCTURE_SET_GENERATOR_H +#define __STR_VECTOR_STRUCTURE_SET_GENERATOR_H + +#include + +#include +#include + +#include "rttbStructureSetGeneratorInterface.h" + + + +namespace rttb{ + namespace core{ + + /*! @class StrVectorStructureSetGenerator + @brief Generate a structure set with a vector of Structures. + */ + class StrVectorStructureSetGenerator: public core::StructureSetGeneratorInterface + { + public: + typedef core::StructureSet::StructTypePointer StructTypePointer; + + typedef StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + + protected: + IDType _patientUID; + std::vector _strVector; + + StrVectorStructureSetGenerator() {}; + + + public: + + /*! @brief Constructor + @param aStructureVector the vector of structure shared pointer + @param aPatientUID the patient UID. + */ + StrVectorStructureSetGenerator(std::vector& aStructureVector,IDType aPatientUID=""); + + /*! @brief Generate StructureSet + @return Return shared pointer of StructureSet. + */ + StructureSetPointer generateStructureSet(); + }; + } +} + +#endif diff --git a/code/core/rttbStructure.cpp b/code/core/rttbStructure.cpp new file mode 100644 index 0000000..839cd73 --- /dev/null +++ b/code/core/rttbStructure.cpp @@ -0,0 +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 +#include +#include + +#include +#include +#include + +#include "rttbStructure.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace core{ + + /*! Compares two polygons in the same plane. + Helper function for sorting of polygons. + */ + bool comparePolygon(PolygonType A, PolygonType B){ + PolygonType::iterator it; + for(it=A.begin();it!=A.end();it++) + { + if((*it)(2)!=A.at(0)(2)) + { + throw std::range_error("Error: A must in the same _z plane!"); + } + } + PolygonType::iterator it2; + for(it2=B.begin();it2!=B.end();it2++) + { + if((*it2)(2)!=B.at(0)(2)) + { + throw std::range_error("Error: B must in the same _z plane!"); + } + } + if(A.size()==0 || B.size()==0) + { + throw std::range_error("Error: A and B must not be empty!"); + } + return (A.at(0)(2)_structureVector=copy.getStructureVector(); + this->_strUID=copy.getUID(); + this->_label=copy.getLabel(); + } + + Structure::~Structure(){} + + const PolygonSequenceType& Structure::getStructureVector() const{ + return _structureVector; + } + + + + int Structure::getNumberOfEndpoints() const{ + int count=0; + PolygonSequenceType::const_iterator itVV; + for(itVV=_structureVector.begin();itVV!=_structureVector.end();itVV++){ + count+=(int)(*itVV).size(); + } + return count; + } + + IDType Structure::getUID() const{ + + return _strUID; + } + + void Structure::setUID(const IDType aUID){ + _strUID=aUID; + } + + void Structure::setLabel(const StructureLabel aLabel){ + _label=aLabel; + } + + StructureLabel Structure::getLabel() const{ + return _label; + } + + }//end namespace core + }//end namespace rttb diff --git a/code/core/rttbStructure.h b/code/core/rttbStructure.h new file mode 100644 index 0000000..9ac2671 --- /dev/null +++ b/code/core/rttbStructure.h @@ -0,0 +1,105 @@ +// ----------------------------------------------------------------------- +// 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) +*/ + +/* Changes in Architecture: +This class should be universally used independent of the origin of the Structures. All UID references are omitted. +*/ +#ifndef __STRUCTURE_H +#define __STRUCTURE_H + + +#include +#include + +#include + +#include "rttbBaseType.h" + + +namespace rttb{ + + namespace core{ + + /*! @class Structure + @brief This is a class representing a RT Structure + */ + class Structure + { + public: + typedef boost::shared_ptr StructTypePointer; + private: + /*! @brief WorldCoordinate3D in mm + */ + PolygonSequenceType _structureVector; + + + /*! @brief Contour Geometric Type using DICOM-RT definition (3006,0042). + * POINT: indicates that the contour is a single point, defining a specific location of significance. + * OPEN_PLANAR: indicates that the last vertex shall not be connected to the first point, and that all points + * in Contour Data (3006,0050) shall be coplanar. + * OPEN_NONPLANAR: indicates that the last vertex shall not be connected to the first point, and that the points + * in Contour Data(3006,0050) may be non-coplanar. + * CLOSED_PLANAR: indicates that the last point shall be connected to the first point, where the first point is + * not repeated in the Contour Data. All points in Contour Data (3006,0050) shall be coplanar. + */ + std::vector _contourGeometricTypeVector; + + /*! @brief Structure UID*/ + IDType _strUID; + + /*! @brief Structure Label*/ + StructureLabel _label; + + + public: + /*! @brief Structure Standard Constructor + uid will be randomly generated using boost::uuid library at first. To change the uid using setUID(). + */ + Structure(); + + /*! @brief Structure Constructor + uid will be randomly generated using boost::uuid library at first. To change the uid using setUID(). + */ + Structure(PolygonSequenceType strVector); + + Structure(const Structure& copy); + + ~Structure(); + + const PolygonSequenceType& getStructureVector() const; + + /*! @brief Get the number of end points (points that define the polygon) of all contours of the structure. + */ + int getNumberOfEndpoints() const; + + IDType getUID() const; + + void setUID(const IDType aUID); + + void setLabel(const StructureLabel aLabel); + + StructureLabel getLabel() const; + + }; + }//end namespace core + }//end namespace rttb + +#endif diff --git a/code/core/rttbStructureSet.cpp b/code/core/rttbStructureSet.cpp new file mode 100644 index 0000000..fc6f30f --- /dev/null +++ b/code/core/rttbStructureSet.cpp @@ -0,0 +1,78 @@ +// ----------------------------------------------------------------------- +// 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; + if(aStructureNo < 0 || aStructureNo > size){ + std::stringstream sstr; + sstr << "aStructureNo must between 0 and "< + +#include + +#include "rttbBaseType.h" +#include "rttbStructure.h" + +namespace rttb{ + namespace core{ + + /*! @class StructureSet + @brief This is an class representing a structure set, which can be used to generate masks. + */ + class StructureSet + { + public: + typedef Structure::StructTypePointer StructTypePointer; + typedef size_t NumberOfStructuresType; + + protected: + std::vector _structureSetVector; + + IDType _UID; + IDType _patientUID; + + + public: + StructureSet(); + virtual ~StructureSet(){}; + + /*! @brief Constructor + @param aPatientUID the patient UID. + @param aUID the structure set UID. If it is empty, it will be calculated in the constructor + */ + StructureSet(std::vector aStructureVector,IDType aPatientUID="", IDType aUID=""); + + /*! @brief Get the Structure with the index aStructureNo + @return Return Structure pointer. + @exception InvalidParameterException Thrown if structureNo not between 0 and number of structures + of structureSet. + */ + StructTypePointer getStructure(int aStructureNo) const; + + /*! @brief Get the number of structures + @return Return the number of structures. + */ + NumberOfStructuresType getNumberOfStructures() const; + + virtual IDType getUID(); + + virtual IDType getPatientUID(); + + }; + } + } + +#endif diff --git a/code/core/rttbStructureSetGeneratorInterface.h b/code/core/rttbStructureSetGeneratorInterface.h new file mode 100644 index 0000000..bdb8f97 --- /dev/null +++ b/code/core/rttbStructureSetGeneratorInterface.h @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------- +// 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() {}; + virtual ~StructureSetGeneratorInterface() {}; + + public: + + + /*! @brief Generate StructureSet + @return Return shared pointer of StructureSet. + */ + virtual StructureSetPointer generateStructureSet() = 0; + }; + } + } + +#endif diff --git a/code/indices/CMakeLists.txt b/code/indices/CMakeLists.txt new file mode 100644 index 0000000..bdb24cc --- /dev/null +++ b/code/indices/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBIndices DEPENDS RTTBCore PACKAGE_DEPENDS Boost) \ No newline at end of file diff --git a/code/indices/files.cmake b/code/indices/files.cmake new file mode 100644 index 0000000..f08aa12 --- /dev/null +++ b/code/indices/files.cmake @@ -0,0 +1,17 @@ +SET(CPP_FILES + rttbConformalIndex.cpp + rttbConformationNumber.cpp + rttbConformityIndex.cpp + rttbCoverageIndex.cpp + rttbDoseIndex.cpp + rttbHomogeneityIndex.cpp + ) + +SET(H_FILES + rttbConformalIndex.h + rttbConformationNumber.h + rttbConformityIndex.h + rttbCoverageIndex.h + rttbDoseIndex.h + rttbHomogeneityIndex.h +) diff --git a/code/indices/rttbConformalIndex.cpp b/code/indices/rttbConformalIndex.cpp new file mode 100644 index 0000000..d6794d7 --- /dev/null +++ b/code/indices/rttbConformalIndex.cpp @@ -0,0 +1,115 @@ +// ----------------------------------------------------------------------- +// 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 "rttbConformalIndex.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + +namespace rttb{ + + namespace indices{ + + ConformalIndex::ConformalIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference) + { + _dvhSet=dvhSet; + _doseReference=aDoseReference; + initSuccess=false; + } + + bool ConformalIndex::init() + { + if(!_dvhSet){ + throw core::NullPointerException("DVHSet must not be NULL! "); + } + if(this->calcIndex()){ + initSuccess=true; + return true; + } + else + return false; + } + + bool ConformalIndex::calcIndex() + { + VolumeType TV=_dvhSet->getTVVolume(0); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + if(TV!=0 && Vref!=0){ + _value=(_dvhSet->getTVVolume(_doseReference)/TV)* + (_dvhSet->getTVVolume(_doseReference)/Vref); + + std::vector dvhHTSet=this->_dvhSet->getDVHHTSet(); + std::vector::iterator it; + + for(it=dvhHTSet.begin(); it!=dvhHTSet.end();++it) + { + core::DVH dvh=*(it); + VolumeType HT=dvh.getVx(0); + if(HT!=0) + _value*=(1-dvh.getVx(this->_doseReference)/HT); + } + } + else if(TV==0){ + throw core::InvalidParameterException("DVH Set invalid: Target volume should not be 0!"); + } + else{ + rttbExceptionMacro(core::InvalidParameterException, << "Reference dose "<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + return true; + } + + IndexValueType ConformalIndex::getDoseIndexAt(GridIndexType tvIndex){ + std::vector dvhTVSet=this->_dvhSet->getDVHTVSet(); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + if(tvIndex>=dvhTVSet.size()){ + rttbExceptionMacro(core::InvalidParameterException, <<"tvIndex invalid: it should be <"<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + + double value=dvh.getVx(_doseReference)/TV;//the irradiation factor of i-th target volume + value=value*dvh.getVx(_doseReference)/Vref;//conformation number + + std::vector dvhHTSet=this->_dvhSet->getDVHHTSet(); + std::vector::iterator it; + + for(it=dvhHTSet.begin(); it!=dvhHTSet.end();++it) + { + dvh=*(it); + VolumeType HT=dvh.getVx(0); + if(HT!=0){ + value*=(1-dvh.getVx(this->_doseReference)/HT); + } + } + return value; + } + } + + }//end namespace indices +}//end namespace rttb diff --git a/code/indices/rttbConformalIndex.h b/code/indices/rttbConformalIndex.h new file mode 100644 index 0000000..0d0d454 --- /dev/null +++ b/code/indices/rttbConformalIndex.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 __CONFORMAL_INDEX_H +#define __CONFORMAL_INDEX_H + + +#include "rttbDoseIndex.h" +#include "rttbBaseType.h" + +namespace rttb{ + + namespace indices{ + + /*! @class ConformalIndex + @brief This class representing a ConformalIndex Object. Conformal Index (COIN)= Conformation Number(CN)* (1-Vref,0/Vnt,0)*(1-Vref,1/Vnt,1)... i: i-th critiacal organ + Conformation Number (CN)= (TVref/TV) * (TVref/Vref) + @ingroup indices + */ + class ConformalIndex: public DoseIndex + { + protected: + /*! @brief Calculate conformal index + @exception InvalidParameterException Thrown if dvhSet or aDoseReference invalid + */ + bool calcIndex(); + + + public: + /*! @brief Constructor + */ + ConformalIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference); + + /*! @return Return true if calcIndex() finished + @exception NullPointerException thrown if dvhSet is NULL + */ + bool init(); + + /*! @brief Dose index calculation for tvIndex-th treated volume + @param tvIndex index in the DVH in the current set of tv-DVHs + @return Return index value + @exception InvalidParameterException Thrown if tvIndex or aDoseReference invalid + */ + IndexValueType getDoseIndexAt(const GridIndexType tvIndex); + + }; + + } +} + +#endif diff --git a/code/indices/rttbConformationNumber.cpp b/code/indices/rttbConformationNumber.cpp new file mode 100644 index 0000000..21dce8f --- /dev/null +++ b/code/indices/rttbConformationNumber.cpp @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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 "rttbConformationNumber.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + +namespace rttb{ + + namespace indices{ + + ConformationNumber::ConformationNumber(core::DVHSet* dvhSet, DoseTypeGy aDoseReference) + { + _dvhSet=dvhSet; + _doseReference=aDoseReference; + initSuccess=false; + } + + bool ConformationNumber::init() + { + if(!_dvhSet){ + throw core::NullPointerException("DVHSet must not be NULL! "); + } + if( this->calcIndex()){ + initSuccess=true; + return true; + } + return false; + } + + bool ConformationNumber::calcIndex() + { + VolumeType TV=_dvhSet->getTVVolume(0); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + if(TV!=0 && Vref!=0){ + _value=(_dvhSet->getTVVolume(_doseReference)/TV)* + (_dvhSet->getTVVolume(_doseReference)/Vref); + } + else if(TV==0){ + throw core::InvalidParameterException("DVH Set invalid: Target volume should not be 0!"); + } + else{ + rttbExceptionMacro(core::InvalidParameterException, << "Reference dose "<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + + return true; + } + + IndexValueType ConformationNumber::getDoseIndexAt(GridIndexType tvIndex){ + std::vector dvhTVSet=this->_dvhSet->getDVHTVSet(); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + if(tvIndex>=dvhTVSet.size()){ + rttbExceptionMacro(core::InvalidParameterException, <<"tvIndex invalid: it should be <"<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + IndexValueType value=dvh.getVx(_doseReference)/TV;//the irradiation factor of i-th target volume + value=value*dvh.getVx(_doseReference)/Vref; + return value; + } + } + + }//end namespace indices + }//end namespace rttb \ No newline at end of file diff --git a/code/indices/rttbConformationNumber.h b/code/indices/rttbConformationNumber.h new file mode 100644 index 0000000..3c9e92d --- /dev/null +++ b/code/indices/rttbConformationNumber.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 __CONFORMATION_NUMBER_H +#define __CONFORMATION_NUMBER_H + + + +#include "rttbDoseIndex.h" +#include "rttbBaseType.h" + +namespace rttb{ + + namespace indices{ + + /*! @class ConformationNumber + @brief This class representing a ConformationNumber Object. Conformation Number (CN)= (TVref/TV) * (TVref/Vref) + @ingroup indices + */ + class ConformationNumber: public DoseIndex + { + protected: + /*! @brief Calculate conformation number + @exception InvalidParameterException Thrown if dvhSet or aDoseReference invalid + */ + bool calcIndex(); + + + public: + + /*! @brief Constructor + */ + ConformationNumber(core::DVHSet* dvhSet, DoseTypeGy aDoseReference); + + /*! @return Return true if calcIndex() finished sucessfully + @exception NullPointerException thrown if dvhSet is NULL + */ + bool init(); + + /*! @brief Dose index calculation for tvIndex-th treated volume + @param tvIndex index in the DVH in the current set of tv-DVHs + @return Return index value + @exception InvalidParameterException Thrown if tvIndex or aDoseReference invalid + */ + IndexValueType getDoseIndexAt(const GridIndexType tvIndex); + + }; + } +} + +#endif diff --git a/code/indices/rttbConformityIndex.cpp b/code/indices/rttbConformityIndex.cpp new file mode 100644 index 0000000..b4f2c7a --- /dev/null +++ b/code/indices/rttbConformityIndex.cpp @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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 "rttbConformityIndex.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + +namespace rttb{ + + namespace indices{ + + ConformityIndex::ConformityIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference) + { + _dvhSet=dvhSet; + _doseReference=aDoseReference; + initSuccess=false; + } + + bool ConformityIndex::init() + { + if(!_dvhSet){ + throw core::NullPointerException("DVHSet must not be NULL! "); + } + if( this->calcIndex()){ + initSuccess=true; + return true; + } + else + return false; + } + + bool ConformityIndex::calcIndex() + { + VolumeType TV=_dvhSet->getTVVolume(0); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + + if(TV!=0 && Vref!=0){ + _value=(_dvhSet->getTVVolume(this->_doseReference)/TV)*(1-_dvhSet->getHTVolume(_doseReference)/Vref); + } + else if(TV==0){ + throw core::InvalidParameterException("DVH Set invalid: Target volume should not be 0!"); + } + else{ + rttbExceptionMacro(core::InvalidParameterException, << "Reference dose "<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + return true; + } + + IndexValueType ConformityIndex::getDoseIndexAt(GridIndexType tvIndex){ + std::vector dvhTVSet=this->_dvhSet->getDVHTVSet(); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + if(tvIndex>=dvhTVSet.size()){ + rttbExceptionMacro(core::InvalidParameterException, <<"tvIndex invalid: it should be <"<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + double value=dvh.getVx(_doseReference)/TV;//the irradiation factor of i-th treated volume + value=value*(1-_dvhSet->getHTVolume(_doseReference)/Vref); + return value; + } + } + + }//end namespace indices + }//end namespace rttb diff --git a/code/indices/rttbConformityIndex.h b/code/indices/rttbConformityIndex.h new file mode 100644 index 0000000..adb18a1 --- /dev/null +++ b/code/indices/rttbConformityIndex.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 __CONFORMITY_INDEX_H +#define __CONFORMITY_INDEX_H + + +#include "rttbDoseIndex.h" +#include "rttbBaseType.h" + +namespace rttb{ + + namespace indices{ + + /*! @class ConformityIndex + @brief This class representing a ConformityIndex Object. Conformity Index (CI): CI(D)=IFtv(D)*(1-IFht(D)), D:reference dose, + IFtv(D): the irradiation factor of the PTV, defined as the fraction of the PTV receiving a dose higher than D + IFht(D): the irradiation factor of healthy tissue, defined as the radio of the volume of tissue outside the PTV receiving a dose greater than D to the volume of isodose D + @ingroup indices + */ + class ConformityIndex: public DoseIndex + { + protected: + /*! @brief Calculate Conformity index + @exception InvalidParameterException Thrown if dvhSet or aDoseReference invalid + */ + bool calcIndex(); + + + public: + /*! @brief Constructor + */ + ConformityIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference); + + /*! @return Return true if calcIndex() finished sucessfully + @exception NullPointerException thrown if dvhSet is NULL + */ + bool init(); + + /*! @brief Dose index calculation for tvIndex-th treated volume + @param tvIndex index in the DVH in the current set of tv-DVHs + @return Return index value + @exception InvalidParameterException Thrown if tvIndex or aDoseReference invalid + */ + IndexValueType getDoseIndexAt(const GridIndexType tvIndex); + + }; + + } +} + +#endif diff --git a/code/indices/rttbCoverageIndex.cpp b/code/indices/rttbCoverageIndex.cpp new file mode 100644 index 0000000..546807d --- /dev/null +++ b/code/indices/rttbCoverageIndex.cpp @@ -0,0 +1,78 @@ +// ----------------------------------------------------------------------- +// 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 "rttbCoverageIndex.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + +namespace rttb{ + + namespace indices{ + + CoverageIndex::CoverageIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference) + { + _dvhSet=dvhSet; + _doseReference=aDoseReference; + initSuccess=false; + } + + bool CoverageIndex::init() + { + if(!_dvhSet){ + throw core::NullPointerException("DVHSet must not be NULL! "); + } + if( this->calcIndex()){ + initSuccess=true; + return true; + } + else + return false; + } + + bool CoverageIndex::calcIndex() + { + VolumeType TV=_dvhSet->getTVVolume(0); + if(TV!=0) + _value=_dvhSet->getTVVolume(this->_doseReference)/TV; + else{ + throw core::InvalidParameterException("DVH Set invalid: Target volume should not be 0!"); + } + return true; + } + + IndexValueType CoverageIndex::getDoseIndexAt(GridIndexType tvIndex){ + std::vector dvhTVSet=this->_dvhSet->getDVHTVSet(); + VolumeType Vref=_dvhSet->getWholeVolume(_doseReference); + if(tvIndex>=dvhTVSet.size()){ + rttbExceptionMacro(core::InvalidParameterException, <<"tvIndex invalid: it should be <"< +#include + +#include "rttbDoseIndex.h" +#include "rttbBaseType.h" +#include "rttbDVHSet.h" + +namespace rttb{ + + namespace indices{ + + /*! @class CoverageIndex + @brief This class representing a CoverageIndex Object. Coverage Index fraction of the target volume receiving a dose >= the reference dose + @ingroup indices + */ + class CoverageIndex: public DoseIndex + { + + protected: + /*! @brief Calculate conformation number + @exception InvalidParameterException Thrown if dvhSet invalid + */ + bool calcIndex(); + + + public: + /*! @brief Constructor + */ + CoverageIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference); + + /*! @return Return true if calcIndex() finished sucessfully + @exception NullPointerException thrown if dvhSet is NULL + @see calcIndex + */ + bool init(); + + /*! @brief Dose index calculation for tvIndex-th treated volume + * @param tvIndex: index in the vector of DVH TV + * @return Return index value + @exception InvalidParameterException Thrown if tvIndex invalid + */ + IndexValueType getDoseIndexAt(const GridIndexType tvIndex); + + + + }; + + } +} + +#endif diff --git a/code/indices/rttbDoseIndex.cpp b/code/indices/rttbDoseIndex.cpp new file mode 100644 index 0000000..bb187c7 --- /dev/null +++ b/code/indices/rttbDoseIndex.cpp @@ -0,0 +1,50 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDoseIndex.h" +#include "rttbException.h" + +namespace rttb{ + namespace indices{ + void DoseIndex::setDoseReference(DoseTypeGy aDoseReference) + { + _doseReference=aDoseReference; + initSuccess=false; + } + + DoseTypeGy DoseIndex::getDoseReference() const + { + return _doseReference; + } + + IndexValueType DoseIndex::getValue() const + { + if(initSuccess){ + return _value; + } + else{ + throw core::Exception("DoseIndex init error: init() must be called first!"); + } + } + } + } + + diff --git a/code/indices/rttbDoseIndex.h b/code/indices/rttbDoseIndex.h new file mode 100644 index 0000000..6897a36 --- /dev/null +++ b/code/indices/rttbDoseIndex.h @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// 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_INDEX_H +#define __DOSE_INDEX_H + + +#include "rttbBaseType.h" +#include "rttbDVHSet.h" + +namespace rttb{ + + + namespace indices{ + /*! @class DoseIndex + @brief This is the interface for dose/plan comparison indices. + @ingroup indices + */ + class DoseIndex + { + protected: + + /*! @todo Use Shared Pointers for _dvhSet*/ + core::DVHSet* _dvhSet; + + IndexValueType _value; + + DoseTypeGy _doseReference; + + /*! @brief If init() successful*/ + bool initSuccess; + + /*! @brief Dose index calculation */ + virtual bool calcIndex()=0; + + + public: + /*! @brief Initialize the calculation + @return Return true if successfully + */ + virtual bool init()=0; + + /*! @brief Set the reference dose + */ + void setDoseReference(DoseTypeGy aDoseReference); + + /*! @brief Get the reference dose + */ + DoseTypeGy getDoseReference() const; + + /*! @brief Get the value of dose/plan comparison index + @return Return the value of this index + @exception Exception Thrown if the class was not initialized previously. + */ + IndexValueType getValue() const; + + /*! @brief Dose/plan comparison index calculation for tvIndex-th treated volume + (tv = target volume; th = healthy tissue) + @param tvIndex index in the DVH in the current set of tv-DVHs + @todo is this name good? getIndexAt() instead? + */ + virtual IndexValueType getDoseIndexAt(const GridIndexType tvIndex)=0; + }; + } +} + + +#endif diff --git a/code/indices/rttbHomogeneityIndex.cpp b/code/indices/rttbHomogeneityIndex.cpp new file mode 100644 index 0000000..6d74d0a --- /dev/null +++ b/code/indices/rttbHomogeneityIndex.cpp @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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 "rttbHomogeneityIndex.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + +namespace rttb{ + namespace indices{ + + HomogeneityIndex::HomogeneityIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference) + { + _dvhSet=dvhSet; + _doseReference=aDoseReference; + initSuccess=false; + } + + bool HomogeneityIndex::init() + { + if(!_dvhSet){ + throw core::NullPointerException("DVHSet must not be NULL! "); + } + if(this->calcIndex()){ + initSuccess=true; + return true; + } + return false; + } + + bool HomogeneityIndex::calcIndex() + { + double max=0; + double min; + std::vector dvhTVSet=this->_dvhSet->getDVHTVSet(); + std::vector::iterator it; + + for(it=dvhTVSet.begin(); it!=dvhTVSet.end();it++) + { + core::DVH dvh=*(it); + if(it==dvhTVSet.begin()) + min=dvh.getMinimum(); + if(dvh.getMaximum()>max) + max=dvh.getMaximum(); + if(dvh.getMinimum()getDoseReference()!=0){ + _value=(max-min)/this->getDoseReference(); + } + else{ + rttbExceptionMacro(core::InvalidParameterException, << "Reference dose "<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + return true; + + } + + IndexValueType HomogeneityIndex::getDoseIndexAt(GridIndexType tvIndex){ + std::vector dvhTVSet=this->_dvhSet->getDVHTVSet(); + if(tvIndex>=dvhTVSet.size()){ + rttbExceptionMacro(core::InvalidParameterException, <<"tvIndex invalid: it should be <"<getDoseReference()<=0){ + rttbExceptionMacro(core::InvalidParameterException, << "Reference dose "<getDoseReference()<<" invalid: Volume of reference dose should not be 0!"); + } + return (dvh.getMaximum()-dvh.getMinimum())/this->getDoseReference(); + } + + }//end namespace indices +}//end namespace rttb \ No newline at end of file diff --git a/code/indices/rttbHomogeneityIndex.h b/code/indices/rttbHomogeneityIndex.h new file mode 100644 index 0000000..53a8a73 --- /dev/null +++ b/code/indices/rttbHomogeneityIndex.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 __HOMOGENEITY_INDEX_H +#define __HOMOGENEITY_INDEX_H + +#include +#include + +#include "rttbDoseIndex.h" +#include "rttbBaseType.h" + +namespace rttb{ + + namespace indices{ + /*! @class HomogeneityIndex + @brief This class representing a HomogeneityIndex Object. Homogeneity Index (HI) = (Dmax(PTV)-Dmin(PTV))/Dref + @ingroup indices + */ + class HomogeneityIndex: public DoseIndex + { + protected: + /*! @brief Calculate Conformity index + @exception InvalidParameterException Thrown if aDoseReference invalid + */ + bool calcIndex(); + + + public: + /*! @brief Constructor + */ + HomogeneityIndex(core::DVHSet* dvhSet, DoseTypeGy aDoseReference); + + /*! @return Return true if calcIndex() finished sucessfully + @exception NullPointerException thrown if dvhSet is NULL + */ + bool init(); + + /*! @brief Dose index calculation for tvIndex-th treated volume + @param tvIndex index in the DVH in the current set of tv-DVHs + @return Return index value + @exception InvalidParameterException Thrown if tvIndex or aDoseReference invalid + */ + IndexValueType getDoseIndexAt(const GridIndexType tvIndex); + + }; + + } +} + + +#endif diff --git a/code/interpolation/CMakeLists.txt b/code/interpolation/CMakeLists.txt new file mode 100644 index 0000000..3aa2474 --- /dev/null +++ b/code/interpolation/CMakeLists.txt @@ -0,0 +1,7 @@ +OPTION(BUILD_MatchPointBinding + "Determine if the MatchPoint binding for dose interpolation classes will be generated." OFF) +IF(BUILD_MatchPointBinding) +ADD_SUBDIRECTORY(MatchPointBinding) +ENDIF(BUILD_MatchPointBinding) + +RTTB_CREATE_MODULE(RTTBInterpolation DEPENDS RTTBCore PACKAGE_DEPENDS Boost) \ No newline at end of file diff --git a/code/interpolation/MatchPointBinding/CMakeLists.txt b/code/interpolation/MatchPointBinding/CMakeLists.txt new file mode 100644 index 0000000..baa2ca0 --- /dev/null +++ b/code/interpolation/MatchPointBinding/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBMatchPointBinding DEPENDS RTTBCore RTTBInterpolation PACKAGE_DEPENDS Boost MatchPoint ITK) \ No newline at end of file diff --git a/code/interpolation/MatchPointBinding/files.cmake b/code/interpolation/MatchPointBinding/files.cmake new file mode 100644 index 0000000..e53490c --- /dev/null +++ b/code/interpolation/MatchPointBinding/files.cmake @@ -0,0 +1,7 @@ +SET(CPP_FILES + rttbMatchPointTransformation.cpp + ) + +SET(H_FILES + rttbMatchPointTransformation.h + ) diff --git a/code/interpolation/MatchPointBinding/rttbMatchPointTransformation.cpp b/code/interpolation/MatchPointBinding/rttbMatchPointTransformation.cpp new file mode 100644 index 0000000..a663564 --- /dev/null +++ b/code/interpolation/MatchPointBinding/rttbMatchPointTransformation.cpp @@ -0,0 +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 "rttbMatchPointTransformation.h" +#include "rttbPaddingException.h" +#include "rttbNullPointerException.h" + +namespace rttb +{ + namespace interpolation + { + MatchPointTransformation::MatchPointTransformation( + const Registration3D3DType* aRegistration): _pRegistration(aRegistration) + { + //handle null pointers + if (aRegistration == NULL) + { + throw core::NullPointerException("Pointer to registration is NULL."); + } + } + + void MatchPointTransformation::convert(const WorldCoordinate3D& aWorldCoordinate, + TargetPointType& aTargetPoint) const + { + assert(aWorldCoordinate.size() == 3); + assert(aTargetPoint.Length == 3); + + for (unsigned int i = 0; i < aTargetPoint.Length; ++i) + { + aTargetPoint[i] = aWorldCoordinate[i]; + } + } + + void MatchPointTransformation::convert(const MovingPointType& aMovingPoint, + WorldCoordinate3D& aWorldCoordinate) const + { + assert(aWorldCoordinate.size() == 3); + assert(aMovingPoint.Length == 3); + + for (unsigned int i = 0; i < aMovingPoint.Length; ++i) + { + aWorldCoordinate[i] = aMovingPoint[i]; + } + } + + bool MatchPointTransformation::transformInverse(const WorldCoordinate3D& + worldCoordinateTarget, WorldCoordinate3D& worldCoordinateMoving) const + { + TargetPointType aTargetPoint; + MovingPointType aMovingPoint; + + convert(worldCoordinateTarget, aTargetPoint); + bool ok = _pRegistration->mapPointInverse(aTargetPoint, aMovingPoint); + convert(aMovingPoint, worldCoordinateMoving); + return ok; + } + + bool MatchPointTransformation::transform(const WorldCoordinate3D& worldCoordinateMoving, + WorldCoordinate3D& worldCoordinateTarget) const + { + TargetPointType aTargetPoint; + MovingPointType aMovingPoint; + + convert(worldCoordinateMoving, aMovingPoint); + bool ok = _pRegistration->mapPoint(aMovingPoint, aTargetPoint); + convert(aTargetPoint, worldCoordinateTarget); + return ok; + } + + } +} diff --git a/code/interpolation/MatchPointBinding/rttbMatchPointTransformation.h b/code/interpolation/MatchPointBinding/rttbMatchPointTransformation.h new file mode 100644 index 0000000..e96d10c --- /dev/null +++ b/code/interpolation/MatchPointBinding/rttbMatchPointTransformation.h @@ -0,0 +1,82 @@ +// ----------------------------------------------------------------------- +// 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 __MATCHPOINT_TRANSFORMATION_H +#define __MATCHPOINT_TRANSFORMATION_H + +#include + +#include "mapRegistration.h" + +#include "rttbTransformationInterface.h" +#include "rttbBaseType.h" + + +namespace rttb +{ + namespace interpolation + { + /*! @class MatchPointTransformation + @brief This class can deal with dose information that has to be transformed into another geometry than the original dose image (transformation specified by MatchPoint registration object). + @ingroup interpolation + */ + class MatchPointTransformation: public TransformationInterface + { + public: + static const unsigned int TargetDimension3D = 3; + static const unsigned int MovingDimension3D = 3; + typedef map::core::Registration Registration3D3DType; + typedef map::core::Registration::MovingPointType + MovingPointType; + typedef map::core::Registration::TargetPointType + TargetPointType; + + /*! @brief Constructor. + @param aRegistration registration given in MatchPoint format (note the use of pointer since itkSmartPointer does not support inheritance) + @pre all input parameters have to be valid + @exception core::NullPointerException if one input parameter is NULL + @exception core::PaddingException if the transformation is undefined and if _acceptPadding==false + */ + MatchPointTransformation(const Registration3D3DType* aRegistration); + + ~MatchPointTransformation() {}; + + /*! @brief performs a transformation targetImage --> movingImage + */ + bool transformInverse(const WorldCoordinate3D& worldCoordinateTarget, + WorldCoordinate3D& worldCoordinateMoving) const override; + + /*! @brief performs a transformation movingImage --> targetImage + */ + bool transform(const WorldCoordinate3D& worldCoordinateMoving, + WorldCoordinate3D& worldCoordinateTarget) const override; + + protected: + void convert(const WorldCoordinate3D& aWorldCoordinate, TargetPointType& aTargetPoint) const; + void convert(const MovingPointType& aMovingPoint, WorldCoordinate3D& aWorldCoordinate) const; + + private: + //! Has to be a Pointer type because of inheritance issues with itkSmartPointer (that doesn't recognize the inheritance) + const Registration3D3DType* _pRegistration; + }; + } +} + +#endif diff --git a/code/interpolation/files.cmake b/code/interpolation/files.cmake new file mode 100644 index 0000000..f4120aa --- /dev/null +++ b/code/interpolation/files.cmake @@ -0,0 +1,18 @@ +SET(CPP_FILES + rttbMappableDoseAccessorBase.cpp + rttbRosuMappableDoseAccessor.cpp + rttbSimpleMappableDoseAccessor.cpp + rttbInterpolationBase.cpp + rttbNearestNeighborInterpolation.cpp + rttbLinearInterpolation.cpp + ) + +SET(H_FILES + rttbMappableDoseAccessorBase.h + rttbRosuMappableDoseAccessor.h + rttbSimpleMappableDoseAccessor.h + rttbInterpolationBase.h + rttbNearestNeighborInterpolation.h + rttbLinearInterpolation.h + rttbTransformationInterface.h + ) diff --git a/code/interpolation/rttbInterpolationBase.cpp b/code/interpolation/rttbInterpolationBase.cpp new file mode 100644 index 0000000..0bce389 --- /dev/null +++ b/code/interpolation/rttbInterpolationBase.cpp @@ -0,0 +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) +*/ + +#include "rttbInterpolationBase.h" +#include "rttbInvalidParameterException.h" +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" + +namespace rttb +{ + namespace interpolation + { + void InterpolationBase::setDoseAccessorPointer(const DoseAccessorPointer originalDose) + { + if (originalDose != NULL) + { + _spOriginalDose = originalDose; + } + else + { + throw core::NullPointerException("originalDose is NULL!"); + } + }; + + void InterpolationBase::getNeighborhoodVoxelValues( + const WorldCoordinate3D& aWorldCoordinate, + unsigned int neighborhood, boost::array& target, + boost::shared_array values) const + { + if (_spOriginalDose == NULL) + { + throw core::NullPointerException("originalDose is NULL!"); + } + + //Determine target (abs(desired worldCoordinate- corner pixel world coordinate/pixel spacing) and values of corner pixels (from originalDose) + VoxelGridIndex3D aIndex; + + if (_spOriginalDose->getGeometricInfo().worldCoordinateToIndex(aWorldCoordinate, aIndex)) + { + //determine just the nearest voxel to the world coordinate + if (neighborhood == 0) + { + values[0] = _spOriginalDose->getDoseAt(aIndex); + } + //determine the 8 voxels around the world coordinate + else if (neighborhood == 8) + { + std::vector cornerPoints; + + WorldCoordinate3D theNextVoxel; + _spOriginalDose->getGeometricInfo().indexToWorldCoordinate(aIndex, theNextVoxel); + SpacingVectorType3D pixelSpacing = (_spOriginalDose->getGeometricInfo()).getSpacing(); + VoxelGridIndex3D leftTopFrontCoordinate; + + //find the voxel with the smallest coordinate values in each dimension. This defines the standard cube + for (int i = 0; i < 3; i++) + { + if (aWorldCoordinate[i] < theNextVoxel[i]) + { + if (aIndex[i] > 0) + { + leftTopFrontCoordinate[i] = aIndex[i] - 1; + target[i] = (aWorldCoordinate[i] - (theNextVoxel[i] - pixelSpacing[i])) / pixelSpacing[i]; + } + //@todo: this is a workaround, not a good solution + else + { + leftTopFrontCoordinate[i] = aIndex[i]; + target[i] = (aWorldCoordinate[i] - (theNextVoxel[i] - pixelSpacing[i])) / pixelSpacing[i]; + } + } + else + { + leftTopFrontCoordinate[i] = aIndex[i]; + target[i] = (aWorldCoordinate[i] - theNextVoxel[i]) / pixelSpacing[i]; + } + } + + for (int zIncr = 0; zIncr < 2; zIncr++) + { + for (int yIncr = 0; yIncr < 2; yIncr++) + { + for (int xIncr = 0; xIncr < 2; xIncr++) + { + cornerPoints.push_back(VoxelGridIndex3D(leftTopFrontCoordinate[0] + xIncr, + leftTopFrontCoordinate[1] + yIncr, + leftTopFrontCoordinate[2] + zIncr)); + } + } + } + + //target range has to be always [0,1] + for (int i = 0; i < 3; i++) + { + assert(target[i] >= 0.0 && target[i] <= 1.0); + } + + //now just get the values of all (dose) voxels and store them in values + for (int i = 0; i < cornerPoints.size(); ++i) + { + if (_spOriginalDose->getGeometricInfo().isInside(cornerPoints.at(i))) + { + values[i] = _spOriginalDose->getDoseAt(cornerPoints.at(i)); + } + else + { + //outside value! boundary treatment + values[i] = getNearestInsideVoxelValue(cornerPoints.at(i)); + } + + assert(values[i] != -1); + } + } + else + { + throw core::InvalidParameterException("neighborhoods other than 0 and 8 not yet supported in Interpolation"); + } + } + else + { + throw core::MappingOutsideOfImageException("Error in conversion from world coordinates to index"); + } + } + + DoseTypeGy InterpolationBase::getNearestInsideVoxelValue(const VoxelGridIndex3D& currentVoxelIndex) + const + { + VoxelGridIndex3D voxelChangedXYZ[] = {currentVoxelIndex, currentVoxelIndex, currentVoxelIndex, currentVoxelIndex, currentVoxelIndex, currentVoxelIndex, currentVoxelIndex}; + int runningIndex; + + //x,y,z + for (runningIndex = 0; runningIndex < 3; ++runningIndex) + { + voxelChangedXYZ[runningIndex][runningIndex] -= 1; + } + + //xy + voxelChangedXYZ[runningIndex][0] -= 1; + voxelChangedXYZ[runningIndex][1] -= 1; + ++runningIndex; + //xz + voxelChangedXYZ[runningIndex][0] -= 1; + voxelChangedXYZ[runningIndex][2] -= 1; + ++runningIndex; + //yz + voxelChangedXYZ[runningIndex][1] -= 1; + voxelChangedXYZ[runningIndex][2] -= 1; + ++runningIndex; + //xyz + voxelChangedXYZ[runningIndex][0] -= 1; + voxelChangedXYZ[runningIndex][1] -= 1; + voxelChangedXYZ[runningIndex][2] -= 1; + ++runningIndex; + + int replacementVoxelIndex = 0; + + while (replacementVoxelIndex < runningIndex) + { + if (_spOriginalDose->getGeometricInfo().validIndex(voxelChangedXYZ[replacementVoxelIndex])) + { + return _spOriginalDose->getDoseAt(voxelChangedXYZ[replacementVoxelIndex]); + } + + ++replacementVoxelIndex; + } + + return -1; + } + + + }//end namespace core +}//end namespace rttb diff --git a/code/interpolation/rttbInterpolationBase.h b/code/interpolation/rttbInterpolationBase.h new file mode 100644 index 0000000..b0a28a8 --- /dev/null +++ b/code/interpolation/rttbInterpolationBase.h @@ -0,0 +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 __INTERPOLATION_BASE_H +#define __INTERPOLATION_BASE_H + +#include +#include + +#include +#include +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbBaseType.h" + +namespace rttb +{ + namespace interpolation + { + + /*! @class InterpolationBase + @brief Base class for interpolation. + @ingroup interpolation + */ + class InterpolationBase + { + public: + typedef boost::shared_ptr Pointer; + typedef rttb::core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + /*! @brief Constructor + */ + InterpolationBase() {}; + + /*! @brief Virtual destructor of base class + */ + virtual ~InterpolationBase() {}; + + /*! @brief Sets the DoseAccessPointer + @pre originalDose initialized + @exception core::NullPointerException if originalDose==NULL + */ + void setDoseAccessorPointer(const DoseAccessorPointer originalDose); + + /*! @brief Returns the interpolated value for the given world coordinate + */ + virtual DoseTypeGy getValue(const WorldCoordinate3D& aWorldCoordinate) const = 0; + + protected: + DoseAccessorPointer _spOriginalDose; + /*! @brief determines voxels in a certain neighborhood of a physical based coordinate and converts in a standard cube with corner points [0 0 0], [1 0 0], [0 1 0], [1 1 0], [0 0 1], [1 0 1], [0 1 1], [1 1 1]. + @param aWorldCoordinate the coordinate where to start + @param neighborhood voxel around coordinate (currently only 0 and 8 implemented) + @param target coordinates inside the standard cube with values [0 1] in each dimension. + @param values dose values at all corner points of the standard cube. Is of type boost:shared_array[neighborhood] + @pre target and values have to be correctly initialized (e.g. boost::array target = {0.0, 0.0, 0.0}; boost::shared_array values(new DoseTypeGy[8]()); where 8 is neighborhood) + @exception core::InvalidParameterException if neighborhood =! 0 && !=8 + @exception core::MappingOutsideOfImageException if initial mapping of aWorldCoordinate is outside image + @exception core::NullPointerException if dose is NULL + @todo use shared_ptr[] with boost >1.52 instead of shared_array + */ + void getNeighborhoodVoxelValues(const WorldCoordinate3D& aWorldCoordinate, + unsigned int neighborhood, boost::array& target, + boost::shared_array values) const; + + /*! @brief returns the nearest inside voxel value + @pre the voxelGridIndex is outside the image and voxelGridIndex>image.size() for all dimensions. Also voxelGridIndex[]>=0 for all dimensions + @note used for virtually expanding the image by one voxel as edge handling + */ + DoseTypeGy getNearestInsideVoxelValue(const VoxelGridIndex3D& currentVoxelIndex) const; + }; + + } +} + +#endif diff --git a/code/interpolation/rttbLinearInterpolation.cpp b/code/interpolation/rttbLinearInterpolation.cpp new file mode 100644 index 0000000..e54ab53 --- /dev/null +++ b/code/interpolation/rttbLinearInterpolation.cpp @@ -0,0 +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 "rttbLinearInterpolation.h" + +namespace rttb +{ + namespace interpolation + { + + DoseTypeGy LinearInterpolation::trilinear(boost::array target, + boost::shared_array values) const + { + //4 linear interpolation in x direction + DoseTypeGy c_00 = values[0] * (1.0 - target[0]) + values[1] * target[0]; + DoseTypeGy c_10 = values[2] * (1.0 - target[0]) + values[3] * target[0]; + DoseTypeGy c_01 = values[4] * (1.0 - target[0]) + values[5] * target[0]; + DoseTypeGy c_11 = values[6] * (1.0 - target[0]) + values[7] * target[0]; + + //combine result in y direction + DoseTypeGy c_0 = c_00 * (1.0 - target[1]) + c_10 * target[1]; + DoseTypeGy c_1 = c_01 * (1.0 - target[1]) + c_11 * target[1]; + + //finally incorporate z direction + return (c_0 * (1.0 - target[2]) + c_1 * target[2]); + } + + DoseTypeGy LinearInterpolation::getValue(const WorldCoordinate3D& aWorldCoordinate) const + { + //proper initialization of target and values + boost::array target = {0.0, 0.0, 0.0}; + boost::shared_array values(new DoseTypeGy[8]()); + getNeighborhoodVoxelValues(aWorldCoordinate, 8, target, values); + + return trilinear(target, values); + } + + } +} diff --git a/code/interpolation/rttbLinearInterpolation.h b/code/interpolation/rttbLinearInterpolation.h new file mode 100644 index 0000000..0556017 --- /dev/null +++ b/code/interpolation/rttbLinearInterpolation.h @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------- +// 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 __LINEAR_INTERPOLATION_H +#define __LINEAR_INTERPOLATION_H + +#include +#include + +#include "rttbInterpolationBase.h" + +namespace rttb +{ + + namespace interpolation + { + + /*! @class LinearInterpolation + @brief Linear interpolation. + @ingroup interpolation + */ + class LinearInterpolation : public InterpolationBase + { + public: + typedef rttb::core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + /*! @brief Constructor + */ + LinearInterpolation() {}; + /*! @brief Returns the interpolated value + */ + DoseTypeGy getValue(const WorldCoordinate3D& aWorldCoordinate) const; + + private: + /*! @brief Trilinar interpolation + @sa InterpolationBase for details about target and values + @note Source: http://en.wikipedia.org/wiki/Trilinear_interpolation + */ + DoseTypeGy trilinear(boost::array target, boost::shared_array values) const; + }; + + } +} + +#endif diff --git a/code/interpolation/rttbMappableDoseAccessorBase.cpp b/code/interpolation/rttbMappableDoseAccessorBase.cpp new file mode 100644 index 0000000..751ace4 --- /dev/null +++ b/code/interpolation/rttbMappableDoseAccessorBase.cpp @@ -0,0 +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) +*/ + +#include "rttbMappableDoseAccessorBase.h" +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" + +namespace rttb +{ + namespace interpolation + { + MappableDoseAccessorBase::MappableDoseAccessorBase(const core::GeometricInfo& geoInfoTargetImage, + const DoseAccessorPointer doseMovingImage, + const TransformationInterface::Pointer aTransformation, bool acceptPadding, + double defaultOutsideValue): _spOriginalDoseDataMovingImage(doseMovingImage), + _spTransformation(aTransformation), _geoInfoTargetImage(geoInfoTargetImage), + _acceptPadding(acceptPadding), _defaultOutsideValue(defaultOutsideValue) + { + //handle null pointers + if (doseMovingImage == NULL || aTransformation == NULL) + { + throw core::NullPointerException("Pointers to input accessors/transformation cannot be NULL."); + } + } + + bool MappableDoseAccessorBase::validID(const VoxelGridID aID) const + { + return (aID >= 0 && aID < getGridSize()); + } + + bool MappableDoseAccessorBase::validIndex(const VoxelGridIndex3D& aIndex) const + { + VoxelGridID aID; + + if (!convert(aIndex, aID)) + { + return false; + } + else + { + return validID(aID); + } + } + + DoseTypeGy MappableDoseAccessorBase::getDoseAt(const VoxelGridID aID) const + { + VoxelGridIndex3D aVoxelGridIndex3D; + + if (convert(aID, aVoxelGridIndex3D)) + { + return getDoseAt(aVoxelGridIndex3D); + } + else + { + if (_acceptPadding) + { + return _defaultOutsideValue; + } + else + { + throw core::MappingOutsideOfImageException("Error in conversion from index to world coordinates"); + return -1; + } + } + } + + bool MappableDoseAccessorBase::convert(const VoxelGridID& gridID, VoxelGridIndex3D& gridIndex) const + { + if (validID(gridID)) + { + gridIndex(0) = gridID % _geoInfoTargetImage.getNumColumns(); + VoxelGridID tempID = (gridID - gridIndex.x()) / _geoInfoTargetImage.getNumColumns(); + gridIndex(1) = tempID % _geoInfoTargetImage.getNumRows(); + gridIndex(2) = (tempID - gridIndex.y()) / _geoInfoTargetImage.getNumRows(); + return true; + } + + return false; + } + + bool MappableDoseAccessorBase::convert(const VoxelGridIndex3D& gridIndex, VoxelGridID& gridID) const + { + if ((gridIndex.x() >= (unsigned int)_geoInfoTargetImage.getNumColumns()) + || (gridIndex.y() >= (unsigned int)_geoInfoTargetImage.getNumRows()) + || (gridIndex.z() >= (unsigned int)_geoInfoTargetImage.getNumSlices())) + { + return false; + } + else + { + gridID = gridIndex.z() * _geoInfoTargetImage.getNumColumns() * _geoInfoTargetImage.getNumRows() + + gridIndex.y() * _geoInfoTargetImage.getNumColumns() + + gridIndex.x(); + return validID(gridID); + } + } + + } +} diff --git a/code/interpolation/rttbMappableDoseAccessorBase.h b/code/interpolation/rttbMappableDoseAccessorBase.h new file mode 100644 index 0000000..7d882e0 --- /dev/null +++ b/code/interpolation/rttbMappableDoseAccessorBase.h @@ -0,0 +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 __MAPPABLE_DOSE_ACCESSOR_BASE_H +#define __MAPPABLE_DOSE_ACCESSOR_BASE_H + +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" +#include "rttbTransformationInterface.h" + +namespace rttb +{ + namespace interpolation + { + + /*! @class MappableDoseAccessorBase + @brief Base class for dealing with dose information that has to be transformed into another geometry than the original dose image + @details implementation of strategy is done by derived class (e.g. SimpleMappableDoseAccessor or RosuMappableDoseAccessor. Transformation is defined in TransformationInterface + @ingroup interpolation + */ + class MappableDoseAccessorBase: public core::DoseAccessorInterface + { + public: + typedef boost::shared_ptr Pointer; + protected: + DoseAccessorPointer _spOriginalDoseDataMovingImage; + TransformationInterface::Pointer _spTransformation; + + core::GeometricInfo _geoInfoTargetImage; + bool _acceptPadding; + DoseTypeGy _defaultOutsideValue; + + bool convert(const VoxelGridID& gridID, VoxelGridIndex3D& gridIndex) const; + + bool convert(const VoxelGridIndex3D& gridIndex, VoxelGridID& gridID) const; + + public: + /*! @brief Constructor. + @param geoInfoTargetImage target image geometry + @param doseMovingImage dose of moving image + @param aTransformation the transformation + @param acceptPadding is mapping outside the image allowed + @param defaultOutsideValue the default outside voxel value if accepptPadding=true + @pre all input parameters have to be valid + @exception core::NullPointerException if one input parameter is NULL + */ + MappableDoseAccessorBase(const core::GeometricInfo& geoInfoTargetImage, + const DoseAccessorPointer doseMovingImage, const TransformationInterface::Pointer aTransformation, + bool acceptPadding = true, + DoseTypeGy defaultOutsideValue = 0.0); + + /*! @brief Virtual destructor of base class + */ + virtual ~MappableDoseAccessorBase() {}; + + /*! @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; + + inline const core::GeometricInfo& getGeometricInfo() const + { + return _geoInfoTargetImage; + }; + + inline GridSizeType getGridSize() const + { + return _geoInfoTargetImage.getNumberOfVoxels(); + }; + + /*! @brief Returns the dose for a given VoxelGridID (convenience function that handles conversion VoxelGridID->VoxelGridIndex3D) + @sa getDoseAt(const VoxelGridIndex3D& aIndex) + */ + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + /*! @brief Returns the dose for a given VoxelGridIndex3D + */ + virtual DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const = 0 ; + + const IDType getDoseUID() const + { + return _spOriginalDoseDataMovingImage->getDoseUID(); + }; + }; + } +} + +#endif diff --git a/code/interpolation/rttbNearestNeighborInterpolation.cpp b/code/interpolation/rttbNearestNeighborInterpolation.cpp new file mode 100644 index 0000000..e3e0484 --- /dev/null +++ b/code/interpolation/rttbNearestNeighborInterpolation.cpp @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// 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 "rttbNearestNeighborInterpolation.h" + +namespace rttb +{ + namespace interpolation + { + DoseTypeGy NearestNeighborInterpolation::getValue(const WorldCoordinate3D& aWorldCoordinate) const + { + //proper initialization of target and values (although target is irrelevant in nearest neighbor case) + boost::array target = {0.0, 0.0, 0.0}; + boost::shared_array values(new DoseTypeGy[8]()); + getNeighborhoodVoxelValues(aWorldCoordinate, 0, target, values); + return values[0]; + } + + } +} diff --git a/code/interpolation/rttbNearestNeighborInterpolation.h b/code/interpolation/rttbNearestNeighborInterpolation.h new file mode 100644 index 0000000..1baf3fa --- /dev/null +++ b/code/interpolation/rttbNearestNeighborInterpolation.h @@ -0,0 +1,54 @@ +// ----------------------------------------------------------------------- +// 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 __NEAREST_NEIGHBOR_INTERPOLATION_H +#define __NEAREST_NEIGHBOR_INTERPOLATION_H + +#include +#include + +#include "rttbInterpolationBase.h" + +namespace rttb +{ + namespace interpolation + { + + /*! @class NearestNeighborInterpolation + @brief Nearest Neighbor interpolation + @ingroup interpolation + */ + class NearestNeighborInterpolation : public InterpolationBase + { + public: + /*! @brief Constructor + */ + NearestNeighborInterpolation() {}; + + /*! @brief Returns the interpolated value (the nearest voxel value given by _spOriginalDose->getGeometricInfo().worldCoordinateToIndex()) + */ + DoseTypeGy getValue(const WorldCoordinate3D& aWorldCoordinate) const; + }; + + } +} + +#endif diff --git a/code/interpolation/rttbRosuMappableDoseAccessor.cpp b/code/interpolation/rttbRosuMappableDoseAccessor.cpp new file mode 100644 index 0000000..22cddab --- /dev/null +++ b/code/interpolation/rttbRosuMappableDoseAccessor.cpp @@ -0,0 +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 "rttbRosuMappableDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" +#include "rttbLinearInterpolation.h" + +namespace rttb +{ + namespace interpolation + { + RosuMappableDoseAccessor::RosuMappableDoseAccessor(const core::GeometricInfo& geoInfoTargetImage, + const DoseAccessorPointer doseMovingImage, + const TransformationInterface::Pointer aTransformation, + bool acceptPadding, + DoseTypeGy defaultOutsideValue): MappableDoseAccessorBase(geoInfoTargetImage, doseMovingImage, + aTransformation, acceptPadding, defaultOutsideValue) + { + //define linear interpolation + InterpolationBase::Pointer interpolationLinear = InterpolationBase::Pointer( + new LinearInterpolation()); + _spInterpolation = interpolationLinear; + _spInterpolation->setDoseAccessorPointer(_spOriginalDoseDataMovingImage); + } + + DoseTypeGy RosuMappableDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + //Transform requested voxel coordinates of original image into world coordinates with RTTB + WorldCoordinate3D worldCoordinateTarget; + + if (_geoInfoTargetImage.indexToWorldCoordinate(aIndex, worldCoordinateTarget)) + { + std::vector octants = getOctants(worldCoordinateTarget); + + if (octants.size() > 2) + { + DoseTypeGy interpolatedDoseValue = 0.0; + + //get trilinear interpolation value of every octant point + for (std::vector::const_iterator octantIterator = std::begin(octants); + octantIterator != std::end(octants); + ++octantIterator) + { + //transform coordinates + WorldCoordinate3D worldCoordinateMoving; + WorldCoordinate3D worldCoordinateTargetOctant = *octantIterator; + _spTransformation->transformInverse(worldCoordinateTargetOctant, worldCoordinateMoving); + + try + { + interpolatedDoseValue += _spInterpolation->getValue(worldCoordinateMoving); + } + //Mapped outside of image? Check if padding is allowed + catch (core::MappingOutsideOfImageException& /*e*/) + { + if (_acceptPadding) + { + interpolatedDoseValue += _defaultOutsideValue; + } + else + { + throw core::MappingOutsideOfImageException("Mapping outside of image"); + } + } + catch (core::Exception& e) + { + std::cout << e.what() << std::endl; + return -1; + } + } + + return interpolatedDoseValue / (DoseTypeGy)octants.size(); + } + else + { + if (_acceptPadding) + { + return _defaultOutsideValue; + } + else + { + throw core::MappingOutsideOfImageException("Too many samples are mapped outside the image!"); + return -1; + } + } + } + else + { + if (_acceptPadding) + { + return _defaultOutsideValue; + } + else + { + throw core::MappingOutsideOfImageException("Error in conversion from index to world coordinates"); + return -1; + } + } + } + + std::vector RosuMappableDoseAccessor::getOctants( + const WorldCoordinate3D& aCoordinate) const + { + std::vector octants; + SpacingVectorType3D spacingTargetImage = _geoInfoTargetImage.getSpacing(); + + core::GeometricInfo geometricInfoDoseData = _spOriginalDoseDataMovingImage->getGeometricInfo(); + + //as the corner point is the coordinate of the voxel (grid), 0.25 and 0.75 are the center of the subvoxels + for (double xOct = 0.25; xOct <= 0.75; xOct += 0.5) + { + for (double yOct = 0.25; yOct <= 0.75; yOct += 0.5) + { + for (double zOct = 0.25; zOct <= 0.75; zOct += 0.5) + { + WorldCoordinate3D aWorldCoordinate(aCoordinate.x() + (xOct * spacingTargetImage.x()), + aCoordinate.y() + (yOct * spacingTargetImage.y()), + aCoordinate.z() + (zOct * spacingTargetImage.z())); + + if (geometricInfoDoseData.isInside(aWorldCoordinate)) + { + octants.push_back(aWorldCoordinate); + } + } + } + } + + return octants; + } + + + }//end namespace interpolation +}//end namespace rttb diff --git a/code/interpolation/rttbRosuMappableDoseAccessor.h b/code/interpolation/rttbRosuMappableDoseAccessor.h new file mode 100644 index 0000000..76cf50c --- /dev/null +++ b/code/interpolation/rttbRosuMappableDoseAccessor.h @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// 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 __ROSU_MAPPABLE_DOSE_ACCESSOR_H +#define __ROSU_MAPPABLE_DOSE_ACCESSOR_H + +#include + +#include "rttbBaseType.h" +#include "rttbInterpolationBase.h" +#include "rttbTransformationInterface.h" +#include "rttbMappableDoseAccessorBase.h" + +namespace rttb +{ + namespace interpolation + { + + /*! @class RosuMappableDoseAccessor + @brief Class for dose mapping based on interpolation described in the Rosu2005 paper + @details implementation of the following paper: Rosu, M., Chetty, I. J., Balter, J. M., Kessler, M. L., McShan, D. L., & Ten Haken, R. K. (2005). Dose reconstruction in deforming lung anatomy: Dose grid size effects and clinical implications. Medical Physics, 32(8), 2487. + @ingroup interpolation + */ + class RosuMappableDoseAccessor: public MappableDoseAccessorBase + { + private: + InterpolationBase::Pointer _spInterpolation; + + public: + typedef boost::shared_ptr Pointer; + + /*! @brief Constructor. Just hands values over to base class constructor. + @note no interpolation as parameter since linear interpolation is fixed. + @sa MappableDoseAccessorBase + */ + RosuMappableDoseAccessor(const core::GeometricInfo& geoInfoTargetImage, + const DoseAccessorPointer doseMovingImage, + const TransformationInterface::Pointer aTransformation, + bool acceptPadding = true, + DoseTypeGy defaultOutsideValue = 0.0); + + /*! @brief Virtual destructor. + */ + virtual ~RosuMappableDoseAccessor() {}; + + /*! @brief Returns the dose for a given voxel grid index. The computation of the octant around the voxel is done and the interpolation is performed. + @details Boundary treatment: if more than 6 subvoxels are outside: return _defaultOutsideValue. Otherwise: ignore the outside values. + @return the dose or if (isOutside==true && _acceptPadding==true) then _defaultValue + @exception core::MappingOutsideOfImageException if the point is mapped outside and if _acceptPadding==false, possibly returning _defaultValue) + */ + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + private: + /*! @brief returns the octant coordinates around a coordinate. + @details i.e. coordinate is the center of a virtual voxel. Then, each side is divided into equal parts. The centers of the new subvoxels are then returned. + @return a vector of the octant coordinates. + */ + std::vector getOctants(const WorldCoordinate3D& aCoordinate) const; + }; + } +} + +#endif diff --git a/code/interpolation/rttbSimpleMappableDoseAccessor.cpp b/code/interpolation/rttbSimpleMappableDoseAccessor.cpp new file mode 100644 index 0000000..776cb2c --- /dev/null +++ b/code/interpolation/rttbSimpleMappableDoseAccessor.cpp @@ -0,0 +1,100 @@ +// ----------------------------------------------------------------------- +// 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 "rttbSimpleMappableDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" + +namespace rttb +{ + namespace interpolation + { + SimpleMappableDoseAccessor::SimpleMappableDoseAccessor(const core::GeometricInfo& + geoInfoTargetImage, + const DoseAccessorPointer doseMovingImage, TransformationInterface::Pointer aTransformation, + const InterpolationBase::Pointer aInterpolation, bool acceptPadding, + double defaultOutsideValue): MappableDoseAccessorBase(geoInfoTargetImage, doseMovingImage, + aTransformation, acceptPadding, defaultOutsideValue), + _spInterpolation(aInterpolation) + { + //handle null pointers + if (aInterpolation == NULL) + { + throw core::NullPointerException("Pointer to interpolation cannot be NULL."); + } + else + { + _spInterpolation->setDoseAccessorPointer(_spOriginalDoseDataMovingImage); + } + } + + DoseTypeGy SimpleMappableDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + //Transform requested voxel coordinates of original image into world coordinates with RTTB + WorldCoordinate3D worldCoordinateTarget; + + if (_geoInfoTargetImage.indexToWorldCoordinate(aIndex, worldCoordinateTarget)) + { + //transform coordinates + WorldCoordinate3D worldCoordinateMoving; + _spTransformation->transformInverse(worldCoordinateTarget, worldCoordinateMoving); + + //Use Interpolation to compute dose at mappedImage + try + { + return _spInterpolation->getValue(worldCoordinateMoving); + } + //Mapped outside of image? Check if padding is allowed + catch (core::MappingOutsideOfImageException& /*e*/) + { + if (_acceptPadding) + { + return _defaultOutsideValue; + } + else + { + throw core::MappingOutsideOfImageException("Error in conversion from index to world coordinates"); + return -1; + } + } + catch (core::Exception& e) + { + std::cout << e.what() << std::endl; + return -1; + } + } + //ok, if that fails, throw exception. Makes no sense to go further + else + { + if (_acceptPadding) + { + return _defaultOutsideValue; + } + else + { + throw core::MappingOutsideOfImageException("Error in conversion from index to world coordinates"); + return -1; + } + } + } + + }//end namespace interpolation +}//end namespace rttb diff --git a/code/interpolation/rttbSimpleMappableDoseAccessor.h b/code/interpolation/rttbSimpleMappableDoseAccessor.h new file mode 100644 index 0000000..a0f2959 --- /dev/null +++ b/code/interpolation/rttbSimpleMappableDoseAccessor.h @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// 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 __SIMPLE_MAPPABLE_DOSE_ACCESSOR_H +#define __SIMPLE_MAPPABLE_DOSE_ACCESSOR_H + +#include + +#include "rttbBaseType.h" +#include "rttbInterpolationBase.h" +#include "rttbTransformationInterface.h" +#include "rttbMappableDoseAccessorBase.h" + +namespace rttb +{ + namespace interpolation + { + + /*! @class SimpleMappableDoseAccessor + @brief Class for dose mapping based on simple trilinear interpolation + @ingroup interpolation + */ + class SimpleMappableDoseAccessor: public MappableDoseAccessorBase + { + private: + InterpolationBase::Pointer _spInterpolation; + public: + typedef boost::shared_ptr Pointer; + + /*! @brief Constructor. Just hands values over to base class constructor. + @param aInterpolation the used interpolation. + @sa MappableDoseAccessorBase + */ + SimpleMappableDoseAccessor(const core::GeometricInfo& geoInfoTargetImage, + const DoseAccessorPointer doseMovingImage, + const TransformationInterface::Pointer aTransformation, + const InterpolationBase::Pointer aInterpolation, + bool acceptPadding = true, + DoseTypeGy defaultOutsideValue = 0.0); + + /*! @brief Virtual destructor of class + */ + virtual ~SimpleMappableDoseAccessor() {}; + + /*! @brief Returns the dose for a given voxel grid index. Plain trilinear interpolation is performed. + @return the dose or if (isOutside==true && _acceptPadding==true) then _defaultValue + @exception core::MappingOutsideOfImageException if the point is mapped outside and if _acceptPadding==false, possibly returning _defaultValue) + */ + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + }; + } +} + +#endif diff --git a/code/interpolation/rttbTransformationInterface.h b/code/interpolation/rttbTransformationInterface.h new file mode 100644 index 0000000..4be7243 --- /dev/null +++ b/code/interpolation/rttbTransformationInterface.h @@ -0,0 +1,71 @@ +// ----------------------------------------------------------------------- +// 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 __TRANSFORMATION_INTERFACE_H +#define __TRANSFORMATION_INTERFACE_H + +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbBaseType.h" + +namespace rttb +{ + namespace interpolation + { + + /*! @class TransformationInterface + @brief Base class for transformation (in World coordinates). + @ingroup interpolation + */ + class TransformationInterface + { + public: + typedef boost::shared_ptr Pointer; + protected: + /*! @brief Constructor + */ + TransformationInterface() {}; + + /*! @brief Virtual destructor of interface class + */ + virtual ~TransformationInterface() {}; + public: + /*! @brief performs a transformation targetImage --> movingImage + */ + virtual bool transformInverse(const WorldCoordinate3D& worldCoordinateTarget, + WorldCoordinate3D& worldCoordinateMoving) const = 0; + + /*! @brief performs a transformation movingImage --> targetImage + */ + virtual bool transform(const WorldCoordinate3D& worldCoordinateMoving, + WorldCoordinate3D& worldCoordinateTarget) const = 0; + + private: + TransformationInterface(const TransformationInterface&);//not implemented on purpose -> non-copyable + TransformationInterface& operator=(const + TransformationInterface&);//not implemented on purpose -> non-copyable + }; + + } +} + +#endif diff --git a/code/io/CMakeLists.txt b/code/io/CMakeLists.txt new file mode 100644 index 0000000..917cfcb --- /dev/null +++ b/code/io/CMakeLists.txt @@ -0,0 +1,21 @@ +MESSAGE (STATUS "processing RTToolbox io") +ADD_SUBDIRECTORY (other) + +OPTION(BUILD_IO_Dicom "Determine if the IO class wrappers for DICOM format will be generated." OFF) +IF(BUILD_IO_Dicom) + ADD_SUBDIRECTORY(dicom) + OPTION(BUILD_IO_HELAX "Determine if the IO class wrappers for HELAX format will be generated." OFF) + IF(BUILD_IO_HELAX) + ADD_SUBDIRECTORY(helax) + ENDIF(BUILD_IO_HELAX) +ENDIF(BUILD_IO_Dicom) + +OPTION(BUILD_IO_ITK "Determine if the IO class wrappers for ITK image formats will be generated." OFF) +IF(BUILD_IO_ITK) + ADD_SUBDIRECTORY(itk) +ENDIF(BUILD_IO_ITK) + +OPTION(BUILD_IO_Virtuos "Determine if the IO class wrappers for Virtuos format will be generated." OFF) +IF(BUILD_IO_Virtuos) + ADD_SUBDIRECTORY(virtuos) +ENDIF(BUILD_IO_Virtuos) diff --git a/code/io/dicom/CMakeLists.txt b/code/io/dicom/CMakeLists.txt new file mode 100644 index 0000000..76e5b05 --- /dev/null +++ b/code/io/dicom/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBDicomIO DEPENDS RTTBCore PACKAGE_DEPENDS Boost DCMTK) \ No newline at end of file diff --git a/code/io/dicom/files.cmake b/code/io/dicom/files.cmake new file mode 100644 index 0000000..8f2e3d8 --- /dev/null +++ b/code/io/dicom/files.cmake @@ -0,0 +1,19 @@ +SET(CPP_FILES + rttbDcmrtException.cpp + rttbDicomDoseAccessor.cpp + rttbDicomFileDoseAccessorGenerator.cpp + rttbDicomFileStructureSetGenerator.cpp + rttbDicomIODDoseAccessorGenerator.cpp + rttbDicomIODStructureSetGenerator.cpp + rttbDVHDicomFileReader.cpp + ) + +SET(H_FILES + rttbDcmrtException.h + rttbDicomDoseAccessor.h + rttbDicomFileDoseAccessorGenerator.h + rttbDicomFileStructureSetGenerator.h + rttbDicomIODDoseAccessorGenerator.h + rttbDicomIODStructureSetGenerator.h + rttbDVHDicomFileReader.h +) diff --git a/code/io/dicom/rttbDVHDicomFileReader.cpp b/code/io/dicom/rttbDVHDicomFileReader.cpp new file mode 100644 index 0000000..f08292c --- /dev/null +++ b/code/io/dicom/rttbDVHDicomFileReader.cpp @@ -0,0 +1,50 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDVHDicomFileReader.h" +#include "rttbException.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + DVHDicomFileReader::DVHDicomFileReader(FileNameString aFileName){ + this->setFileName(aFileName); + this->createDVH(); + } + + void DVHDicomFileReader::setFileName(FileNameString aFileName){ + _fileName=aFileName; + this->createDVH(); + } + + void DVHDicomFileReader::createDVH() + { + /**@TODO Implementation needed*/ + assert(false); + throw rttb::core::Exception("DICOM DVH reader currently not implemented."); + }; + + } + } +} + diff --git a/code/io/dicom/rttbDVHDicomFileReader.h b/code/io/dicom/rttbDVHDicomFileReader.h new file mode 100644 index 0000000..17f77c1 --- /dev/null +++ b/code/io/dicom/rttbDVHDicomFileReader.h @@ -0,0 +1,56 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_DICOM_FILE_READER_H +#define __DVH_DICOM_FILE_READER_H + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbDVHGeneratorInterface.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + + /*! @class DVHDicomFileReader + @brief Read DVH data from a dicom file and create corresponding DVH object. + */ + class DVHDicomFileReader: public core::DVHGeneratorInterface + { + private: + FileNameString _fileName; + void createDVH(); + + public: + /*! @brief DVHDicomFileReader Constructor + @param aFileName the dicom dvh file name + */ + DVHDicomFileReader(FileNameString aFileName); + + /*! @brief Set the dicom dvh file name (triggers data import) + @param aFileName the dicom dvh file name + */ + void setFileName(FileNameString aFileName); + }; + } + } +} + +#endif diff --git a/code/io/dicom/rttbDcmrtException.cpp b/code/io/dicom/rttbDcmrtException.cpp new file mode 100644 index 0000000..ac1a2f2 --- /dev/null +++ b/code/io/dicom/rttbDcmrtException.cpp @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDcmrtException.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + const char* DcmrtException::what() const throw() { + return rttb_what.c_str(); + } + + const char* DcmrtException::GetNameOfClass() const{ + return "DcmrtException"; + } + } + + }//end namespace io +}//end namespace rttb diff --git a/code/io/dicom/rttbDcmrtException.h b/code/io/dicom/rttbDcmrtException.h new file mode 100644 index 0000000..ab325ed --- /dev/null +++ b/code/io/dicom/rttbDcmrtException.h @@ -0,0 +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) +*/ + +#ifndef __DCMRT_EXCEPTION_H +#define __DCMRT_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + + /*! @class DcmrtException + @brief This class represents a DcmrtException. Any dcmrt error will throw this exception. + */ + class DcmrtException: public core::Exception + { + public: + DcmrtException(const std::string& aWhat):Exception(aWhat){} + + virtual ~DcmrtException() throw() {} + + /*! @brief Get the exception description + */ + const char * what() const throw(); + + /*! @brief Get the name of the class that was thrown + */ + const char* GetNameOfClass() const; + }; + } + } + +} + +#endif diff --git a/code/io/dicom/rttbDicomDoseAccessor.cpp b/code/io/dicom/rttbDicomDoseAccessor.cpp new file mode 100644 index 0000000..ac9fda7 --- /dev/null +++ b/code/io/dicom/rttbDicomDoseAccessor.cpp @@ -0,0 +1,296 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDicomDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbIndexOutOfBoundsException.h" + + + +namespace rttb +{ + namespace io + { + namespace dicom + { + + DicomDoseAccessor::~DicomDoseAccessor() + { + + } + + DicomDoseAccessor::DicomDoseAccessor(DRTDoseIODPtr aDRTDoseIODP) + { + + _dose = aDRTDoseIODP; + + OFString uid; + _dose->getSeriesInstanceUID(uid); + _doseUID = uid.c_str(); + + this->begin(); + } + + bool DicomDoseAccessor::begin() + { + + assembleGeometricInfo(); + + doseData.clear(); + + OFString doseGridScalingStr; + this->_dose->getDoseGridScaling(doseGridScalingStr); + + try + { + _doseGridScaling = boost::lexical_cast(doseGridScalingStr.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 i = 0; i < (unsigned int)this->_geoInfo.getNumberOfVoxels(); i++) + { + this->doseData.push_back(pixelData[i]); + } + + return true; + } + else + { + throw io::dicom::DcmrtException("Read Pixel Data (7FE0,0010) failed!"); + } + } + else + { + throw io::dicom::DcmrtException("Read DICOM-RT Dose file failed!"); + } + + + } + + bool DicomDoseAccessor::assembleGeometricInfo() + { + + Uint16 temp = 0; + this->_dose->getColumns(temp); + _geoInfo.setNumColumns(temp); + + temp = 0; + this->_dose->getRows(temp); + _geoInfo.setNumRows(temp); + + if (_geoInfo.getNumColumns() == 0 || _geoInfo.getNumRows() == 0) + { + throw core::InvalidDoseException("Empty dicom dose!") ; + } + + OFString numberOfFramesStr; + OFString imageOrientationRowX, imageOrientationRowY, imageOrientationRowZ; + OFString imageOrientationColumnX, imageOrientationColumnY, imageOrientationColumnZ; + WorldCoordinate3D imageOrientationRow; + WorldCoordinate3D imageOrientationColumn; + + try + { + this->_dose->getNumberOfFrames(numberOfFramesStr); + _geoInfo.setNumSlices(boost::lexical_cast(numberOfFramesStr.c_str())); + + + _dose->getImageOrientationPatient(imageOrientationRowX, 0); + _dose->getImageOrientationPatient(imageOrientationRowY, 1); + _dose->getImageOrientationPatient(imageOrientationRowZ, 2); + _dose->getImageOrientationPatient(imageOrientationColumnX, 3); + _dose->getImageOrientationPatient(imageOrientationColumnY, 4); + _dose->getImageOrientationPatient(imageOrientationColumnZ, 5); + + 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 failed! Empty dicom dose!") ; + } + + /*Get orientation*/ + 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, imagePositionY, imagePositionZ; + _dose->getImagePositionPatient(imagePositionX, 0); + _dose->getImagePositionPatient(imagePositionY, 1); + _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("Can not read image position X/Y/Z!") ; + } + + _geoInfo.setImagePositionPatient(imagePositionPatient); + + /*Get spacing*/ + SpacingVectorType3D spacingVector; + OFString pixelSpacingRowStr, pixelSpacingColumnStr, sliceThicknessStr; + _dose->getPixelSpacing(pixelSpacingRowStr, 0); + _dose->getPixelSpacing(pixelSpacingColumnStr, 1); + + try + { + spacingVector(0) = boost::lexical_cast(pixelSpacingRowStr.c_str()); + spacingVector(1) = 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 = 0!"); + } + + _dose->getSliceThickness(sliceThicknessStr); + + try + { + spacingVector(2) = boost::lexical_cast(sliceThicknessStr.c_str()); + } + catch (boost::bad_lexical_cast&) + { + spacingVector(2) = 0 ; + } + + if (spacingVector(2) == 0) + { + OFVector gridFrameOffsetVector; + _dose->getGridFrameOffsetVector(gridFrameOffsetVector); + if(gridFrameOffsetVector.size() >= 2){ + spacingVector(2) = gridFrameOffsetVector.at(1) - gridFrameOffsetVector.at(0); //read slice thickness from GridFrameOffsetVector (3004,000c) + } + + if(spacingVector(2) == 0){ + OFCondition status; + DcmItem doseitem; + OFString pixelSpacingBetweenSlices; + + status = _dose->write(doseitem); + + if (status.good()) + { + + status = doseitem.findAndGetOFString(DcmTagKey(0x0018, 0x0088), pixelSpacingBetweenSlices); + try + { + spacingVector(2) = boost::lexical_cast(pixelSpacingBetweenSlices.c_str());//read slice thickness from PixelSpacingBetweenSlices (0018,0088) + } + catch (boost::bad_lexical_cast&) + { + spacingVector(2) = 0 ; + } + } + + + //if no useful tags to compute slicing -> set slice thickness to spacingVector(0) + if(spacingVector(2) == 0 ){ + std::cerr << "sliceThickness == 0! It wird be replaced with pixelSpacingRow=" << + _geoInfo.getPixelSpacingRow() + << "!" << std::endl; + spacingVector(2) = spacingVector(0); + } + + + } + } + + _geoInfo.setSpacing(spacingVector); + return true; + } + + DoseTypeGy DicomDoseAccessor::getDoseAt(const VoxelGridID aID) const + { + return doseData.at(aID) * _doseGridScaling; + } + + DoseTypeGy DicomDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + VoxelGridID aVoxelGridID; + + if (_geoInfo.convert(aIndex, aVoxelGridID)) + { + return getDoseAt(aVoxelGridID); + } + else + { + return -1; + } + } + + } + } +} diff --git a/code/io/dicom/rttbDicomDoseAccessor.h b/code/io/dicom/rttbDicomDoseAccessor.h new file mode 100644 index 0000000..68f04f5 --- /dev/null +++ b/code/io/dicom/rttbDicomDoseAccessor.h @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_DOSE_ACCESSOR_H +#define __DICOM_DOSE_ACCESSOR_H + +#include +#include +#include + +#include + +#include "osconfig.h" /* make sure OS specific configuration is included first */ +#include "drtdose.h" + +#include "rttbDoseAccessorInterface.h" +#include "rttbBaseType.h" + + +namespace rttb +{ + namespace io + { + namespace dicom + { + + /*! @class DicomDoseAccessor + @brief This class gives access to dose information from DRTDoseIOD + */ + class DicomDoseAccessor: public core::DoseAccessorInterface + { + public: + typedef boost::shared_ptr DRTDoseIODPtr; + + private: + DRTDoseIODPtr _dose; + + /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ + std::vector doseData; + + double _doseGridScaling; + + IDType _doseUID; + + DicomDoseAccessor(); + + protected: + /*! @brief Initialize dose data + @exception InvalidDoseException Thrown if _dose is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. + */ + bool begin(); + + /*! @brief get all required data from dicom information contained in _dose + @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. + */ + bool assembleGeometricInfo(); + + + public: + ~DicomDoseAccessor(); + + /*! @brief Constructor. Initialisation with a boost::shared_ptr of DRTDoseIOD + @exception DcmrtException Throw if dcmrt error + */ + DicomDoseAccessor(DRTDoseIODPtr aDRTDoseIODP); + + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + const IDType getDoseUID() const + { + return _doseUID; + }; + }; + } + } +} + +#endif diff --git a/code/io/dicom/rttbDicomFileDoseAccessorGenerator.cpp b/code/io/dicom/rttbDicomFileDoseAccessorGenerator.cpp new file mode 100644 index 0000000..152ac4f --- /dev/null +++ b/code/io/dicom/rttbDicomFileDoseAccessorGenerator.cpp @@ -0,0 +1,74 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbIndexOutOfBoundsException.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + + DicomFileDoseAccessorGenerator::~DicomFileDoseAccessorGenerator(){} + + + DicomFileDoseAccessorGenerator::DicomFileDoseAccessorGenerator(FileNameType aDICOMRTDoseFileName){ + _dicomDoseFileName=aDICOMRTDoseFileName; + + } + + core::DoseAccessorGeneratorInterface::DoseAccessorPointer DicomFileDoseAccessorGenerator::generateDoseAccessor() { + + OFCondition status; + + DcmFileFormat fileformat; + + DRTDoseIODPtr dose= boost::make_shared(); + status = fileformat.loadFile(_dicomDoseFileName.c_str()); + if (!status.good()) + { + std::cerr << "Error: load rtdose loadFile("<<_dicomDoseFileName.c_str()<<") failed!"<read(*fileformat.getDataset()); + if(!status.good()) + { + std::cerr << "Error: read DRTDoseIOD failed!"<(dose); + return _doseAccessor; + + + } + + + + } + } +} diff --git a/code/io/dicom/rttbDicomFileDoseAccessorGenerator.h b/code/io/dicom/rttbDicomFileDoseAccessorGenerator.h new file mode 100644 index 0000000..e13f954 --- /dev/null +++ b/code/io/dicom/rttbDicomFileDoseAccessorGenerator.h @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_FILE_DOSE_ACCESSOR_GENERATOR_H +#define __DICOM_FILE_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "osconfig.h" /* make sure OS specific configuration is included first */ +#include "drtdose.h" + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbBaseType.h" + + +namespace rttb{ + namespace io{ + namespace dicom{ + + /*! @class DicomFileDoseAccessorGenerator + @brief Load dose data from dicom file and generate DicomDoseAccessor. + */ + class DicomFileDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef boost::shared_ptr DRTDoseIODPtr; + + private: + FileNameType _dicomDoseFileName; + + DicomFileDoseAccessorGenerator(); + + + protected: + + + public: + ~DicomFileDoseAccessorGenerator(); + + /*! @brief Constructor. Initialisation with a DICOM-RT dose file with name aDICOMRTDoseFileName + + */ + DicomFileDoseAccessorGenerator(FileNameType aDICOMRTDoseFileName); + + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @exception InvalidDoseException Thrown if the loaded dose is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + */ + DoseAccessorPointer generateDoseAccessor() ; + + }; + } + } +} + +#endif diff --git a/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp b/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp new file mode 100644 index 0000000..c6849c9 --- /dev/null +++ b/code/io/dicom/rttbDicomFileStructureSetGenerator.cpp @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// 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 "rttbInvalidParameterException.h" +#include "rttbStructure.h" + +#include "rttbDicomFileStructureSetGenerator.h" +#include "rttbDicomIODStructureSetGenerator.h" +#include "rttbDcmrtException.h" + + +namespace rttb{ + namespace io{ + namespace dicom{ + + + DicomFileStructureSetGenerator::DicomFileStructureSetGenerator(DICOMRTFileNameString aDICOMRTStrSetFileName){ + + _fileName=aDICOMRTStrSetFileName; + + } + + + DicomFileStructureSetGenerator::~DicomFileStructureSetGenerator() + { + + } + + DicomFileStructureSetGenerator::StructureSetPointer DicomFileStructureSetGenerator::generateStructureSet(){ + OFCondition status; + + DcmFileFormat fileformat; + DRTStrSetIODPtr drtStrSetIODPtr=boost::make_shared(); + + status = fileformat.loadFile(_fileName.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(); + } + + }//end namespace dicom + }//end namespace io +}//end namespace rttb diff --git a/code/io/dicom/rttbDicomFileStructureSetGenerator.h b/code/io/dicom/rttbDicomFileStructureSetGenerator.h new file mode 100644 index 0000000..6d7ae61 --- /dev/null +++ b/code/io/dicom/rttbDicomFileStructureSetGenerator.h @@ -0,0 +1,82 @@ +// ----------------------------------------------------------------------- +// 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) +*/ + +/* Changes in Architecture: +The DICOM specific classes will be removed and transfered to the corresponding IO classes. +This class should only provide general structure functionality. +*/ + +#ifndef __DICOM_FILE_STRUCTURE_SET_GENERATOR_H +#define __DICOM_FILE_STRUCTURE_SET_GENERATOR_H + + +#include +#include + +#include "drtstrct.h" + +#include "rttbBaseType.h" +#include "rttbStrVectorStructureSetGenerator.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + + /*! @class DicomFileStructureSetGenerator + @brief Generate a structure set from a corresponding dicomRT file. + */ + class DicomFileStructureSetGenerator: public core::StrVectorStructureSetGenerator + { + public: + typedef core::StructureSet::StructTypePointer StructTypePointer; + typedef StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + typedef boost::shared_ptr DRTStrSetIODPtr; + + private: + IDType _UID; + DICOMRTFileNameString _fileName; + + DicomFileStructureSetGenerator(){}; + + public: + + /*! @brief Constructor + @param aDICOMRTStrSetFileName a DICOM-RT Structure set file name + + */ + DicomFileStructureSetGenerator(DICOMRTFileNameString aDICOMRTStrSetFileName); + + /*! @brief Destructor + */ + ~DicomFileStructureSetGenerator(); + + /*! @brief generate structure set + @return return shared pointer of StructureSet + @exception DcmrtException Thrown if loadFile and read failed + @exception InvalidParameterException throw if the imported header tags are not numerical. + */ + StructureSetPointer generateStructureSet(); + }; + } + } +} + +#endif diff --git a/code/io/dicom/rttbDicomIODDoseAccessorGenerator.cpp b/code/io/dicom/rttbDicomIODDoseAccessorGenerator.cpp new file mode 100644 index 0000000..e8fe27f --- /dev/null +++ b/code/io/dicom/rttbDicomIODDoseAccessorGenerator.cpp @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDicomIODDoseAccessorGenerator.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbIndexOutOfBoundsException.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + + + DicomIODDoseAccessorGenerator::~DicomIODDoseAccessorGenerator(){} + + DicomIODDoseAccessorGenerator::DicomIODDoseAccessorGenerator(DRTDoseIODPtr aDRTDoseIODP){ + _doseIODPtr=aDRTDoseIODP; + + } + + core::DoseAccessorGeneratorInterface::DoseAccessorPointer DicomIODDoseAccessorGenerator::generateDoseAccessor() { + _doseAccessor=boost::make_shared(_doseIODPtr); + return _doseAccessor; + } + + + } + } +} diff --git a/code/io/dicom/rttbDicomIODDoseAccessorGenerator.h b/code/io/dicom/rttbDicomIODDoseAccessorGenerator.h new file mode 100644 index 0000000..30e209d --- /dev/null +++ b/code/io/dicom/rttbDicomIODDoseAccessorGenerator.h @@ -0,0 +1,77 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_IOD_DOSE_ACCESSOR_GENERATOR_H +#define __DICOM_IOD_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "osconfig.h" /* make sure OS specific configuration is included first */ +#include "drtdose.h" + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbBaseType.h" + + +namespace rttb{ + namespace io{ + namespace dicom{ + + /*! @class DicomIODDoseAccessorGenerator + @brief Generate DicomDoseAccessor with a DRTDoseIOD. + */ + class DicomIODDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef boost::shared_ptr DRTDoseIODPtr; + + + + protected: + + private: + DRTDoseIODPtr _doseIODPtr; + + DicomIODDoseAccessorGenerator(); + + + public: + ~DicomIODDoseAccessorGenerator(); + + /*! @brief Constructor. Initialisation with a boost shared pointer of DRTDoseIOD + + */ + DicomIODDoseAccessorGenerator(DRTDoseIODPtr aDRTDoseIODP); + + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @exception InvalidDoseException Thrown if aDRTDoseIODP is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + */ + DoseAccessorPointer generateDoseAccessor() ; + + }; + } + } +} + +#endif diff --git a/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp b/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp new file mode 100644 index 0000000..2fd28bf --- /dev/null +++ b/code/io/dicom/rttbDicomIODStructureSetGenerator.cpp @@ -0,0 +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 +#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(); + + /*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); + DRTContourSequence *cs; + cs=&rcsItem->getContourSequence(); + long no2=cs->getNumberOfItems(); + + PolygonSequenceType structureVector; + + for(int 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); + + int numberOfContourPointsInt; + std::stringstream is(numberOfContourPoints.c_str()); + is >> numberOfContourPointsInt; + OFString countourNumber; + csItem->getContourNumber(countourNumber); + + PolygonType contourVector; + char* pEnd; + for(int k=0; kgetContourData(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;igetNumberOfItems();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()<setUID(sstr.str()); + + _strVector.push_back(str); + 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/dicom/rttbDicomIODStructureSetGenerator.h b/code/io/dicom/rttbDicomIODStructureSetGenerator.h new file mode 100644 index 0000000..ffa7449 --- /dev/null +++ b/code/io/dicom/rttbDicomIODStructureSetGenerator.h @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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) +*/ + +/* Changes in Architecture: +The DICOM specific classes will be removed and transfered to the corresponding IO classes. +This class should only provide general structure functionality. +*/ + +#ifndef __DICOM_IOD_STRUCTURE_SET_GENERATOR_H +#define __DICOM_IOD_STRUCTURE_SET_GENERATOR_H + +#include + +#include +#include + +#include "drtstrct.h" + +#include "rttbBaseType.h" +#include "rttbStrVectorStructureSetGenerator.h" +#include "rttbStructure.h" + + +namespace rttb{ + namespace io{ + namespace dicom{ + + /*! @class DicomIODStructureSetGenerator + @brief Generate a structure set from a DRTStructureSetIOD pointer. + */ + class DicomIODStructureSetGenerator: public core::StrVectorStructureSetGenerator + { + public: + typedef core::StructureSet::StructTypePointer StructTypePointer; + typedef StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + typedef boost::shared_ptr DRTStrSetIODPtr; + + private: + DRTStrSetIODPtr _drtStrSetIOD; + IDType _UID; + + + + /*! Import Structure data from file. + @exception InvalidParameterException Thrown if the imported header tags are not numerical. + */ + void readStrSet(); + + public: + + /*! @brief Structure Constructor + Get the vector of structures from DRTStructureSetIOD object + @exception NullPointerException Thrown if structureSet is NULL + */ + DicomIODStructureSetGenerator(DRTStrSetIODPtr aDRTStructureSetIOD); + + /*! @brief Destructor + */ + ~DicomIODStructureSetGenerator(); + + /*! @brief generate structure set + @return return shared pointer of StructureSet + @exception InvalidParameterException throw if the imported header tags are not numerical. + */ + StructureSetPointer generateStructureSet(); + + }; + } + } +} + +#endif diff --git a/code/io/helax/CMakeLists.txt b/code/io/helax/CMakeLists.txt new file mode 100644 index 0000000..cc0cd7e --- /dev/null +++ b/code/io/helax/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBHelaxIO DEPENDS RTTBCore RTTBDicomIO PACKAGE_DEPENDS BoostBinaries DCMTK) \ No newline at end of file diff --git a/code/io/helax/files.cmake b/code/io/helax/files.cmake new file mode 100644 index 0000000..8dfb4aa --- /dev/null +++ b/code/io/helax/files.cmake @@ -0,0 +1,11 @@ +SET(CPP_FILES + rttbDicomHelaxDoseAccessor.cpp + rttbDicomHelaxFileDoseAccessorGenerator.cpp + rttbDicomHelaxIODVecDoseAccessorGenerator.cpp + ) + +SET(H_FILES + rttbDicomHelaxDoseAccessor.h + rttbDicomHelaxFileDoseAccessorGenerator.h + rttbDicomHelaxIODVecDoseAccessorGenerator.h + ) diff --git a/code/io/helax/rttbDicomHelaxDoseAccessor.cpp b/code/io/helax/rttbDicomHelaxDoseAccessor.cpp new file mode 100644 index 0000000..c7f3d1c --- /dev/null +++ b/code/io/helax/rttbDicomHelaxDoseAccessor.cpp @@ -0,0 +1,316 @@ +// ----------------------------------------------------------------------- +// 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 (int 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; + char* pEnd; + _doseVector.at(0)->getDoseGridScaling( + doseGridScalingStr);//get the first dose grid scaling as _doseGridScaling + _doseGridScaling = strtod(doseGridScalingStr.c_str(), &pEnd); + + if (*pEnd != '\0' || _doseGridScaling == 0) + { + throw core::InvalidDoseException("Dose grid scaling not readable or = 0!") ; + } + + for (int i = 0; i < _doseVector.size(); i++) + { + DRTDoseIODPtr dose = _doseVector.at(i); + + OFString currentDoseGridScalingStr; + dose->getDoseGridScaling(currentDoseGridScalingStr); + double currentDoseGridScaling = strtod(currentDoseGridScalingStr.c_str(), &pEnd); + + if (*pEnd != '\0' || currentDoseGridScaling == 0) + { + 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 i = 0; i < unsigned int(_geoInfo.getNumColumns()*_geoInfo.getNumRows()); i++) + { + this->_doseData.push_back(pixelData[i]*currentDoseGridScaling / + _doseGridScaling); //recalculate dose data + } + + } + else + { + throw dicom::DcmrtException("Read Pixel Data (7FE0,0010) failed!"); + } + } + else + { + throw dicom::DcmrtException("Read DICOM-RT Dose file failed!"); + } + } + + //std::cout << _doseData.size()<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; + imageOrientationRow(0) = boost::lexical_cast(imageOrientationRowX.c_str()); + imageOrientationRow(1) = boost::lexical_cast(imageOrientationRowY.c_str()); + imageOrientationRow(2) = boost::lexical_cast(imageOrientationRowZ.c_str()); + WorldCoordinate3D imageOrientationColumn; + imageOrientationColumn(0) = boost::lexical_cast(imageOrientationColumnX.c_str()); + imageOrientationColumn(1) = boost::lexical_cast(imageOrientationColumnY.c_str()); + imageOrientationColumn(2) = boost::lexical_cast(imageOrientationColumnZ.c_str()); + + 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; + char* pEnd; + imagePositionPatient(0) = strtod(imagePositionX.c_str(), &pEnd); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read image position X!"); + } + + imagePositionPatient(1) = strtod(imagePositionY.c_str(), &pEnd); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read image position Y!"); + } + + imagePositionPatient(2) = strtod(imagePositionZ.c_str(), &pEnd); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read image position Z!"); + } + + _geoInfo.setImagePositionPatient(imagePositionPatient); + + SpacingVectorType3D spacingVector; + OFString pixelSpacingRowStr; + dose->getPixelSpacing(pixelSpacingRowStr, 0); + spacingVector(0) = strtod(pixelSpacingRowStr.c_str(), &pEnd); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read Pixel Spacing Row!"); + } + + OFString pixelSpacingColumnStr; + dose->getPixelSpacing(pixelSpacingColumnStr, 1); + spacingVector(1) = strtod(pixelSpacingColumnStr.c_str(), &pEnd); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read Pixel Spacing 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); + spacingVector(2) = strtod(sliceThicknessStr.c_str(), &pEnd); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read slice thickness!"); + } + + if (spacingVector(2) == 0) + { + if (_doseVector.size() > 1) + { + DRTDoseIODPtr dose2 = _doseVector.at(1); + OFString imagePositionZ2; + dose2->getImagePositionPatient(imagePositionZ2, 2); + spacingVector(2) = strtod(imagePositionZ2.c_str(), &pEnd) - imagePositionPatient(2); + + if (*pEnd != '\0') + { + throw core::InvalidDoseException("Can not read image position Z!"); + } + } + else + { + std::cerr << "sliceThickness == 0! It will be replaced with pixelSpacingRow=" << + _geoInfo.getPixelSpacingRow() + << "!" << std::endl; + spacingVector(2) = spacingVector(0); + } + } + + _geoInfo.setSpacing(spacingVector); + return true; + } + + DoseTypeGy DicomHelaxDoseAccessor::getDoseAt(const VoxelGridID aID) const + { + return _doseData.at(aID) * _doseGridScaling; + } + + DoseTypeGy DicomHelaxDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + VoxelGridID aVoxelGridID; + + if (_geoInfo.convert(aIndex, aVoxelGridID)) + { + return getDoseAt(aVoxelGridID); + } + else + { + return -1; + } + } + + } + } + +} diff --git a/code/io/helax/rttbDicomHelaxDoseAccessor.h b/code/io/helax/rttbDicomHelaxDoseAccessor.h new file mode 100644 index 0000000..c08c2f8 --- /dev/null +++ b/code/io/helax/rttbDicomHelaxDoseAccessor.h @@ -0,0 +1,105 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_HELAX_DOSE_ACCESSOR_H +#define __DICOM_HELAX_DOSE_ACCESSOR_H + +#include +#include +#include + +#include + +#include "osconfig.h" /* make sure OS specific configuration is included first */ +#include "drtdose.h" + +#include "rttbDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + + +namespace rttb +{ + namespace io + { + namespace helax + { + + /*! @class DicomHelaxDoseAccessor + @brief Load dose data from a directory containing dicom dose files, each file describes the helax dose in one slice. + */ + class DicomHelaxDoseAccessor: public core::DoseAccessorInterface + { + public: + typedef boost::shared_ptr DRTDoseIODPtr; + + private: + /*! vector of DRTDoseIOD shared pointers, each DRTDoseIOD pointer presents the dose in one slice*/ + std::vector _doseVector; + + /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ + std::vector _doseData; + + double _doseGridScaling; + + IDType _doseUID; + + DicomHelaxDoseAccessor(); + + protected: + /*! @brief Initialize dose data + @exception InvalidDoseException Thrown if any DRTDoseIOD pointer of _doseVector is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. + */ + bool begin(); + + /*! @brief get all required data from dicom information contained in _dose + @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. + */ + bool assembleGeometricInfo(); + + + public: + + ~DicomHelaxDoseAccessor(); + + + /*! @brief Constructor. Initialisation with a vector of DRTDoseIOD pointers + @exception InvalidDoseException Thrown if any DRTDoseIOD pointer of _doseVector is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + */ + DicomHelaxDoseAccessor(std::vector aDICOMRTDoseVector); + + + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + const IDType getDoseUID() const + { + return _doseUID; + }; + }; + } + } +} + +#endif diff --git a/code/io/helax/rttbDicomHelaxFileDoseAccessorGenerator.cpp b/code/io/helax/rttbDicomHelaxFileDoseAccessorGenerator.cpp new file mode 100644 index 0000000..67235a2 --- /dev/null +++ b/code/io/helax/rttbDicomHelaxFileDoseAccessorGenerator.cpp @@ -0,0 +1,133 @@ +// ----------------------------------------------------------------------- +// 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 "boost/filesystem/operations.hpp" +#include "boost/filesystem/path.hpp" +#include "boost/progress.hpp" + +#include "rttbDicomHelaxFileDoseAccessorGenerator.h" +#include "rttbDicomHelaxDoseAccessor.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbIndexOutOfBoundsException.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace io{ + namespace helax{ + DicomHelaxFileDoseAccessorGenerator::~DicomHelaxFileDoseAccessorGenerator(){} + + DicomHelaxFileDoseAccessorGenerator::DicomHelaxFileDoseAccessorGenerator(FileNameType aDICOMRTDoseDirName){ + _doseDirName=aDICOMRTDoseDirName; + + + } + + core::DoseAccessorGeneratorInterface::DoseAccessorPointer DicomHelaxFileDoseAccessorGenerator::generateDoseAccessor() { + boost::filesystem::path path=boost::filesystem::path(_doseDirName); + + OFCondition status; + + DcmFileFormat fileformat; + + std::vector doseVector; + IDType doseUID; + + int file_count=0; + if(!boost::filesystem::exists(path) || !boost::filesystem::is_directory(path)){ + throw core::InvalidParameterException("Directory not found!"); + } + else{ + boost::filesystem::directory_iterator end_iter; + bool isFirstDose=true; + + for(boost::filesystem::directory_iterator dir_itr(path);dir_itr!=end_iter;++dir_itr) + { + if(boost::filesystem::is_regular_file(dir_itr->status())) + { + boost::filesystem::path filePath(dir_itr->path().filename().string()); + filePath=boost::filesystem::system_complete(dir_itr->path()); + + + DRTDoseIODPtr dose= boost::make_shared(); + + status = fileformat.loadFile(filePath.string().c_str()); + if (!status.good()) + { + throw core::InvalidDoseException("Error: load dose fileformat.loadFile failed!"); + } + + status = dose->read(*fileformat.getDataset()); + if(!status.good()) + { + throw core::InvalidDoseException("Error: read DRTDoseIOD failed!"); + } + + OFString modality; + status = dose->getModality(modality); + if (modality != "RTDOSE") + { + std::cout<<"Error: "<getSeriesInstanceUID(uid); + + if(file_count==1) + { + doseUID=uid.c_str(); + } + + + if(doseUID!=uid.c_str()) + { + std::cout << "There are more than 1 RT dose in the directory. The DoseAccessor returns only the first one! Loaded uid: "<< doseUID<< ";ignored UID: "<< uid <(doseVector); + return _doseAccessor; + + } + + + + + } + } +} diff --git a/code/io/helax/rttbDicomHelaxFileDoseAccessorGenerator.h b/code/io/helax/rttbDicomHelaxFileDoseAccessorGenerator.h new file mode 100644 index 0000000..43017d1 --- /dev/null +++ b/code/io/helax/rttbDicomHelaxFileDoseAccessorGenerator.h @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_HELAX_FILE_DOSE_ACCESSOR_GENERATOR_H +#define __DICOM_HELAX_FILE_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "osconfig.h" /* make sure OS specific configuration is included first */ +#include "drtdose.h" + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbBaseType.h" + + +namespace rttb{ + namespace io{ + namespace helax{ + + /*! @class DicomHelaxFileDoseAccessorGenerator + @brief Load dose data from dicom helax files and generate DoseAccessor + */ + class DicomHelaxFileDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef boost::shared_ptr DRTDoseIODPtr; + + + protected: + + private: + FileNameType _doseDirName; + + DicomHelaxFileDoseAccessorGenerator(); + + public: + ~DicomHelaxFileDoseAccessorGenerator(); + /*! @brief Constructor. Initialisation with a directory name + + */ + DicomHelaxFileDoseAccessorGenerator(FileNameType aDICOMRTDoseDirName); + + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @exception InvalidParameterException Thrown if aDICOMRTDoseDirName is not found + @exception InvalidDoseException Thrown if any loaded dose is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + */ + DoseAccessorPointer generateDoseAccessor() ; + + + }; + } + } +} + +#endif diff --git a/code/io/helax/rttbDicomHelaxIODVecDoseAccessorGenerator.cpp b/code/io/helax/rttbDicomHelaxIODVecDoseAccessorGenerator.cpp new file mode 100644 index 0000000..a008f05 --- /dev/null +++ b/code/io/helax/rttbDicomHelaxIODVecDoseAccessorGenerator.cpp @@ -0,0 +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 +#include +#include +#include "boost/filesystem/operations.hpp" +#include "boost/filesystem/path.hpp" +#include "boost/progress.hpp" + +#include "rttbDicomHelaxIODVecDoseAccessorGenerator.h" +#include "rttbDicomHelaxDoseAccessor.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbIndexOutOfBoundsException.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace io{ + namespace helax{ + + DicomHelaxIODVecDoseAccessorGenerator::~DicomHelaxIODVecDoseAccessorGenerator(){} + + DicomHelaxIODVecDoseAccessorGenerator::DicomHelaxIODVecDoseAccessorGenerator(std::vector& aDICOMRTDoseVector){ + _dosePtrVector=aDICOMRTDoseVector; + + } + + core::DoseAccessorGeneratorInterface::DoseAccessorPointer DicomHelaxIODVecDoseAccessorGenerator::generateDoseAccessor() { + _doseAccessor=boost::make_shared(_dosePtrVector); + return _doseAccessor; + } + + + + + } + } +} diff --git a/code/io/helax/rttbDicomHelaxIODVecDoseAccessorGenerator.h b/code/io/helax/rttbDicomHelaxIODVecDoseAccessorGenerator.h new file mode 100644 index 0000000..8ea5ba4 --- /dev/null +++ b/code/io/helax/rttbDicomHelaxIODVecDoseAccessorGenerator.h @@ -0,0 +1,74 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_HELAX_IOD_VEC_DOSE_ACCESSOR_GENERATOR_H +#define __DICOM_HELAX_IOD_VEC_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "osconfig.h" /* make sure OS specific configuration is included first */ +#include "drtdose.h" + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbBaseType.h" + + +namespace rttb{ + namespace io{ + namespace helax{ + + /*! @class DicomHelaxIODVecDoseAccessorGenerator + @brief Generate DoseAccessor with a vector of DRTDoseIOD. + */ + class DicomHelaxIODVecDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef boost::shared_ptr DRTDoseIODPtr; + + + protected: + private: + std::vector _dosePtrVector; + + DicomHelaxIODVecDoseAccessorGenerator(); + + public: + ~DicomHelaxIODVecDoseAccessorGenerator(); + + /*! @brief Constructor. Initialisation with a vector of DRTDoseIOD pointers + + */ + DicomHelaxIODVecDoseAccessorGenerator(std::vector& aDICOMRTDoseVector); + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @exception InvalidDoseException Thrown if any DRTDoseIOD pointer of _doseVector is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + @exception DcmrtException Throw if dcmrt error + */ + DoseAccessorPointer generateDoseAccessor() ; + + + }; + } + } +} + +#endif diff --git a/code/io/helax/rttbDicomIODDoseAccessorGenerator.cpp b/code/io/helax/rttbDicomIODDoseAccessorGenerator.cpp new file mode 100644 index 0000000..e8fe27f --- /dev/null +++ b/code/io/helax/rttbDicomIODDoseAccessorGenerator.cpp @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDicomIODDoseAccessorGenerator.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbIndexOutOfBoundsException.h" + +namespace rttb{ + namespace io{ + namespace dicom{ + + + DicomIODDoseAccessorGenerator::~DicomIODDoseAccessorGenerator(){} + + DicomIODDoseAccessorGenerator::DicomIODDoseAccessorGenerator(DRTDoseIODPtr aDRTDoseIODP){ + _doseIODPtr=aDRTDoseIODP; + + } + + core::DoseAccessorGeneratorInterface::DoseAccessorPointer DicomIODDoseAccessorGenerator::generateDoseAccessor() { + _doseAccessor=boost::make_shared(_doseIODPtr); + return _doseAccessor; + } + + + } + } +} diff --git a/code/io/itk/CMakeLists.txt b/code/io/itk/CMakeLists.txt new file mode 100644 index 0000000..576da0f --- /dev/null +++ b/code/io/itk/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBITKIO DEPENDS RTTBCore PACKAGE_DEPENDS Boost MatchPoint ITK) \ No newline at end of file diff --git a/code/io/itk/files.cmake b/code/io/itk/files.cmake new file mode 100644 index 0000000..d68beb1 --- /dev/null +++ b/code/io/itk/files.cmake @@ -0,0 +1,23 @@ +SET(CPP_FILES + rttbITKImageDoseAccessor.cpp + rttbITKImageFileDoseAccessorGenerator.cpp + rttbITKImageDoseAccessorGenerator.cpp + rttbGenericImageReader.cpp + rttbFileDispatch.cpp + rttbITKImageDoseAccessorConverter.cpp + ) + +SET(H_FILES + rttbITKImageDoseAccessor.h + rttbITKImageFileDoseAccessorGenerator.h + rttbITKImageFileDoseAccessorGenerator.tpp + rttbITKImageDoseAccessorGenerator.h + rttbGenericImageReader.h + rttbImageReader.h + rttbImageReader.tpp + rttbFileDispatch.h + rttbITKImageDoseAccessorConverter.h + rttbDoseAccessorProcessorInterface.h + rttbDoseAccessorProcessorBase.h + rttbDoseAccessorConversionSettingInterface.h +) diff --git a/code/io/itk/rttbDoseAccessorConversionSettingInterface.h b/code/io/itk/rttbDoseAccessorConversionSettingInterface.h new file mode 100644 index 0000000..1d12a3b --- /dev/null +++ b/code/io/itk/rttbDoseAccessorConversionSettingInterface.h @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// 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_CONVERSION_SETTING_INTERFACE_H +#define __DOSE_ACCESSOR_CONVERSION_SETTING_INTERFACE_H + +#include "rttbBaseType.h" + +namespace rttb{ + namespace core + { + /*! @class DoseAccessorConversionSettingInterface + @brief Interface for specifying settings for dose accessor convertors (e.g. how to handle invalid dose voxels) + */ + class DoseAccessorConversionSettingInterface + { + public: + /* Defines if the conversion process should fail (with an exception) if an invalid id/voxel occures.*/ + void setFailOnInvalidIDs(bool failOnInvalid) + { + _failedOnInvalidID = failOnInvalid; + } + + /* Indicates how the conversion should handle invalid ids/voxels.\n + * true: fails with an exception + * false: uses the specified "invalid dose value".*/ + bool failsOnInvalidIDs() const + { + return _failedOnInvalidID; + } + + /* Sets the value that should be used for invalid ids/voxels.*/ + void setInvalidDoseValue(DoseTypeGy value) + { + _invalidDoseValue = value; + } + + /* Returns the value that is used for invalid ids/voxels.*/ + DoseTypeGy getInvalidDoseValue() const + { + return _invalidDoseValue; + } + + DoseAccessorConversionSettingInterface():_failedOnInvalidID(false),_invalidDoseValue(-1.0){}; + virtual ~DoseAccessorConversionSettingInterface(){}; + + private: + DoseAccessorConversionSettingInterface(const DoseAccessorConversionSettingInterface&); //not implemented on purpose -> non-copyable + DoseAccessorConversionSettingInterface& operator=(const DoseAccessorConversionSettingInterface&);//not implemented on purpose -> non-copyable + + protected: + + bool _failedOnInvalidID; + DoseTypeGy _invalidDoseValue; + }; + } + } + +#endif diff --git a/code/io/itk/rttbDoseAccessorProcessorBase.h b/code/io/itk/rttbDoseAccessorProcessorBase.h new file mode 100644 index 0000000..9bbd057 --- /dev/null +++ b/code/io/itk/rttbDoseAccessorProcessorBase.h @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// 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_PROCESSOR_BASE_H +#define __DOSE_ACCESSOR_PROCESSOR_BASE_H + +#include + +#include "rttbDoseAccessorProcessorInterface.h" + +namespace rttb{ + namespace core + { + /*! @class DoseAccessorProcessorBase + @brief Abstract class for all DoseAccessor generating classes + */ + class DoseAccessorProcessorBase: public DoseAccessorProcessorInterface + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + virtual void setDoseAccessor(DoseAccessorPointer accessor) + { + _doseAccessor = accessor; + }; + + private: + DoseAccessorProcessorBase(const DoseAccessorProcessorBase&); //not implemented on purpose -> non-copyable + DoseAccessorProcessorBase& operator=(const DoseAccessorProcessorBase&);//not implemented on purpose -> non-copyable + + protected: + DoseAccessorProcessorBase(){}; + virtual ~DoseAccessorProcessorBase(){}; + + /*! @brief Dose accessor which should be generated */ + DoseAccessorPointer _doseAccessor; + }; + } + } + +#endif diff --git a/code/io/itk/rttbDoseAccessorProcessorInterface.h b/code/io/itk/rttbDoseAccessorProcessorInterface.h new file mode 100644 index 0000000..b7bda9e --- /dev/null +++ b/code/io/itk/rttbDoseAccessorProcessorInterface.h @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------- +// 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_PROCESSOR_INTERFACE_H +#define __DOSE_ACCESSOR_PROCESSOR_INTERFACE_H + +#include "rttbDoseAccessorInterface.h" + +namespace rttb{ + namespace core + { + /*! @class DoseAccessorProcessorInterface + @brief Interface for all DoseAccessor generating classes + */ + class DoseAccessorProcessorInterface + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + + private: + DoseAccessorProcessorInterface(const DoseAccessorProcessorInterface&); //not implemented on purpose -> non-copyable + DoseAccessorProcessorInterface& operator=(const DoseAccessorProcessorInterface&);//not implemented on purpose -> non-copyable + + + protected: + DoseAccessorProcessorInterface() {}; + virtual ~DoseAccessorProcessorInterface(){}; + + public: + + /*! @brief Sets the DoseAccessor that should be processed + @pre passed accessor must point to a valid instance. + */ + virtual void setDoseAccessor(DoseAccessorPointer accessor) = 0; + + /*! @brief Process the passed DoseAccessor + @return if the processing was successful. + */ + virtual bool process() = 0; + }; + } + } + +#endif diff --git a/code/io/itk/rttbFileDispatch.cpp b/code/io/itk/rttbFileDispatch.cpp new file mode 100644 index 0000000..1f6f83d --- /dev/null +++ b/code/io/itk/rttbFileDispatch.cpp @@ -0,0 +1,168 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/Core/source/mapFileDispatch.cpp $ +*/ + +#include "rttbFileDispatch.h" + +#include "itksys/SystemTools.hxx" + +namespace rttb +{ + namespace core + { + FileNameString + FileDispatch:: + getName(const FileNameString& sFilePath) + { + FileNameString result = itksys::SystemTools::GetFilenameWithoutLastExtension(sFilePath); + return result; + }; + + FileNameString + FileDispatch:: + getExtension(const FileNameString& sFilePath) + { + FileNameString result = itksys::SystemTools::GetFilenameLastExtension(sFilePath); + return result; + }; + + FileNameString + FileDispatch:: + getFullName(const FileNameString& sFilePath) + { + FileNameString result = itksys::SystemTools::GetFilenameName(sFilePath); + return result; + }; + + FileNameString + FileDispatch:: + getPath(const FileNameString& sFilePath) + { + FileNameString result = itksys::SystemTools::GetFilenamePath(sFilePath); + return ensureCorrectOSPathSeparator(result); + + }; + + FileNameString + FileDispatch:: + getName() + { + return getName(_fileName); + }; + + FileNameString + FileDispatch:: + getExtension() + { + return getExtension(_fileName); + }; + + FileNameString + FileDispatch:: + getFullName() + { + return getFullName(_fileName); + }; + + FileNameString + FileDispatch:: + getPath() + { + return getPath(_fileName); + }; + + FileDispatch:: + FileDispatch(const FileNameString& filePath) + { + _fileName = filePath; + }; + + /** + * A file scope helper function to concat path and file into + * a full path + */ + FileNameString + FileDispatch:: + createFullPath(const char* path, const char* file) + { + FileNameString ret; + + #ifdef _WIN32 + const char sep = '\\'; + #else + const char sep = '/'; + #endif + /** + * make sure the end of path is a separator + */ + ret = path; + ret = ensureCorrectOSPathSeparator(ret); + + if (ret.size()) + { + if (ret[ret.size() - 1] != sep) + { + ret.append(1, sep); + } + } + + ret.append(file); + return ret; + } + + FileNameString + FileDispatch:: + createFullPath(const FileNameString& path, const FileNameString& file) + { + FileNameString ret = createFullPath(path.c_str(), file.c_str()); + return ret; + } + + /** Convertes all path seperators in the seperators used in the current OS.*/ + FileNameString + FileDispatch:: + ensureCorrectOSPathSeparator(const FileNameString& path) + { + FileNameString ret = path; + + #ifdef _WIN32 + const FileNameString curSep = "\\"; + const char wrongSep = '/'; + #else + const FileNameString curSep = "/"; + const char wrongSep = '\\'; + #endif + + FileNameString::size_type pos = ret.find_first_of(wrongSep); + + while (pos != FileNameString::npos) + { + ret.replace(pos, 1, curSep); + + pos = ret.find_first_of(wrongSep); + } + + return ret; + } + + + } +} \ No newline at end of file diff --git a/code/io/itk/rttbFileDispatch.h b/code/io/itk/rttbFileDispatch.h new file mode 100644 index 0000000..4f86e24 --- /dev/null +++ b/code/io/itk/rttbFileDispatch.h @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/Core/include/mapFileDispatch.h $ +*/ + + +#ifndef __RTTB_FILE_DISPATCH_H +#define __RTTB_FILE_DISPATCH_H + +#include +#include "rttbBaseType.h" + +namespace rttb +{ + namespace core + { + /*! @class FileDispatch + @brief Convenience functions for working with files and path + @note code copied from MatchPoint, see documentation (http://sourceforge.net/projects/matchpoint/) + */ + + class FileDispatch + { + public: + + /** Returns the name of the file (without extension).*/ + static FileNameString getName(const FileNameString& sFilePath); + /** Returns the extansion of the file (dot included).*/ + static FileNameString getExtension(const FileNameString& sFilePath); + /** Returns name of the file plus extension.*/ + static FileNameString getFullName(const FileNameString& sFilePath); + /** Returns the directory the file is located in (without trailing slash). + * @remark this function always removes the last element of the path. Thus + * if you pass a path without a file, it will return the parent directory.*/ + static FileNameString getPath(const FileNameString& sFilePath); + + /** Helper function to concat path and file into + * a full path */ + static FileNameString createFullPath(const char* path, const char* file); + static FileNameString createFullPath(const FileNameString& path, const FileNameString& file); + + /** Convertes all path seperators in the seperators used in the current OS.*/ + static FileNameString ensureCorrectOSPathSeparator(const FileNameString& path); + + FileNameString getName(); + FileNameString getExtension(); + FileNameString getFullName(); + FileNameString getPath(); + + FileDispatch(const FileNameString& filePath); + + private: + FileNameString _fileName; + }; + + }//end namespace core +}//end namespace rttb + +#endif diff --git a/code/io/itk/rttbGenericImageReader.cpp b/code/io/itk/rttbGenericImageReader.cpp new file mode 100644 index 0000000..2a5ba87 --- /dev/null +++ b/code/io/itk/rttbGenericImageReader.cpp @@ -0,0 +1,305 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/IO/source/mapGenericImageReader.cpp $ +*/ + +#include "rttbGenericImageReader.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + + + void GenericImageReader::load() + { + _spImage = NULL; + + FileNameString probeFileName = this->_fileName; + + if (this->_seriesReadStyle == ImageSeriesReadStyle::Numeric) + { + typedef ::itk::NumericSeriesFileNames NamesType; + + NamesType::Pointer names = NamesType::New(); + + names->SetStartIndex(1); + names->SetEndIndex(1); + names->SetSeriesFormat(this->_fileName.c_str()); + + probeFileName = names->GetFileNames()[0]; + } + + ::itk::ImageIOBase::Pointer imageIO = + ::itk::ImageIOFactory::CreateImageIO(probeFileName.c_str(), + ::itk::ImageIOFactory::ReadMode); + + if (!imageIO) + { + throw ::itk::ExceptionObject("No ImageIO found for given file. Please check if the file exists and has a supported format. File:" + + probeFileName); + } + + // Now that we found the appropriate ImageIO class, ask it to + // read the meta data from the image file. + imageIO->SetFileName(probeFileName.c_str()); + imageIO->ReadImageInformation(); + + this->_loadedComponentType = imageIO->GetComponentType(); + this->_loadedPixelType = imageIO->GetPixelType(); + + if (this->_loadedPixelType == ::itk::ImageIOBase::RGB && imageIO->GetNumberOfComponents() == 1) + { + //if only one channel per pixel handle as scalar as long as RGB etc. is not supported + this->_loadedPixelType = ::itk::ImageIOBase::SCALAR; + } + + this->_loadedComponentTypeStr = imageIO->GetComponentTypeAsString(this->_loadedComponentType); + this->_loadedPixelTypeStr = imageIO->GetPixelTypeAsString(this->_loadedPixelType); + this->_loadedDimensions = imageIO->GetNumberOfDimensions(); + + if (this->_seriesReadStyle == ImageSeriesReadStyle::Numeric && + this->_loadedDimensions == 2) + { + this->_loadedDimensions = 3; //it is a stack of 2D images -> 3D + } + + if (this->_loadedDimensions < 2 || this->_loadedDimensions > 3) + { + throw ::itk::ExceptionObject("The file uses a number of dimensions that is not supported in this application. Only dim<=3 supported "); + } + + switch (_loadedPixelType) + { + case ::itk::ImageIOBase::SCALAR: + { + if (this->_loadedDimensions == 2) + { + loadScalar<2>(); + } + else + { + loadScalar<3>(); + } + + break; + } + + default: + { + throw ::itk::ExceptionObject("The file uses a pixel type that is not supported in this application. Only SCALAR pixel type supported "); + } + } + + _upToDate = true; + }; + + + template + void + GenericImageReader:: + loadScalar() + { + // Use the pixel type to instantiate the appropriate reader + switch (this->_loadedComponentType) + { + case ::itk::ImageIOBase::UCHAR: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, + false, 0, 0, _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::CHAR: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, false, 0, 0, + _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::USHORT: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, + false, 0, 0, _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::SHORT: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, false, 0, 0, + _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::UINT: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, + false, 0, 0, _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::INT: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, false, 0, 0, + _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::ULONG: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, + false, 0, 0, _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::LONG: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, false, 0, 0, + _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::FLOAT: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, false, 0, 0, + _upperSeriesLimit, &_dictionaryArray); + break; + } + + case ::itk::ImageIOBase::DOUBLE: + { + this->_spImage = readImage(_fileName, _seriesReadStyle, false, 0, 0, + _upperSeriesLimit, &_dictionaryArray); + break; + } + + default: + { + throw ::itk::ExceptionObject("The file uses a pixel component type that is not supported in this application. ComponentType: " + + this->_loadedComponentTypeStr); + } + } + }; + + const FileNameString& + GenericImageReader:: + getFileName() const + { + return _fileName; + }; + + + void + GenericImageReader:: + setFileName(const FileNameString& fileName) + { + if (fileName != _fileName) + { + _upToDate = false; + _fileName = fileName; + } + } + + + const unsigned int + GenericImageReader:: + getUpperSeriesLimit() const + { + return _upperSeriesLimit; + }; + + + void + GenericImageReader:: + setUpperSeriesLimit(const unsigned int upperLimit) + { + if (upperLimit != _upperSeriesLimit) + { + _upToDate = false; + _upperSeriesLimit = upperLimit; + }; + }; + + + const ImageSeriesReadStyle::Type + GenericImageReader:: + getSeriesReadStyle() const + { + return _seriesReadStyle; + }; + + + void + GenericImageReader:: + setSeriesReadStyle(ImageSeriesReadStyle::Type readStyle) + { + if (readStyle != _seriesReadStyle) + { + _upToDate = false; + _seriesReadStyle = readStyle; + }; + }; + + + GenericImageReader::GenericOutputImageType* + GenericImageReader:: + GetOutput(unsigned int& loadedDimensions, LoadedPixelType& loadedPixelType, + LoadedComponentType& loadedComponentType) + { + if (!_upToDate) + { + load(); + loadedPixelType = _loadedPixelType; + loadedComponentType = _loadedComponentType; + loadedDimensions = _loadedDimensions; + }; + + return _spImage; + }; + + + + GenericImageReader:: + GenericImageReader() + { + _fileName = ""; + _upperSeriesLimit = 255; + _upToDate = false; + _seriesReadStyle = ImageSeriesReadStyle::Default; + }; + + + GenericImageReader:: + ~GenericImageReader() + { + }; + + const GenericImageReader::MetaDataDictionaryArrayType& + GenericImageReader:: + getMetaDictionaryArray() + { + return _dictionaryArray; + }; + + }//end namespace itk + }//end namespace io +}//end namespace rttb diff --git a/code/io/itk/rttbGenericImageReader.h b/code/io/itk/rttbGenericImageReader.h new file mode 100644 index 0000000..cca2863 --- /dev/null +++ b/code/io/itk/rttbGenericImageReader.h @@ -0,0 +1,163 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/IO/include/mapGenericImageReader.h $ +*/ + +#ifndef __RTTB_GENERIC_IMAGE_READER_H +#define __RTTB_GENERIC_IMAGE_READER_H + +#include "rttbImageReader.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + /** @class ImageReader + * @brief Helper class manages the generic loading (unspecified dimension and pixel type) of 2D/3D images ... + * + * GenericImageReader uses the ImageReader class and dispatches the dimension and pixel type information from the specified image file. + * GenericImageReader supports 2D and 3D images and the following pixel types: + * - (unsigned) char + * - (unsigned) short + * - (unsigned) int + * - (unsigned) long + * - float + * - double + * . + * Due to the fact that it builds upon the itk io infrastructure, all formats supported by ITK + * can be read. + * For further information regarding the usage see documentation of ImageReader. + * @sa ImageReader + * @note code copied from MatchPoint, see documentation (http://sourceforge.net/projects/matchpoint/) + */ + class GenericImageReader : public ::itk::Object + { + public: + typedef GenericImageReader Self; + typedef ::itk::Object Superclass; + typedef ::itk::SmartPointer Pointer; + typedef ::itk::SmartPointer ConstPointer; + + itkTypeMacro(GenericImageReader, ::itk::Object); + itkNewMacro(Self); + + typedef ::itk::DataObject GenericOutputImageType; + + typedef ::itk::ImageIOBase::IOPixelType LoadedPixelType; + + typedef ::itk::ImageIOBase::IOComponentType LoadedComponentType; + + typedef std::vector<::itk::MetaDataDictionary> MetaDataDictionaryArrayType; + + private: + /** Loaded Image.*/ + GenericOutputImageType::Pointer _spImage; + /** The file name of the image. */ + FileNameString _fileName; + /** The upper limit for the searching of series files in the path.*/ + unsigned int _upperSeriesLimit; + /** Indicates if the image data is up to date or should be read again.*/ + bool _upToDate; + /** Defines if the specified image file is part of a series and the + * whole series should be read into one image. Only relevant for 3D images.*/ + ImageSeriesReadStyle::Type _seriesReadStyle; + + unsigned int _loadedDimensions; + LoadedPixelType _loadedPixelType; + LoadedComponentType _loadedComponentType; + std::string _loadedComponentTypeStr; + std::string _loadedPixelTypeStr; + + MetaDataDictionaryArrayType _dictionaryArray; + + /** Loads the image. First identifies pixel type and dimension and then deligates according + * to the pixel type. + * @exception map::core::ExceptionObject If no ImageIO is found. + * @exception map::core::ExceptionObject If dimension of the image is not supported. Only 2D/3D is supported. + * @exception map::core::ExceptionObject If pixel type is not supported. Currently only scalar pixels are supported. + */ + void load(); + + /** Loads an scalar image. + * @exception map::core::ExceptionObject If pixel component type is not supported. + */ + template + void loadScalar(); + + //template + //void loadRGB(); + + public: + /** Function to access the member variable _FileName. _FileName represents the filename of the + * headerfile. The path must be included, the file extension may left away. + * @return File name of the header file.*/ + const FileNameString& getFileName() const; + + /** Function to access the member variable _FileName. _FileName represents the filename of the + * headerfile. The path must be included, the file extension may left away. + * @param [in] sFileName The file name of the header file.*/ + void setFileName(const FileNameString& sFileName); + + /** Function to access the member variable _upperSeriesLimit. _upperSeriesLimit represents + * the upper limit for the series file search. + * @return The upper limit of the series search.*/ + const unsigned int getUpperSeriesLimit() const; + + /** Function to access the member variable _upperSeriesLimit. _upperSeriesLimit represents + * the upper limit for the series file search. Changing the series limit out dates the ImageReader. + * @remark It is only relevant if series style is set to "Numeric". + * @param [in] upperLimit The upper limit of the header file.*/ + void setUpperSeriesLimit(const unsigned int upperLimit); + + /** Function to access the member variable _seriesReadStyle (see member description for more information).*/ + const ImageSeriesReadStyle::Type getSeriesReadStyle() const; + + /** Function to access the member variable _seriesReadStyle (see member description for more information). + * Changing the style out dates the ImageReader.*/ + void setSeriesReadStyle(ImageSeriesReadStyle::Type readStyle); + + /** Function loads the image if needed and returns the data. + * @return Pointer to loaded image. + * @exception map::core::ExceptionObject If no ImageIO is found. + * @exception map::core::ExceptionObject If dimension of the image is not supported. Only 2D/3D is supported. + * @exception map::core::ExceptionObject If pixel type is not supported. Currently only scalar pixels are supported. + * @exception map::core::ExceptionObject If pixel component type is not supported. + */ + GenericOutputImageType* GetOutput(unsigned int& loadedDimensions, LoadedPixelType& loadedPixelType, + LoadedComponentType& loadedComponentType); + + /** Function returns the reference to the meta data dictionary(ies) of the latest file(s) loaded by this class. + * Array may be empty if no MetaDictionary exists.*/ + const MetaDataDictionaryArrayType& getMetaDictionaryArray(); + + protected: + GenericImageReader(); + + virtual ~GenericImageReader(); + }; + + + }//end namespace itk + }//end namespace io +}//end namespace rttb + +#endif diff --git a/code/io/itk/rttbITKImageDoseAccessor.cpp b/code/io/itk/rttbITKImageDoseAccessor.cpp new file mode 100644 index 0000000..182adfe --- /dev/null +++ b/code/io/itk/rttbITKImageDoseAccessor.cpp @@ -0,0 +1,133 @@ +// ----------------------------------------------------------------------- +// 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 "rttbITKImageDoseAccessor.h" +#include "rttbException.h" +#include "rttbInvalidDoseException.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + ITKImageDoseAccessor::~ITKImageDoseAccessor() + { + + } + + ITKImageDoseAccessor::ITKImageDoseAccessor(ITKDoseImageType::ConstPointer doseImage): + _doseGridScaling(1.0) + { + _dose = doseImage; + + if (_dose.IsNull()) + { + throw core::InvalidDoseException("Dose image = 0!") ; + } + + _geoInfo = convertToGeometricInfo(_dose); + } + + DoseTypeGy ITKImageDoseAccessor::getDoseAt(const VoxelGridID aID) const + { + VoxelGridIndex3D aVoxelGridIndex; + + if (_geoInfo.convert(aID, aVoxelGridIndex)) + { + return getDoseAt(aVoxelGridIndex); + } + else + { + return -1; + } + } + + DoseTypeGy ITKImageDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + if (_geoInfo.validIndex(aIndex)) + { + const ITKDoseImageType::IndexType pixelIndex = {{aIndex[0], aIndex[1], aIndex[2]}}; + return _dose->GetPixel(pixelIndex) * _doseGridScaling; + } + else + { + return -1; + } + + } + + bool isValid(const core::GeometricInfo& geoInfo) + { + if (geoInfo.getSpacing()[0] == 0 || geoInfo.getSpacing()[1] == 0 || geoInfo.getSpacing()[2] == 0) + { + throw core::InvalidDoseException("Dose spacing = 0!") ; + return false; + } + + if (geoInfo.getNumRows() == 0 || geoInfo.getNumColumns() == 0 || geoInfo.getNumSlices() == 0) + { + throw core::InvalidDoseException("Dose row/col/slices = 0!") ; + return false; + } + + return true; + } + + core::GeometricInfo convertToGeometricInfo(const ITKImageBaseType* image) + { + core::GeometricInfo geoInfo; + geoInfo.setSpacing(SpacingVectorType3D(image->GetSpacing()[0], image->GetSpacing()[1], + image->GetSpacing()[2])); + geoInfo.setImagePositionPatient(WorldCoordinate3D(image->GetOrigin()[0], image->GetOrigin()[1], + image->GetOrigin()[2])); + OrientationMatrix OM(0); + + for (int col = 0; col < 3; ++col) + { + for (int row = 0; row < 3; ++row) + { + OM(col, row) = image->GetDirection()(col, row); + } + } + + geoInfo.setOrientationMatrix(OM); + geoInfo.setNumColumns(image->GetLargestPossibleRegion().GetSize()[0]); + geoInfo.setNumRows(image->GetLargestPossibleRegion().GetSize()[1]); + geoInfo.setNumSlices(image->GetLargestPossibleRegion().GetSize()[2]); + + if (isValid(geoInfo)) + { + return geoInfo; + } + else + { + throw core::InvalidDoseException("no valid GeometricInfo after conversion from itk"); + return core::GeometricInfo(); + } + } + + } + } +} + diff --git a/code/io/itk/rttbITKImageDoseAccessor.h b/code/io/itk/rttbITKImageDoseAccessor.h new file mode 100644 index 0000000..ded44fe --- /dev/null +++ b/code/io/itk/rttbITKImageDoseAccessor.h @@ -0,0 +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 __ITK_DOSE_ACCESSOR_H +#define __ITK_DOSE_ACCESSOR_H + +#include + +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + +#include "itkImage.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + typedef ::itk::Image ITKDoseImageType; + typedef ::itk::ImageBase<3> ITKImageBaseType; + /*! @class ITKImageDoseAccessor + @brief This class gives access to dose information stored in an itk image + @details _doseGridScaling is always 1.0. Thus, it is assumed that the values in the itkImage are absolute. + */ + class ITKImageDoseAccessor: public core::DoseAccessorInterface + { + private: + /** @brief The dose as itkImage */ + ITKDoseImageType::ConstPointer _dose; + + IDType _doseUID; + + /** @brief The dosegridscaling + * @note is always 1.0 + */ + double _doseGridScaling; + + /*! @brief constructor + @exception InvalidDoseException if _dose is NULL + */ + ITKImageDoseAccessor(); + + + public: + ~ITKImageDoseAccessor(); + + /*! @brief Constructor. Initialization with a itk image containing the dose + @pre doseImage must be a valid instance (and not null) + @note the doseImage pixels are dose (i.e. _doseGridScaling=1.0 is assumed always) + */ + ITKImageDoseAccessor(ITKDoseImageType::ConstPointer doseImage); + + /*! @brief returns the dose for an id + */ + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + /*! @brief returns the dose for an index + */ + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + const IDType getDoseUID() const + { + return _doseUID; + }; + + }; + + /*! @brief get all required data from the itk image contained in _dose + */ + core::GeometricInfo convertToGeometricInfo(const ITKImageBaseType* image); + + /*! @brief check if GeometricInfo is valid + @details e.g. if spacing != 0 and size[i] != 0 + @exception InvalidDoseException + @todo add as member function in GeometricInfo? + */ + bool isValid(const core::GeometricInfo& geoInfo); + } + } +} + +#endif diff --git a/code/io/itk/rttbITKImageDoseAccessorConverter.cpp b/code/io/itk/rttbITKImageDoseAccessorConverter.cpp new file mode 100644 index 0000000..0660866 --- /dev/null +++ b/code/io/itk/rttbITKImageDoseAccessorConverter.cpp @@ -0,0 +1,135 @@ +// ----------------------------------------------------------------------- +// 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 "rttbITKImageDoseAccessorConverter.h" +#include "rttbException.h" +#include "rttbInvalidDoseException.h" +#include "rttbGeometricInfo.h" +#include "itkImageRegionIterator.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + + ITKImageDoseAccessorConverter::ITKImageDoseAccessorConverter(DoseAccessorPointer accessor) + { + setDoseAccessor(accessor); + } + + bool ITKImageDoseAccessorConverter::process() + { + //Transfer GeometricInfo to ITK Properties + core::GeometricInfo geoInfo = _doseAccessor->getGeometricInfo(); + + ITKDoseImageType::RegionType region; + ITKDoseImageType::IndexType start; + + for (unsigned int i = 0; i < 3; ++i) + { + start[i] = 0; + } + + ITKDoseImageType::SizeType size; + size[0] = geoInfo.getNumColumns(); + size[1] = geoInfo.getNumRows(); + size[2] = geoInfo.getNumSlices(); + + ITKDoseImageType::SpacingType spacing; + + for (unsigned int i = 0; i < 3; ++i) + { + spacing[i] = geoInfo.getSpacing()[i]; + } + + ITKDoseImageType::PointType origin; + + for (unsigned int i = 0; i < 3; ++i) + { + origin[i] = geoInfo.getImagePositionPatient()[i]; + } + + ITKDoseImageType::DirectionType direction; + OrientationMatrix OM = geoInfo.getOrientationMatrix(); + + for (int col = 0; col < 3; ++col) + { + for (int row = 0; row < 3; ++row) + { + direction(col, row) = OM(col, row); + } + } + + //Create image, assign properties + region.SetSize(size); + region.SetIndex(start); + + _itkImage = ITKDoseImageType::New(); + _itkImage->SetRegions(region); + _itkImage->SetSpacing(spacing); + _itkImage->SetDirection(direction); + _itkImage->SetOrigin(origin); + _itkImage->Allocate(); + + ::itk::ImageRegionIterator imageIterator(_itkImage, region); + VoxelGridID id = 0; + + //Transfer dose values to itk image + //Large advantage: rttbVoxelGridId ordering is the same as itk ordering + while (!imageIterator.IsAtEnd()) + { + VoxelGridIndex3D aIndex; + + if (_doseAccessor->getGeometricInfo().validID(id)) + { + // Set the current pixel + imageIterator.Set(_doseAccessor->getDoseAt(id)); + } + else + { + if (failsOnInvalidIDs()) + { + throw core::InvalidDoseException("invalid dose id!"); + return false; + } + else + { + imageIterator.Set(_invalidDoseValue); + } + } + + ++imageIterator; + ++id; + } + + return true; + } + + }//end namespace itk + }//end namespace io +}//end namespace rttb + diff --git a/code/io/itk/rttbITKImageDoseAccessorConverter.h b/code/io/itk/rttbITKImageDoseAccessorConverter.h new file mode 100644 index 0000000..9f46e27 --- /dev/null +++ b/code/io/itk/rttbITKImageDoseAccessorConverter.h @@ -0,0 +1,68 @@ +// ----------------------------------------------------------------------- +// 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_DOSE_ACCESSOR_CONVERTER_H +#define __ITK_IMAGE_DOSE_ACCESSOR_CONVERTER_H + +#include + +#include "rttbDoseAccessorProcessorBase.h" +#include "rttbDoseAccessorConversionSettingInterface.h" +#include "rttbITKImageDoseAccessor.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + /*! @class ITKImageDoseAccessorConverter + @brief Class converts/dumps the processed accessor into an itk image + @remark DoseAccessorConversionInterface defines how the converter should react on non valid dose values. + */ + class ITKImageDoseAccessorConverter: public core::DoseAccessorProcessorBase, + public core::DoseAccessorConversionSettingInterface + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + bool process(); + + ITKDoseImageType::Pointer getITKImage() + { + return _itkImage; + } + + ITKImageDoseAccessorConverter(DoseAccessorPointer accessor); + virtual ~ITKImageDoseAccessorConverter() {}; + + private: + ITKImageDoseAccessorConverter(const + ITKImageDoseAccessorConverter&); //not implemented on purpose -> non-copyable + ITKImageDoseAccessorConverter& operator=(const + ITKImageDoseAccessorConverter&);//not implemented on purpose -> non-copyable + + ITKDoseImageType::Pointer _itkImage; + + }; + } + } +} +#endif diff --git a/code/io/itk/rttbITKImageDoseAccessorGenerator.cpp b/code/io/itk/rttbITKImageDoseAccessorGenerator.cpp new file mode 100644 index 0000000..b870a98 --- /dev/null +++ b/code/io/itk/rttbITKImageDoseAccessorGenerator.cpp @@ -0,0 +1,52 @@ +// ----------------------------------------------------------------------- +// 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 "rttbITKImageDoseAccessorGenerator.h" +#include "rttbException.h" +#include "rttbInvalidDoseException.h" + + +namespace rttb{ + namespace io{ + namespace itk{ + + ITKImageDoseAccessorGenerator::ITKImageDoseAccessorGenerator(const ITKDoseImageType* aDoseImage){ + if (aDoseImage == NULL){ + throw core::InvalidDoseException("doseImage is NULL"); + } + + _dosePtr = aDoseImage; + } + + core::DoseAccessorGeneratorBase::DoseAccessorPointer ITKImageDoseAccessorGenerator::generateDoseAccessor(){ + _doseAccessor=boost::make_shared(_dosePtr); + return _doseAccessor; + } + + }//end namespace itk + }//end namespace io +}//end namespace rttb + diff --git a/code/io/itk/rttbITKImageDoseAccessorGenerator.h b/code/io/itk/rttbITKImageDoseAccessorGenerator.h new file mode 100644 index 0000000..c040b45 --- /dev/null +++ b/code/io/itk/rttbITKImageDoseAccessorGenerator.h @@ -0,0 +1,73 @@ +// ----------------------------------------------------------------------- +// 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_DOSE_ACCESSOR_GENERATOR_H +#define __ITK_IMAGE_DOSE_ACCESSOR_GENERATOR_H + +#include +#include + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbBaseType.h" +#include "rttbITKImageDoseAccessor.h" + +#include "itkImage.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + /*! @class ITKImageDoseAccessorGenerator + @brief Generate ITKImageDoseAccessor wrapping an itk image as object (not as file). + @note it implies that the dose information is stored in absolute Gy values. + */ + class ITKImageDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef DoseAccessorGeneratorBase::DoseAccessorPointer DoseAccessorPointer; + + private: + /** @brief The dose as itkImage */ + ITKDoseImageType::ConstPointer _dosePtr; + + ITKImageDoseAccessorGenerator(); + + public: + virtual ~ITKImageDoseAccessorGenerator() {}; + + /*! + @pre aDoseImage must point to a valid instance. + @exception InvalidDoseException Thrown if aDoseImage is invalid. + */ + ITKImageDoseAccessorGenerator(const ITKDoseImageType* aDoseImage); + + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + */ + DoseAccessorPointer generateDoseAccessor() ; + + }; + } + } +} + +#endif diff --git a/code/io/itk/rttbITKImageFileDoseAccessorGenerator.cpp b/code/io/itk/rttbITKImageFileDoseAccessorGenerator.cpp new file mode 100644 index 0000000..0207007 --- /dev/null +++ b/code/io/itk/rttbITKImageFileDoseAccessorGenerator.cpp @@ -0,0 +1,177 @@ +// ----------------------------------------------------------------------- +// 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 "rttbITKImageFileDoseAccessorGenerator.h" +#include "rttbException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" + + +namespace rttb +{ + namespace io + { + namespace itk + { + ITKImageFileDoseAccessorGenerator::~ITKImageFileDoseAccessorGenerator() + { + + } + + ITKImageFileDoseAccessorGenerator::ITKImageFileDoseAccessorGenerator(const FileNameType& fileName) + { + _dicomDoseFileName = fileName; + } + + rttb::core::DoseAccessorGeneratorBase::DoseAccessorPointer + ITKImageFileDoseAccessorGenerator::generateDoseAccessor() + { + GenericImageReader::Pointer spReader = GenericImageReader::New(); + + spReader->setFileName(_dicomDoseFileName); + + GenericImageReader::GenericOutputImageType::Pointer itkGenericImage; + + ITKDoseImageType::ConstPointer itkDoubleImageConst; + + try + { + unsigned int loadedDimensions; + GenericImageReader::LoadedPixelType loadedPixelType; + GenericImageReader::LoadedComponentType loadedComponentType; + itkGenericImage = spReader->GetOutput(loadedDimensions, loadedPixelType, loadedComponentType); + + if (loadedDimensions != 3) + { + throw core::InvalidParameterException("image dimensions != 3. Only dim = 3 supported."); + } + + if (loadedPixelType != ::itk::ImageIOBase::SCALAR) + { + throw core::InvalidParameterException("image component type != SCALAR. Only SCALAR supported."); + } + + if (loadedComponentType == ::itk::ImageIOBase::DOUBLE) + { + _itkDoubleImage = dynamic_cast(itkGenericImage.GetPointer()); + } + else + { + handleGenericImage(itkGenericImage, loadedComponentType); + } + + if (_itkDoubleImage.IsNull()) + { + throw core::InvalidDoseException("Error!!! unable to load input image. File is not existing or has an unsupported format."); + return core::DoseAccessorGeneratorInterface::DoseAccessorPointer(); + } + } + catch (::itk::ExceptionObject& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e << std::endl; + throw rttb::core::InvalidDoseException(e.GetDescription()); + return core::DoseAccessorGeneratorInterface::DoseAccessorPointer(); + } + + _doseAccessor = boost::make_shared(_itkDoubleImage.GetPointer()); + return _doseAccessor; + } + + void ITKImageFileDoseAccessorGenerator::handleGenericImage( + GenericImageReader::GenericOutputImageType* itkGenericImage, + ::itk::ImageIOBase::IOComponentType& loadedComponentType) + { + switch (loadedComponentType) + { + case ::itk::ImageIOBase::UCHAR: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::CHAR: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::USHORT: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::SHORT: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::UINT: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::INT: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::ULONG: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::LONG: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::FLOAT: + { + doCasting(itkGenericImage); + break; + } + + case ::itk::ImageIOBase::DOUBLE: + { + doCasting(itkGenericImage); + break; + } + + default: + { + throw core::InvalidParameterException("image type unknown"); + } + } + } + }//end namespace itk + }//end namespace io +}//end namespace rttb + diff --git a/code/io/itk/rttbITKImageFileDoseAccessorGenerator.h b/code/io/itk/rttbITKImageFileDoseAccessorGenerator.h new file mode 100644 index 0000000..d199284 --- /dev/null +++ b/code/io/itk/rttbITKImageFileDoseAccessorGenerator.h @@ -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$ (last changed revision) +// @date $Date$ (last change date) +// @author $Author$ (last changed by) +*/ +#ifndef __ITK_IMAGE_DOSE_FILE_ACCESSOR_GENERATOR_H +#define __ITK_IMAGE_DOSE_FILE_ACCESSOR_GENERATOR_H + +#include +#include + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbBaseType.h" +#include "rttbITKImageDoseAccessor.h" +#include "rttbGenericImageReader.h" + +#include "itkImage.h" +#include "itkCastImageFilter.h" + + +namespace rttb +{ + namespace io + { + namespace itk + { + + /*! @class ITKImageFileDoseAccessorGenerator + @brief Load dose data using the itk loading methods and wraps the resulting itk image in a ITKImageDoseAccessor. + * this is normally used if dose distributions are stored in formats like meta image, nrrd... + * @note it implies that the dose information is stored in absolute Gy values. + */ + class ITKImageFileDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef DoseAccessorGeneratorBase::DoseAccessorPointer DoseAccessorPointer; + + private: + FileNameType _dicomDoseFileName; + /** @brief The dose as itkImage */ + ITKDoseImageType::Pointer _itkDoubleImage; + + ITKImageFileDoseAccessorGenerator(); + + /*! @brief Casts into itkImage + @details result is stored into _itkDoubleImage + */ + template void doCasting(GenericImageReader::GenericOutputImageType* + genericImage); + + /*! @brief Converts a generic image to itkImage + @param itkGenericImage the image read by GenericImageReader + @param loadedComponentType the component type (used for casting later on) + @exception InvalidParameterException if component type is not supported + @sa GenericImageReader + */ + void handleGenericImage(GenericImageReader::GenericOutputImageType* itkGenericImage, + ::itk::ImageIOBase::IOComponentType& loadedComponentType); + + + public: + ~ITKImageFileDoseAccessorGenerator(); + + ITKImageFileDoseAccessorGenerator(const FileNameType& fileName); + + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @exception InvalidDoseException Thrown if file could not be read + @exception InvalidParameterException Thrown if file has imageDimension !=3 or image component type != SCALAR + @details is always converted into a itkImage by using a CastImageFilter + @sa doCasting, handleGenericImage + */ + DoseAccessorPointer generateDoseAccessor(); + + + }; + }//end namespace itk + }//end namespace io +}//end namespace rttb + +#include "rttbITKImageFileDoseAccessorGenerator.tpp" + +#endif diff --git a/code/io/itk/rttbITKImageFileDoseAccessorGenerator.tpp b/code/io/itk/rttbITKImageFileDoseAccessorGenerator.tpp new file mode 100644 index 0000000..768ee41 --- /dev/null +++ b/code/io/itk/rttbITKImageFileDoseAccessorGenerator.tpp @@ -0,0 +1,64 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/IO/include/mapImageReader.tpp $ +*/ + +#ifndef __RTTB_ITK_IMAGE_FILE_DOSE_ACCESSOR_GENERATOR_TPP +#define __RTTB_ITK_IMAGE_FILE_DOSE_ACCESSOR_GENERATOR_TPP + +#include "rttbITKImageFileDoseAccessorGenerator.h" +#include "rttbInvalidParameterException.h" +#include "rttbInvalidDoseException.h" +#include "rttbITKImageDoseAccessor.h" + +namespace rttb +{ + namespace io + { + namespace itk + { + + template void ITKImageFileDoseAccessorGenerator::doCasting( + GenericImageReader::GenericOutputImageType* genericImage) + { + typedef ::itk::Image InputImageType; + typedef ITKDoseImageType OutputImageType; + InputImageType::Pointer pCastedInput = dynamic_cast(genericImage); + typedef ::itk::CastImageFilter CastFilterType; + CastFilterType::Pointer castFilter = CastFilterType::New(); + castFilter->SetInput(pCastedInput); + + try + { + //important to update the filter! + castFilter->Update(); + _itkDoubleImage = castFilter->GetOutput(); + } + catch (::itk::ExceptionObject& e) + { + std::cerr << "ITK Error!!!" << std::endl; + std::cerr << e << std::endl; + } + } + + } + } +} +#endif diff --git a/code/io/itk/rttbImageReader.h b/code/io/itk/rttbImageReader.h new file mode 100644 index 0000000..2ea3025 --- /dev/null +++ b/code/io/itk/rttbImageReader.h @@ -0,0 +1,224 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/IO/include/mapImageReader.h $ +*/ + + +#ifndef __RTTB_IMAGE_READER_H +#define __RTTB_IMAGE_READER_H + +#include "itkImage.h" +#include "itkImageSource.h" + +namespace rttb +{ + namespace io + { + namespace itk { + struct ImageSeriesReadStyle + { + enum Type + { + Default = 0, //* - Depending on the file extension (DICOM images (*.dcm, *.ima): Dicom; others: None; No image file: Dicom) + None = 1, //* - No series reading, only the specified file + Dicom = 2, //* - Use series reader and DCMTKSeriesFileNames + Numeric = 3, //* - Use series reader and NumericSeriesFileNames + GDCM = 4 //* - Use series reader and GDCMSeriesFileNames + }; + }; + + + /** @class ImageReader + * @brief Helper class manages the loading of 2D/3D images based on itk but with some convenience features ... + * + * ImageReader is used to load 2D or 3D images in an itk like style, but also offers + * some convenience features and the specialties of different formats into account.\n + * 2D images will be loaded directly by the IO classes of itk, so in this case the + * ImageReader is only a layer of abstraction.\n + * 3D images will be handled different, depending on the type of files and the chosen + * series read style:\n + * - Default: Depending on the file extension (DICOM images (*.dcm, *.ima): Dicom; others: None; No image file: Dicom + * - None: directly by the itk io (no series reading) + * - Dicom: uses itk series reader, currently a list of files will be generated + that is similar to GDCM (old ::itk::DICOMKSeriesFileNames is not supported any more by itk > 4.3.x.) + * - GDCM: uses itk series reader, the list of files will be generated + * by the ::itk::GDCMSeriesFileNames in the specified path ordered by + * imagePositionPatient. + * - Numeric: Will be considered as series of images. The list of files will be + * created by ::itk::NumericSeriesFileNames, so in this case the given + * file name is already masked by %d for the increasing index within + * the file name. + * . + * @todo implement Reader as an ::itk::ImageSource in the behavior. + * @note code copied from MatchPoint, see documentation (http://sourceforge.net/projects/matchpoint/) + */ + template + class ImageReader + { + public: + typedef ::itk::Image InputImageType; + typedef ::itk::Image OutputImageType; + typedef TInputPixel RescaleValueType; + typedef std::vector<::itk::MetaDataDictionary> MetaDataDictionaryArrayType; + + typedef std::string String; + + typedef std::ostringstream OStringStream; + typedef std::istringstream IStringStream; + + virtual const char* GetNameOfClass() const + { + return "ImageReader"; + } + + private: + /** Loaded Image.*/ + typename OutputImageType::Pointer _spImage; + /** The file name of the image. */ + String _fileName; + /** The upper limit for the searching of series files in the path.*/ + unsigned int _upperSeriesLimit; + /** Indicates if the image data is up to date or should be read again.*/ + bool _upToDate; + /** Indicates if the output image intensity should be rescaled.*/ + bool _rescaleImage; + /** Indicates the minimum of the output.*/ + RescaleValueType _rescaleMin; + /** Indicates the maximum of the output.*/ + RescaleValueType _rescaleMax; + /** Defines if the specified image file is part of a series and the + * whole series should be read into one image. Only relevant for 3D images.*/ + typename ImageSeriesReadStyle::Type _seriesReadStyle; + + MetaDataDictionaryArrayType _dictionaryArray; + + void load2D(); + + typename ::itk::ImageSource::Pointer prepareNumericSource() const; + typename ::itk::ImageSource::Pointer prepareDICOMSource() const; + typename ::itk::ImageSource::Pointer prepareNormalSource() const; + typename ::itk::ImageSource::Pointer prepareGDCMSource() const; + + void load3D(); + + typedef std::vector<::itk::MetaDataDictionary*> ITKMetaDataDictionaryArray; + void copyMetaDictionaryArray(const ITKMetaDataDictionaryArray* fromArray, + MetaDataDictionaryArrayType& toArray); + + public: + /** Function to access the member variable _FileName. _FileName represents the filename of the + * headerfile. The path must be included, the file extension may left away. + * @return File name of the header file.*/ + const String& getFileName() const; + + /** Function to access the member variable _FileName. _FileName represents the filename of the + * headerfile. The path must be included, the file extension may left away. + * @param [in] sFileName The file name of the header file.*/ + void setFileName(const String& sFileName); + + /** Function to access the member variable _rescaleMin. _rescaleMin represents + * the minimum of the intensity rescale filter. + * @return The minimum of the intensity rescale filter.*/ + const RescaleValueType& getRescaleMinimum() const; + + /** Function to access the member variable _rescaleMin. _rescaleMin represents + * the minimum of the intensity rescale filter. Changing the rescale minimum out dates the ImageReader. + * @param [in] dRescaleMin The minimum of the intensity rescale filter.*/ + void setRescaleMinimum(const RescaleValueType& rescaleMin); + + /** Function to access the member variable _rescaleMin. _rescaleMax represents + * the minimum of the intensity rescale filter. + * @return The minimum of the intensity rescale filter.*/ + const RescaleValueType& getRescaleMaximum() const; + + /** Function to access the member variable _rescaleMin. _rescaleMax represents + * the minimum of the intensity rescale filter. Changing the rescale maximum out dates the ImageReader. + * @param [in] dRescaleMax The minimum of the intensity rescale filter.*/ + void setRescaleMaximum(const RescaleValueType& rescaleMax); + + /** Function to access the member variable _rescaleImage. _rescaleImage indicates if a + * loaded image should be rescaled regarding its intensities. + * @return If the ImageReader converts images to iso-voxel.*/ + const bool getRescaleImage() const; + + /** Function to access the member variable _rescaleImage. _rescaleImage indicates if a + * loaded image should be rescaled regarding its intensities. Changing the rescale option out dates the ImageReader. + * @param [in] rescaleImage Specifies if image should be converted to isovoxel.*/ + void setRescaleImage(const bool rescaleImage); + + /** Function to access the member variable _upperSeriesLimit. _upperSeriesLimit represents + * the upper limit for the series file search. + * @return The upper limit of the series search.*/ + const unsigned int getUpperSeriesLimit() const; + + /** Function to access the member variable _upperSeriesLimit. _upperSeriesLimit represents + * the upper limit for the series file search. Changing the series limit out dates the ImageReader. + * @remark It is only relevant if series style is set to "Numeric". + * @param [in] upperLimit The upper limit of the header file.*/ + void setUpperSeriesLimit(const unsigned int upperLimit); + + /** Function to access the member variable _seriesReadStyle (see member description for more information).*/ + const typename ImageSeriesReadStyle::Type getSeriesReadStyle() const; + + /** Function to access the member variable _seriesReadStyle (see member description for more information). + * Changing the style out dates the ImageReader.*/ + void setSeriesReadStyle(typename ImageSeriesReadStyle::Type readStyle); + + /** Function loads the image if needed and returns the data. + * @return Pointer to loaded image.*/ + OutputImageType* GetOutput(void); + + /** Function returns the reference to the meta data dictionary(ies) of the latest file(s) loaded by this class. + * Array may be empty if no MetaDictionary exists.*/ + const MetaDataDictionaryArrayType& getMetaDictionaryArray(); + + ImageReader(); + + virtual ~ImageReader(); + }; + + + /** + * @brief Helper function for the use of ImageReader in on statement ... + * + * for specific informations please see the documentation of ImageReader. + * @param pLoadedDictArray Pass a pointer to valid array to receive the meta dictionaries + * loaded with the image. If the pointer is null, no dictionaries will be transfered. The array + * will be reseted before the loaded dictionaries will be added. + * @sa ImageReader + * @ingroup Utils + */ + template + typename ImageReader::OutputImageType::Pointer + readImage(const std::string& fileName, + ImageSeriesReadStyle::Type readStyle = ImageSeriesReadStyle::Default, + bool rescaleImage = false, + typename ImageReader::RescaleValueType rescaleMin = 0, + typename ImageReader::RescaleValueType rescaleMax = 255, + unsigned int upperNumericSeriesLimit = 100, + typename ImageReader::MetaDataDictionaryArrayType* + pLoadedDictArray = NULL); + }//end namespace itk + }//end namespace io +}//end namespace rttb + +#include "rttbImageReader.tpp" + +#endif diff --git a/code/io/itk/rttbImageReader.tpp b/code/io/itk/rttbImageReader.tpp new file mode 100644 index 0000000..9e07f23 --- /dev/null +++ b/code/io/itk/rttbImageReader.tpp @@ -0,0 +1,534 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Code/IO/include/mapImageReader.tpp $ +*/ + +#ifndef __RTTB_IMAGE_READER_TPP +#define __RTTB_IMAGE_READER_TPP + +#include "rttbImageReader.h" +#include "rttbFileDispatch.h" + +#ifdef RTTB_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER +#undef ITK_IO_FACTORY_REGISTER_MANAGER +#endif //RTTB_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER + +#include "itkImageFileReader.h" +#include "itkImageFileWriter.h" + +#include "itkImageSeriesReader.h" +#include "itkImageSeriesWriter.h" +#include "itkNumericSeriesFileNames.h" +#include "itkGDCMSeriesFileNames.h" + +#include "itkRescaleIntensityImageFilter.h" +#include "itkCastImageFilter.h" +#include "itkFixedArray.h" +#include "itksys/SystemTools.hxx" + +#include +#include + + +namespace rttb +{ + namespace io + { + namespace itk + { + + template + void + ImageReader:: + load2D() + { + typedef ::itk::ImageFileReader< InputImageType > ImageReaderType; + typedef ::itk::RescaleIntensityImageFilter< InputImageType, InputImageType > RescaleFilterType; + typedef ::itk::CastImageFilter< InputImageType, OutputImageType > CastFilterType; + + typename CastFilterType::Pointer imageCaster = CastFilterType::New(); + typename ImageReaderType::Pointer imageReader = ImageReaderType::New(); + typename RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New(); + + rescaleFilter->SetOutputMinimum(static_cast(_rescaleMin)); + rescaleFilter->SetOutputMaximum(static_cast(_rescaleMax)); + + imageReader->SetFileName(_fileName.c_str()); + rescaleFilter->SetInput(imageReader->GetOutput()); + + if (_rescaleImage) + { + imageCaster->SetInput(rescaleFilter->GetOutput()); + } + else + { + imageCaster->SetInput(imageReader->GetOutput()); + } + + _spImage = imageCaster->GetOutput(); + + imageCaster->Update(); + + _dictionaryArray.clear(); + _dictionaryArray.push_back(imageReader->GetImageIO()->GetMetaDataDictionary()); + + _upToDate = true; + }; + + template + const typename ImageReader::MetaDataDictionaryArrayType& + ImageReader:: + getMetaDictionaryArray() + { + return _dictionaryArray; + }; + + template + void + ImageReader:: + copyMetaDictionaryArray(const ITKMetaDataDictionaryArray* fromArray, + MetaDataDictionaryArrayType& toArray) + { + toArray.clear(); + + ITKMetaDataDictionaryArray::const_iterator itr = fromArray->begin(); + ITKMetaDataDictionaryArray::const_iterator end = fromArray->end(); + + while (itr != end) + { + toArray.push_back(*(*itr)); + ++itr; + } + }; + + template + typename ::itk::ImageSource::InputImageType>::Pointer + ImageReader:: + prepareNumericSource() const + { + //mumeric series image reader + typedef ::itk::ImageSeriesReader< InputImageType > SeriesReaderType; + typedef ::itk::NumericSeriesFileNames NamesType; + + typename SeriesReaderType::Pointer seriesReader = SeriesReaderType::New(); + NamesType::Pointer names = NamesType::New(); + + names->SetStartIndex(1); + names->SetEndIndex(_upperSeriesLimit); + names->SetSeriesFormat(_fileName.c_str()); + + seriesReader->SetFileNames(names->GetFileNames()); + + if (seriesReader->GetFileNames().size() == 0) + { + throw ::itk::ExceptionObject("Image reader is not correctly configured. Preparing a series reading of a numeric source no(!) files were found."); + } + + typename ::itk::ImageSource::InputImageType>::Pointer + genericReader = seriesReader.GetPointer(); + return genericReader; + }; + + template + typename ::itk::ImageSource::InputImageType>::Pointer + ImageReader:: + prepareDICOMSource() const + { + //ITK > v4.3.x removed old DICOMSeriesFileNames. Thus currently only support GDCM as source by default + return prepareGDCMSource(); + /**@TODO Add support for DCMTKSeriesFileNames too*/ + }; + + template + typename ::itk::ImageSource::InputImageType>::Pointer + ImageReader:: + prepareGDCMSource() const + { + core::FileDispatch dispatch(_fileName); + + FileNameString dir = dispatch.getPath(); + FileNameString strippedFileName = dispatch.getFullName(); + + typedef ::itk::GDCMSeriesFileNames NamesGeneratorType; + NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New(); + nameGenerator->SetInputDirectory(dir); + nameGenerator->SetUseSeriesDetails(true); + + ::itk::FilenamesContainer fileNames; + + if (strippedFileName.empty()) + { + std::cerr << "No file name specified. Use first DICOM series found in directory." << std::endl; + fileNames = nameGenerator->GetInputFileNames(); + } + else + { + ::itk::SerieUIDContainer seriesUIDs = nameGenerator->GetSeriesUIDs(); + + std::cerr << "Checking found DICOM series." << std::endl; + + //check the found series for the filename to pick the right series correlated to the passed filename + while (seriesUIDs.size() > 0) + { + fileNames = nameGenerator->GetFileNames(seriesUIDs.back()); + std::cerr << "Checking series: " << seriesUIDs.back() << " (file count: " << + fileNames.size() << ")" << std::endl; + seriesUIDs.pop_back(); + + for (::itk::SerieUIDContainer::const_iterator pos = fileNames.begin(); pos != fileNames.end(); + ++pos) + { + if (pos->find(strippedFileName) != FileNameString::npos) + { + //this series containes the passed filename -> + //we have the right block of files -> we are done. + std::cerr << "Found right series!" << std::endl; + seriesUIDs.clear(); + break; + } + } + } + } + + typedef ::itk::ImageSeriesReader< InputImageType > SeriesReaderType; + typename SeriesReaderType::Pointer seriesReader = SeriesReaderType::New(); + + seriesReader->SetFileNames(fileNames); + + if (seriesReader->GetFileNames().size() == 0) + { + throw ::itk::ExceptionObject("Image reader is not correctly configured. Preparing a series reading of a DICOM source no(!) dicom files were found. search location: " + + _fileName); + } + + typename ::itk::ImageSource::InputImageType>::Pointer + genericReader = seriesReader.GetPointer(); + return genericReader; + }; + + template + typename ::itk::ImageSource::InputImageType>::Pointer + ImageReader:: + prepareNormalSource() const + { + //Normal image reader (no series read style) + typedef ::itk::ImageFileReader< InputImageType > ImageReaderType; + typename ImageReaderType::Pointer imageReader = ImageReaderType::New(); + imageReader->SetFileName(_fileName.c_str()); + + typename ::itk::ImageSource::InputImageType>::Pointer + genericReader = imageReader.GetPointer(); + return genericReader; + }; + + + template + void + ImageReader:: + load3D() + { + core::FileDispatch dispatch(_fileName); + + FileNameString sTemp = dispatch.getExtension(); + FileNameString sDir = dispatch.getPath(); + + //Convert to lowercase + for (FileNameString::iterator spos = sTemp.begin(); spos != sTemp.end(); spos++) + { + (*spos) = std::tolower((*spos), std::locale("")); + } + + typedef ::itk::RescaleIntensityImageFilter< InputImageType, InputImageType > RescaleFilterType; + typedef ::itk::CastImageFilter< InputImageType, OutputImageType > CastFilterType; + typename CastFilterType::Pointer imageCaster = CastFilterType::New(); + typename RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New(); + typename ::itk::ImageSource::Pointer spReader; + + rescaleFilter->SetOutputMinimum(static_cast(_rescaleMin)); + rescaleFilter->SetOutputMaximum(static_cast(_rescaleMax)); + + if (_seriesReadStyle == ImageSeriesReadStyle::Numeric) + { + spReader = prepareNumericSource(); + } + else if (_seriesReadStyle == ImageSeriesReadStyle::Dicom) + { + spReader = prepareDICOMSource(); + } + else if (_seriesReadStyle == ImageSeriesReadStyle::GDCM) + { + spReader = prepareGDCMSource(); + } + else if (_seriesReadStyle == ImageSeriesReadStyle::Default) + { + bool isDir = itksys::SystemTools::FileIsDirectory(_fileName.c_str()); + + if (isDir || sTemp == ".dcm" || sTemp == ".ima") + { + spReader = prepareDICOMSource(); + } + else + { + spReader = prepareNormalSource(); + } + } + else + { + //style is none + spReader = prepareNormalSource(); + } + + if (_rescaleImage) + { + rescaleFilter->SetInput(spReader->GetOutput()); + imageCaster->SetInput(rescaleFilter->GetOutput()); + } + else + { + imageCaster->SetInput(spReader->GetOutput()); + } + + imageCaster->Update(); + + _spImage = imageCaster->GetOutput(); + + typedef ::itk::ImageFileReader< InputImageType > ImageReaderType; + typedef ::itk::ImageSeriesReader< InputImageType > ImageSeriesReaderType; + ImageReaderType* pFileReader = dynamic_cast(spReader.GetPointer()); + ImageSeriesReaderType* pSeriesReader = dynamic_cast(spReader.GetPointer()); + + if (pFileReader) + { + _dictionaryArray.clear(); + _dictionaryArray.push_back(pFileReader->GetImageIO()->GetMetaDataDictionary()); + } + else if (pSeriesReader) + { + copyMetaDictionaryArray(pSeriesReader->GetMetaDataDictionaryArray(), _dictionaryArray); + } + else + { + throw ::itk::ExceptionObject("Image reader is not valid. Internal reader seams not to be itk::ImageFileReader or itk::ImageSeriesReader."); + } + + _upToDate = true; + }; + + template + const FileNameString& + ImageReader:: + getFileName() const + { + return _fileName; + }; + + template + void + ImageReader:: + setFileName(const FileNameString& fileName) + { + if (fileName != _fileName) + { + _upToDate = false; + _fileName = fileName; + } + } + + template + const typename ImageReader::RescaleValueType& + ImageReader:: + getRescaleMinimum() const + { + return _rescaleMin; + }; + + template + void + ImageReader:: + setRescaleMinimum(const RescaleValueType& dRescaleMin) + { + if (dRescaleMin != _rescaleMin) + { + _upToDate = false; + _rescaleMin = dRescaleMin; + }; + }; + + template + const typename ImageReader::RescaleValueType& + ImageReader:: + getRescaleMaximum() const + { + return _rescaleMax; + }; + + template + void + ImageReader:: + setRescaleMaximum(const RescaleValueType& dRescaleMax) + { + if (dRescaleMax != _rescaleMax) + { + _upToDate = false; + _rescaleMax = dRescaleMax; + }; + }; + + template + const bool + ImageReader:: + getRescaleImage() const + { + return _rescaleImage; + }; + + template + void + ImageReader:: + setRescaleImage(const bool rescaleImage) + { + if (rescaleImage != _rescaleImage) + { + _upToDate = false; + _rescaleImage = rescaleImage; + }; + }; + + template + const unsigned int + ImageReader:: + getUpperSeriesLimit() const + { + return _upperSeriesLimit; + }; + + template + void + ImageReader:: + setUpperSeriesLimit(const unsigned int upperLimit) + { + if (upperLimit != _upperSeriesLimit) + { + _upToDate = false; + _upperSeriesLimit = upperLimit; + }; + }; + + template + const ImageSeriesReadStyle::Type + ImageReader:: + getSeriesReadStyle() const + { + return _seriesReadStyle; + }; + + template + void + ImageReader:: + setSeriesReadStyle(ImageSeriesReadStyle::Type readStyle) + { + if (readStyle != _seriesReadStyle) + { + _upToDate = false; + _seriesReadStyle = readStyle; + }; + }; + + template + typename ImageReader::OutputImageType* + ImageReader:: + GetOutput(void) + { + if (!_upToDate) + { + switch (OutputImageType::GetImageDimension()) + { + case 2: + load2D(); + break; + + case 3: + load3D(); + break; + + default: + throw ::itk::ExceptionObject("Image reader only accepts 2 or 3 dimensional images."); + }; + }; + + return _spImage; + }; + + + template + ImageReader:: + ImageReader() + { + _fileName = ""; + _upperSeriesLimit = 255; + _upToDate = false; + _rescaleImage = false; + _rescaleMax = 255; + _rescaleMin = 0; + _seriesReadStyle = ImageSeriesReadStyle::Default; + }; + + template + ImageReader:: + ~ImageReader() + { + }; + + + template + typename ImageReader::OutputImageType::Pointer readImage( + const FileNameString& fileName, + ImageSeriesReadStyle::Type readStyle, + bool rescaleImage, + typename ImageReader::RescaleValueType rescaleMin, + typename ImageReader::RescaleValueType rescaleMax, + unsigned int upperNumericSeriesLimit, + typename ImageReader::MetaDataDictionaryArrayType* + pLoadedDictArray) + { + ImageReader reader; + + reader.setFileName(fileName); + reader.setSeriesReadStyle(readStyle); + reader.setRescaleImage(rescaleImage); + reader.setRescaleMaximum(rescaleMax); + reader.setRescaleMinimum(rescaleMin); + reader.setUpperSeriesLimit(upperNumericSeriesLimit); + + typename ImageReader::OutputImageType::Pointer spResult = + reader.GetOutput(); + + if (pLoadedDictArray) + { + *pLoadedDictArray = reader.getMetaDictionaryArray(); + }; + + return spResult; + }; + + } + } +} +#endif diff --git a/code/io/other/CMakeLists.txt b/code/io/other/CMakeLists.txt new file mode 100644 index 0000000..afd40c3 --- /dev/null +++ b/code/io/other/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBOtherIO DEPENDS RTTBCore RTTBAlgorithms PACKAGE_DEPENDS Boost) \ No newline at end of file diff --git a/code/io/other/files.cmake b/code/io/other/files.cmake new file mode 100644 index 0000000..db854d7 --- /dev/null +++ b/code/io/other/files.cmake @@ -0,0 +1,16 @@ +SET(CPP_FILES + rttbDoseStatisticsXMLWriter.cpp + rttbDVHTxtFileReader.cpp + rttbDVHTxtFileWriter.cpp + rttbDVHXMLFileReader.cpp + rttbDVHXMLFileWriter.cpp + ) + +SET(H_FILES + rttbDoseStatisticsXMLWriter.h + rttbDVHTxtFileReader.h + rttbDVHTxtFileWriter.h + rttbDVHXMLFileReader.h + rttbDVHXMLFileWriter.h + ../rttbDVHWriterInterface.h +) diff --git a/code/io/other/rttbDVHTxtFileReader.cpp b/code/io/other/rttbDVHTxtFileReader.cpp new file mode 100644 index 0000000..6606938 --- /dev/null +++ b/code/io/other/rttbDVHTxtFileReader.cpp @@ -0,0 +1,196 @@ +// ----------------------------------------------------------------------- +// 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; + 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()); + + 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_front(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 io +}//end namespace rttb + diff --git a/code/io/other/rttbDVHTxtFileReader.h b/code/io/other/rttbDVHTxtFileReader.h new file mode 100644 index 0000000..9f605c8 --- /dev/null +++ b/code/io/other/rttbDVHTxtFileReader.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_TXT_FILE_READER_H +#define __DVH_TXT_FILE_READER_H + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbDVHGeneratorInterface.h" + +namespace rttb{ + namespace io{ + namespace other{ + + /*! @class DVHTxtFileReader + @brief Reads DVH data from txt files. + */ + class DVHTxtFileReader: public core::DVHGeneratorInterface + { + + + private: + FileNameString _fileName; + bool _resetFile; + + /*! @brief Create new DVH object using the info from dvh txt file + @exception InvalidParameterException Thrown if _fileName invalid + */ + void createDVH(); + + public: + /*! @brief Constructor. + */ + DVHTxtFileReader(FileNameString aFileName); + + ~DVHTxtFileReader(); + + /*! @brief Change file name. + */ + void resetFileName(FileNameString aFileName); + + /*! @brief Generate DVH, createDVH() will be called + @return Return new shared pointer of DVH. + @exception InvalidParameterException Thrown if _fileName invalid + */ + DVHPointer generateDVH(); + }; + } + } +} + +#endif diff --git a/code/io/other/rttbDVHTxtFileWriter.cpp b/code/io/other/rttbDVHTxtFileWriter.cpp new file mode 100644 index 0000000..299d59b --- /dev/null +++ b/code/io/other/rttbDVHTxtFileWriter.cpp @@ -0,0 +1,115 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDVHTxtFileWriter.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbException.h" + +namespace rttb{ + namespace io{ + namespace other{ + + DVHTxtFileWriter::DVHTxtFileWriter(FileNameString aFileName, DVHType aDVHType){ + this->setFileName(aFileName); + this->setDVHType(aDVHType); + } + + void DVHTxtFileWriter::setDVHType(DVHType aDVHType){ + _dvhType=aDVHType; + } + + FileNameString DVHTxtFileWriter::getFileName() const{ + return _fileName; + } + + void DVHTxtFileWriter::setFileName(FileNameString aFileName){ + _fileName=aFileName; + } + + DVHType DVHTxtFileWriter::getDVHType() const{ + return _dvhType; + } + + void DVHTxtFileWriter::writeDVH(DVHPointer aDvh) { + if(!aDvh) + { + throw core::NullPointerException("aDvh must not be NULL! "); + } + std::ofstream out_dvh_ofstream(this->_fileName.c_str(),std::ios::out); + + if (!out_dvh_ofstream.is_open() || !out_dvh_ofstream.good()) + { + throw core::InvalidParameterException("Invalid dvh file name: could not open write file"); + } + else{ + //setting string stream precission explicitly is mandatory to guarantee that tests + //run sucessfully on different systems! + out_dvh_ofstream.precision(10); + + if(_dvhType.Type != DVHType::Differential && _dvhType.Type != DVHType::Cumulative) + { + throw core::InvalidParameterException("DVH Type not acceptable: Only: DIFFERENTIAL/CUMULATIVE!"); + } + + if(_dvhType.Type == DVHType::Differential) + { + out_dvh_ofstream << "DVH Type: DIFFERENTIAL\n"; + } + else if(_dvhType.Type == DVHType::Cumulative) + { + out_dvh_ofstream << "DVH Type: CUMULATIVE\n"; + } + + out_dvh_ofstream << "DeltaD: "<getDeltaD() << "\n"; + out_dvh_ofstream << "DeltaV: "<getDeltaV() << "\n"; + out_dvh_ofstream << "StructureID: "<getStructureID()<< "\n"; + out_dvh_ofstream << "DoseID: "<getDoseID()<< "\n"; + out_dvh_ofstream << "DVH Data: "<< "\n"; + + if(_dvhType.Type == DVHType::Differential) + { + DataDifferentialType dataDifferential=aDvh->getDataDifferential(); + int numberOfBins=dataDifferential.size(); + for(int i=0; icalcCumulativeDVH(); + int numberOfBins=dataCumulative.size(); + for(int i=0; i +#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 structureLabel; + std::string dvhType; + 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; + + 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"){ + dataDifferential.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()); + + 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_front(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 io +}//end namespace rttb + diff --git a/code/io/other/rttbDVHXMLFileReader.h b/code/io/other/rttbDVHXMLFileReader.h new file mode 100644 index 0000000..a675bd0 --- /dev/null +++ b/code/io/other/rttbDVHXMLFileReader.h @@ -0,0 +1,67 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_XML_FILE_READER_H +#define __DVH_XML_FILE_READER_H + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbDVHGeneratorInterface.h" + +namespace rttb{ + namespace io{ + namespace other{ + + /*! @class DVHXMLFileReader + @brief Reads DVH data from xml files. + */ + class DVHXMLFileReader: public core::DVHGeneratorInterface + { + private: + FileNameString _fileName; + bool _resetFile; + + /*! @brief Create new DVH object using the info from dvh txt file + @exception InvalidParameterException Thrown if _fileName invalid + */ + void createDVH(); + + public: + /*! @brief Constructor. + */ + DVHXMLFileReader(FileNameString aFileName); + + ~DVHXMLFileReader(); + + /*! @brief Change file name. + */ + void resetFileName(FileNameString aFileName); + + /*! @brief Generate DVH, createDVH() will be called + @return Return new shared pointer of DVH. + @exception InvalidParameterException Thrown if _fileName invalid + */ + DVHPointer generateDVH(); + }; + } + } +} + +#endif diff --git a/code/io/other/rttbDVHXMLFileWriter.cpp b/code/io/other/rttbDVHXMLFileWriter.cpp new file mode 100644 index 0000000..be1fa09 --- /dev/null +++ b/code/io/other/rttbDVHXMLFileWriter.cpp @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// 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(); + int 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(int i=0; icalcCumulativeDVH(); + + for(int i=0; i 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!"); + } + + } + + } + } +} \ No newline at end of file diff --git a/code/io/other/rttbDVHXMLFileWriter.h b/code/io/other/rttbDVHXMLFileWriter.h new file mode 100644 index 0000000..d06d707 --- /dev/null +++ b/code/io/other/rttbDVHXMLFileWriter.h @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_XML_FILE_WRITER_H +#define __DVH_XML_FILE_WRITER_H + + +#include "rttbDVH.h" +#include "../rttbDVHWriterInterface.h" +#include "rttbBaseType.h" + +namespace rttb{ + namespace io{ + namespace other{ + + /*! @class DVHXMLFileWriter + @brief Writes DVHs to xml files. + */ + class DVHXMLFileWriter: public DVHWriterInterface + { + public: + typedef core::DVH::DataDifferentialType DataDifferentialType; + typedef core::DVH::DVHPointer DVHPointer; + + private: + FileNameString _fileName; + DVHType _dvhType; + + public: + /*! @brief Constructor + @param aFileName a xml file name to write the DVH to aDVHType: DIFFERENTIAL or CUMULATIVE. + */ + DVHXMLFileWriter(FileNameString aFileName, DVHType aDVHType); + + void setFileName(FileNameString aFileName); + FileNameString getFileName() const; + + void setDVHType(DVHType aDVHType); + DVHType getDVHType() const; + + /*! @brief Write aDvh to xml file with the name: _fileName + @exception NullPointerException Thrown if _aDvh is NULL + @exception InvalidParameterException Thrown if _fileName invalid: could not open; + or if _dvhType invalid: only DIFFERENTIAL or CUMULATIVE is accepted! + @exception Exception thrown if dvh init error + */ + void writeDVH(DVHPointer aDvh); + }; + } + } +} +#endif diff --git a/code/io/other/rttbDoseStatisticsXMLWriter.cpp b/code/io/other/rttbDoseStatisticsXMLWriter.cpp new file mode 100644 index 0000000..28fdc65 --- /dev/null +++ b/code/io/other/rttbDoseStatisticsXMLWriter.cpp @@ -0,0 +1,380 @@ +// ----------------------------------------------------------------------- +// 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 "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbException.h" + +#include "boost/lexical_cast.hpp" + +namespace rttb +{ + namespace io + { + namespace other + { + static const std::string xmlattrTag = ".x"; + static const std::string statisticsTag = "statistics"; + static const std::string columnSeparator = "@"; + + boost::property_tree::ptree writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, + DoseTypeGy aReferenceDose) + { + + using boost::property_tree::ptree; + ptree pt; + + rttb::core::DoseIteratorInterface::DoseIteratorPointer spDoseIterator = + aDoseStatistics->getDoseIterator(); + rttb::core::DoseIteratorInterface::DoseAccessorPointer spDoseAccessor = + spDoseIterator->getDoseAccessor(); + + boost::shared_ptr< std::vector > > myResultPairs = + boost::make_shared< std::vector > >(); + ResultListPointer spMyResultPairs(myResultPairs); + boost::shared_ptr< std::vector > > myResultPairs2 = + boost::make_shared< std::vector > >(); + ResultListPointer spMyResultPairs2(myResultPairs2); + + pt.put(statisticsTag + ".numberOfVoxels", aDoseStatistics->getNumberOfVoxels()); + pt.put(statisticsTag + ".volume", aDoseStatistics->getVx(0)); + pt.put(statisticsTag + ".minimum", aDoseStatistics->getMinimum(spMyResultPairs)); + std::vector >::iterator pairIt = spMyResultPairs->begin(); + int count = 0; + + for (; pairIt != spMyResultPairs->end() && count < 100 ; ++pairIt) //output max. 100 minimum + { + VoxelGridID voxelID = pairIt->second; + pt.add(statisticsTag + ".minimum.voxelGridID", voxelID); + + VoxelGridIndex3D voxelIndex3D; + spDoseAccessor->getGeometricInfo().convert(voxelID, voxelIndex3D); + std::string voxelIndex3DStr; + voxelIndex3DStr = boost::lexical_cast(voxelIndex3D.x()) + "," + + boost::lexical_cast(voxelIndex3D.y()) + "," + + boost::lexical_cast(voxelIndex3D.z()); + pt.add(statisticsTag + ".minimum.voxelIndex3D", voxelIndex3DStr); + + WorldCoordinate3D worldCoor; + spDoseAccessor->getGeometricInfo().indexToWorldCoordinate(voxelIndex3D, worldCoor); + std::string worldCoorStr; + worldCoorStr = boost::lexical_cast(worldCoor.x()) + "," + + boost::lexical_cast(worldCoor.y()) + "," + + boost::lexical_cast(worldCoor.z()); + pt.add(statisticsTag + ".minimum.worldCoordinate", worldCoorStr); + + count++; + } + + pt.put(statisticsTag + ".maximum", aDoseStatistics->getMaximum(spMyResultPairs2)); + std::vector >::iterator pairIt2 = spMyResultPairs2->begin(); + count = 0; + + for (; pairIt2 != spMyResultPairs2->end() && count < 100; ++pairIt2) //output max. 100 maximum + { + VoxelGridID voxelID = pairIt2->second; + pt.add(statisticsTag + ".maximum.voxelGridID", voxelID); + + VoxelGridIndex3D voxelIndex3D; + spDoseAccessor->getGeometricInfo().convert(voxelID, voxelIndex3D); + std::string voxelIndex3DStr; + voxelIndex3DStr = boost::lexical_cast(voxelIndex3D.x()) + "," + + boost::lexical_cast(voxelIndex3D.y()) + "," + + boost::lexical_cast(voxelIndex3D.z()); + pt.add(statisticsTag + ".maximum.voxelIndex3D", voxelIndex3DStr); + + + WorldCoordinate3D worldCoor; + spDoseAccessor->getGeometricInfo().indexToWorldCoordinate(voxelIndex3D, worldCoor); + std::string worldCoorStr; + worldCoorStr = boost::lexical_cast(worldCoor.x()) + "," + + boost::lexical_cast(worldCoor.y()) + "," + + boost::lexical_cast(worldCoor.z()); + pt.add(statisticsTag + ".maximum.worldCoordinate", worldCoorStr); + + count ++; + } + + pt.put(statisticsTag + ".mean", aDoseStatistics->getMean()); + pt.put(statisticsTag + ".standardDeviation", aDoseStatistics->getStdDeviation()); + pt.put(statisticsTag + ".variance", aDoseStatistics->getVariance()); + + /*to do: x should be defined based on the user's feedback*/ + //Dx + boost::property_tree::ptree dxNode1; + boost::property_tree::ptree dxNode2; + boost::property_tree::ptree dxNode3; + boost::property_tree::ptree dxNode4; + boost::property_tree::ptree dxNode5; + boost::property_tree::ptree dxNode6; + + double absoluteVolume = aDoseStatistics->getVx(0); + + dxNode1.put("", aDoseStatistics->getDx(absoluteVolume * 0.02)); + dxNode2.put("", aDoseStatistics->getDx(absoluteVolume * 0.05)); + dxNode3.put("", aDoseStatistics->getDx(absoluteVolume * 0.10)); + dxNode4.put("", aDoseStatistics->getDx(absoluteVolume * 0.90)); + dxNode5.put("", aDoseStatistics->getDx(absoluteVolume * 0.95)); + dxNode6.put("", aDoseStatistics->getDx(absoluteVolume * 0.98)); + + dxNode1.put(xmlattrTag, 2); + dxNode2.put(xmlattrTag, 5); + dxNode3.put(xmlattrTag, 10); + dxNode4.put(xmlattrTag, 90); + dxNode5.put(xmlattrTag, 95); + dxNode6.put(xmlattrTag, 98); + + pt.add_child(statisticsTag + ".Dx", dxNode1); + pt.add_child(statisticsTag + ".Dx", dxNode2); + pt.add_child(statisticsTag + ".Dx", dxNode3); + pt.add_child(statisticsTag + ".Dx", dxNode4); + pt.add_child(statisticsTag + ".Dx", dxNode5); + pt.add_child(statisticsTag + ".Dx", dxNode6); + + + if (aReferenceDose <= 0) + { + throw core::InvalidParameterException("aReferenceDose should be >0!"); + } + + //Vx + boost::property_tree::ptree vxNode1; + boost::property_tree::ptree vxNode2; + boost::property_tree::ptree vxNode3; + boost::property_tree::ptree vxNode4; + boost::property_tree::ptree vxNode5; + boost::property_tree::ptree vxNode6; + + vxNode1.put("", aDoseStatistics->getVx(aReferenceDose * 0.02)); + vxNode2.put("", aDoseStatistics->getVx(aReferenceDose * 0.05)); + vxNode3.put("", aDoseStatistics->getVx(aReferenceDose * 0.10)); + vxNode4.put("", aDoseStatistics->getVx(aReferenceDose * 0.90)); + vxNode5.put("", aDoseStatistics->getVx(aReferenceDose * 0.95)); + vxNode6.put("", aDoseStatistics->getVx(aReferenceDose * 0.98)); + + vxNode1.put(xmlattrTag, 2); + vxNode2.put(xmlattrTag, 5); + vxNode3.put(xmlattrTag, 10); + vxNode4.put(xmlattrTag, 90); + vxNode5.put(xmlattrTag, 95); + vxNode6.put(xmlattrTag, 98); + + pt.add_child(statisticsTag + ".Vx", vxNode1); + pt.add_child(statisticsTag + ".Vx", vxNode2); + pt.add_child(statisticsTag + ".Vx", vxNode3); + pt.add_child(statisticsTag + ".Vx", vxNode4); + pt.add_child(statisticsTag + ".Vx", vxNode5); + pt.add_child(statisticsTag + ".Vx", vxNode6); + + //MOHx + boost::property_tree::ptree mohxNode1; + boost::property_tree::ptree mohxNode2; + boost::property_tree::ptree mohxNode3; + + mohxNode1.put("", aDoseStatistics->getMOHx(absoluteVolume * 0.02)); + mohxNode2.put("", aDoseStatistics->getMOHx(absoluteVolume * 0.05)); + mohxNode3.put("", aDoseStatistics->getMOHx(absoluteVolume * 0.10)); + + mohxNode1.put(xmlattrTag, 2); + mohxNode2.put(xmlattrTag, 5); + mohxNode3.put(xmlattrTag, 10); + + pt.add_child(statisticsTag + ".MOHx", mohxNode1); + pt.add_child(statisticsTag + ".MOHx", mohxNode2); + pt.add_child(statisticsTag + ".MOHx", mohxNode3); + + //MOCx + boost::property_tree::ptree mocxNode1; + boost::property_tree::ptree mocxNode2; + boost::property_tree::ptree mocxNode3; + + mocxNode1.put("", aDoseStatistics->getMOCx(absoluteVolume * 0.02)); + mocxNode2.put("", aDoseStatistics->getMOCx(absoluteVolume * 0.05)); + mocxNode3.put("", aDoseStatistics->getMOCx(absoluteVolume * 0.10)); + + mocxNode1.put(xmlattrTag, 2); + mocxNode2.put(xmlattrTag, 5); + mocxNode3.put(xmlattrTag, 10); + + pt.add_child(statisticsTag + ".MOCx", mocxNode1); + pt.add_child(statisticsTag + ".MOCx", mocxNode2); + pt.add_child(statisticsTag + ".MOCx", mocxNode3); + + + //MaxOHx + boost::property_tree::ptree maxohxNode1; + boost::property_tree::ptree maxohxNode2; + boost::property_tree::ptree maxohxNode3; + + maxohxNode1.put("", aDoseStatistics->getMaxOHx(absoluteVolume * 0.02)); + maxohxNode2.put("", aDoseStatistics->getMaxOHx(absoluteVolume * 0.05)); + maxohxNode3.put("", aDoseStatistics->getMaxOHx(absoluteVolume * 0.10)); + + maxohxNode1.put(xmlattrTag, 2); + maxohxNode2.put(xmlattrTag, 5); + maxohxNode3.put(xmlattrTag, 10); + + pt.add_child(statisticsTag + ".MaxOHx", maxohxNode1); + pt.add_child(statisticsTag + ".MaxOHx", maxohxNode2); + pt.add_child(statisticsTag + ".MaxOHx", maxohxNode3); + + //MinOCx + boost::property_tree::ptree minocxNode1; + boost::property_tree::ptree minocxNode2; + boost::property_tree::ptree minocxNode3; + + minocxNode1.put("", aDoseStatistics->getMinOCx(absoluteVolume * 0.02)); + minocxNode2.put("", aDoseStatistics->getMinOCx(absoluteVolume * 0.05)); + minocxNode3.put("", aDoseStatistics->getMinOCx(absoluteVolume * 0.10)); + + minocxNode1.put(xmlattrTag, 2); + minocxNode2.put(xmlattrTag, 5); + minocxNode3.put(xmlattrTag, 10); + + pt.add_child(statisticsTag + ".MinOCx", minocxNode1); + pt.add_child(statisticsTag + ".MinOCx", minocxNode2); + pt.add_child(statisticsTag + ".MinOCx", minocxNode3); + + return pt; + + } + + void writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, FileNameString aFileName, + DoseTypeGy aReferenceDose) + { + boost::property_tree::ptree pt = writeDoseStatistics(aDoseStatistics, aReferenceDose); + + try + { + boost::property_tree::xml_parser::write_xml(aFileName, pt); + } + catch (boost::property_tree::xml_parser_error& e) + { + throw core::InvalidParameterException("Write xml failed: xml_parser_error!"); + } + } + + XMLString writerDoseStatisticsToString(DoseStatisticsPtr aDoseStatistics, DoseTypeGy aReferenceDose) + { + boost::property_tree::ptree pt = writeDoseStatistics(aDoseStatistics, aReferenceDose); + std::stringstream sstr; + + try + { + boost::property_tree::xml_parser::write_xml(sstr, pt); + } + 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, + DoseTypeGy aReferenceDose) + { + + std::stringstream sstr; + + + rttb::core::DoseIteratorInterface::DoseIteratorPointer spDoseIterator = + aDoseStatistics->getDoseIterator(); + rttb::core::DoseIteratorInterface::DoseAccessorPointer spDoseAccessor = + spDoseIterator->getDoseAccessor(); + + boost::shared_ptr< std::vector > > myResultPairs = + boost::make_shared< std::vector > >(); + ResultListPointer spMyResultPairs(myResultPairs); + boost::shared_ptr< std::vector > > myResultPairs2 = + boost::make_shared< std::vector > >(); + ResultListPointer spMyResultPairs2(myResultPairs2); + + sstr << aDoseStatistics->getVx(0) * 1000 << columnSeparator; // cm3 to mm3 + sstr << aDoseStatistics->getMaximum(spMyResultPairs2) << columnSeparator; + sstr << aDoseStatistics->getMinimum(spMyResultPairs) << columnSeparator; + + sstr << aDoseStatistics->getMean() << columnSeparator; + sstr << aDoseStatistics->getStdDeviation() << columnSeparator; + sstr << aDoseStatistics->getVariance() << columnSeparator; + + /*to do: x should be defined based on the user's feedback*/ + if (aReferenceDose <= 0) + { + throw core::InvalidParameterException("aReferenceDose should be >0!"); + } + + //Vx + + sstr << aDoseStatistics->getVx(aReferenceDose * 0.02) * 1000 << columnSeparator; // cm3 to mm3 + sstr << aDoseStatistics->getVx(aReferenceDose * 0.05) * 1000 << columnSeparator; // cm3 to mm3 + sstr << aDoseStatistics->getVx(aReferenceDose * 0.10) * 1000 << columnSeparator; // cm3 to mm3 + sstr << aDoseStatistics->getVx(aReferenceDose * 0.90) * 1000 << columnSeparator; // cm3 to mm3 + sstr << aDoseStatistics->getVx(aReferenceDose * 0.95) * 1000 << columnSeparator; // cm3 to mm3 + sstr << aDoseStatistics->getVx(aReferenceDose * 0.98) * 1000 << columnSeparator; // cm3 to mm3 + + //Dx + + double absoluteVolume = aDoseStatistics->getVx(0); + + sstr << aDoseStatistics->getDx(absoluteVolume * 0.02) << columnSeparator; + sstr << aDoseStatistics->getDx(absoluteVolume * 0.05) << columnSeparator; + sstr << aDoseStatistics->getDx(absoluteVolume * 0.10) << columnSeparator; + sstr << aDoseStatistics->getDx(absoluteVolume * 0.90) << columnSeparator; + sstr << aDoseStatistics->getDx(absoluteVolume * 0.95) << columnSeparator; + sstr << aDoseStatistics->getDx(absoluteVolume * 0.98) << columnSeparator; + + + //MOHx + sstr << aDoseStatistics->getMOHx(absoluteVolume * 0.02) << columnSeparator; + sstr << aDoseStatistics->getMOHx(absoluteVolume * 0.05) << columnSeparator; + sstr << aDoseStatistics->getMOHx(absoluteVolume * 0.10) << columnSeparator; + + + //MOCx + sstr << aDoseStatistics->getMOCx(absoluteVolume * 0.02) << columnSeparator; + sstr << aDoseStatistics->getMOCx(absoluteVolume * 0.05) << columnSeparator; + sstr << aDoseStatistics->getMOCx(absoluteVolume * 0.10) << columnSeparator; + + + //MaxOHx + sstr << aDoseStatistics->getMaxOHx(absoluteVolume * 0.02) << columnSeparator; + sstr << aDoseStatistics->getMaxOHx(absoluteVolume * 0.05) << columnSeparator; + sstr << aDoseStatistics->getMaxOHx(absoluteVolume * 0.10) << columnSeparator; + + //MinOCx + sstr << aDoseStatistics->getMinOCx(absoluteVolume * 0.02) << columnSeparator; + sstr << aDoseStatistics->getMinOCx(absoluteVolume * 0.05) << columnSeparator; + sstr << aDoseStatistics->getMinOCx(absoluteVolume * 0.10) << columnSeparator; + + + return sstr.str(); + + } + + } + } +} \ No newline at end of file diff --git a/code/io/other/rttbDoseStatisticsXMLWriter.h b/code/io/other/rttbDoseStatisticsXMLWriter.h new file mode 100644 index 0000000..85ea353 --- /dev/null +++ b/code/io/other/rttbDoseStatisticsXMLWriter.h @@ -0,0 +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) +*/ +#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 + */ + boost::property_tree::ptree writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, DoseTypeGy aReferenceDose=100); + + /*! @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 + */ + XMLString writerDoseStatisticsToString(DoseStatisticsPtr aDoseStatistics, DoseTypeGy aReferenceDose=100); + + /*! @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 + */ + void writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, FileNameString aFileName, DoseTypeGy aReferenceDose=100); + + /*! @brief Write statistics to String to generate a table: "Volume mm3@Max@Min@Mean@Std.Dev.@Variance@V2@V5@V10@V90@V95@V98@D2@D5@D10@D90@D95@D98@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 + */ + StatisticsString writerDoseStatisticsToTableString(DoseStatisticsPtr aDoseStatistics, DoseTypeGy aReferenceDose=100); + } + } +} +#endif diff --git a/code/io/rttbDVHWriterInterface.h b/code/io/rttbDVHWriterInterface.h new file mode 100644 index 0000000..ca5e455 --- /dev/null +++ b/code/io/rttbDVHWriterInterface.h @@ -0,0 +1,43 @@ +// ----------------------------------------------------------------------- +// 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 __DVH_WRITER_INTERFACE_H +#define __DVH_WRITER_INTERFACE_H + + +#include "rttbDVH.h" + +namespace rttb{ + namespace io{ + /*! @class DVHWriterInterface + @brief Interface for classes that write DVH data to files. + */ + class DVHWriterInterface + { + typedef core::DVH::DVHPointer DVHPointer; + /*! @brief Write aDvh + */ + public: virtual void writeDVH(DVHPointer aDvh) = 0; + }; + } + +} + +#endif diff --git a/code/io/virtuos/CMakeLists.txt b/code/io/virtuos/CMakeLists.txt new file mode 100644 index 0000000..c1c1c87 --- /dev/null +++ b/code/io/virtuos/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBVirtuosIO DEPENDS RTTBCore PACKAGE_DEPENDS Boost VirtuosIO) \ No newline at end of file diff --git a/code/io/virtuos/files.cmake b/code/io/virtuos/files.cmake new file mode 100644 index 0000000..4d8ebf3 --- /dev/null +++ b/code/io/virtuos/files.cmake @@ -0,0 +1,15 @@ +SET(CPP_FILES + rttbVirtuosCubeinfoDoseAccessorGenerator.cpp + rttbVirtuosDoseAccessor.cpp + rttbVirtuosDoseFileDoseAccessorGenerator.cpp + rttbVirtuosFileStructureSetGenerator.cpp + rttbVirtuosPlanFileDoseAccessorGenerator.cpp + ) + +SET(H_FILES + rttbVirtuosCubeinfoDoseAccessorGenerator.h + rttbVirtuosDoseAccessor.h + rttbVirtuosDoseFileDoseAccessorGenerator.h + rttbVirtuosFileStructureSetGenerator.h + rttbVirtuosPlanFileDoseAccessorGenerator.h + ) diff --git a/code/io/virtuos/rttbVirtuosCubeinfoDoseAccessorGenerator.cpp b/code/io/virtuos/rttbVirtuosCubeinfoDoseAccessorGenerator.cpp new file mode 100644 index 0000000..25901e1 --- /dev/null +++ b/code/io/virtuos/rttbVirtuosCubeinfoDoseAccessorGenerator.cpp @@ -0,0 +1,67 @@ +// ----------------------------------------------------------------------- +// 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 "rttbVirtuosCubeinfoDoseAccessorGenerator.h" +#include "rttbVirtuosDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "rttbIndexOutOfBoundsException.h" + + +//VIRTUOS +#include "pln1file.h" +#include "plt_type.h" +#include "rtp_type.h" + + +namespace rttb{ + namespace io{ + namespace virtuos{ + + + VirtuosCubeinfoDoseAccessorGenerator::~VirtuosCubeinfoDoseAccessorGenerator(){} + + + VirtuosCubeinfoDoseAccessorGenerator::VirtuosCubeinfoDoseAccessorGenerator(Cubeinfo *aPointerOnVirtuosCube, DoseTypeGy normalizationDose, DoseTypeGy prescribedDose): + _pPointerOnVirtuosCube(new Cubeinfo*) + { + //initialize cube pointer + *_pPointerOnVirtuosCube = create_cubeinfo(0); + *_pPointerOnVirtuosCube=aPointerOnVirtuosCube; + _normalizationDose=normalizationDose; + _prescribedDose=prescribedDose; + + } + + VirtuosCubeinfoDoseAccessorGenerator::DoseAccessorPointer VirtuosCubeinfoDoseAccessorGenerator::generateDoseAccessor() { + _doseAccessor=boost::make_shared(*_pPointerOnVirtuosCube, false,_normalizationDose,_prescribedDose); + return _doseAccessor; + } + + + + } + } +} + diff --git a/code/io/virtuos/rttbVirtuosCubeinfoDoseAccessorGenerator.h b/code/io/virtuos/rttbVirtuosCubeinfoDoseAccessorGenerator.h new file mode 100644 index 0000000..1cc97c4 --- /dev/null +++ b/code/io/virtuos/rttbVirtuosCubeinfoDoseAccessorGenerator.h @@ -0,0 +1,76 @@ +// ----------------------------------------------------------------------- +// 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 __VIRTUOS_CUBEINFO_DOSE_ACCESSOR_GENERATOR_H +#define __VIRTUOS_CUBEINFO_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + +#include "ncfile.h" + +namespace rttb{ + namespace io{ + namespace virtuos{ + + /*! @class VirtuosCubeinfoDoseAccessorGenerator + @brief Generate DoseAccessor with Virtuos Cubeinfo + */ + class VirtuosCubeinfoDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + private: + Cubeinfo **_pPointerOnVirtuosCube; + DoseTypeGy _normalizationDose; + DoseTypeGy _prescribedDose; + + + VirtuosCubeinfoDoseAccessorGenerator(); + + protected: + + + + public: + ~VirtuosCubeinfoDoseAccessorGenerator(); + + /*! @brief Constructor. Initialisation with a Cubeinfo*. + @param normalizationDose is defined as (prescribedDose*1000)/maxDoseInGy. Default is 1 Gy. + @param prescribedDose the does that was planned in the reference point in Gy. Default is 1 Gy. + */ + VirtuosCubeinfoDoseAccessorGenerator(Cubeinfo *aPointerOnVirtuosCube, DoseTypeGy normalizationDose = 1, DoseTypeGy prescribedDose = 1); + /*! @brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + */ + DoseAccessorPointer generateDoseAccessor() ; + + }; + } + } +} + +#endif diff --git a/code/io/virtuos/rttbVirtuosDoseAccessor.cpp b/code/io/virtuos/rttbVirtuosDoseAccessor.cpp new file mode 100644 index 0000000..90f6e3f --- /dev/null +++ b/code/io/virtuos/rttbVirtuosDoseAccessor.cpp @@ -0,0 +1,230 @@ +// ----------------------------------------------------------------------- +// 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 "rttbVirtuosDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "rttbIndexOutOfBoundsException.h" + +//VIRTUOS +#include "pln1file.h" +#include "plt_type.h" +#include "rtp_type.h" + + +namespace rttb +{ + namespace io + { + namespace virtuos + { + + VirtuosDoseAccessor::~VirtuosDoseAccessor() + { + if (_freeVirtuosData) + { + this->freeVirtuosData(); + } + } + + VirtuosDoseAccessor::VirtuosDoseAccessor(Cubeinfo* aPointerOnVirtuosCube, bool freeVirtuosData, + DoseTypeGy normalizationDose, DoseTypeGy prescribedDose): + _pPointerOnVirtuosCube(new Cubeinfo*), _freeVirtuosData(freeVirtuosData), _doseGridScaling(0), + _doseUID(""), _doseScalingFactor(0) + { + //initialize cube pointer + *_pPointerOnVirtuosCube = create_cubeinfo(0); + *_pPointerOnVirtuosCube = aPointerOnVirtuosCube; + + + _prescribedDose = prescribedDose; + _normalizationDose = normalizationDose; + + if (_prescribedDose == 0) + { + _prescribedDose = 1; + } + + if (_normalizationDose == 0) + { + _normalizationDose = 1; + } + + _doseScalingFactor = _prescribedDose / _normalizationDose; + std::cout << "Prescribed dose: " << _prescribedDose << std::endl; + std::cout << "Normalization dose: " << _normalizationDose << std::endl; + + //dose import + this->begin(); + + + } + + bool VirtuosDoseAccessor::begin() + { + if (!_pPointerOnVirtuosCube) + { + throw core::NullPointerException(" *_pPointerOnVirtuosCube must not be NULL!"); + } + + assembleGeometricInfo(); + + //load data + if ((*_pPointerOnVirtuosCube)->data_type == 1) + { + this->importPixelData(); + } + else if ((*_pPointerOnVirtuosCube)->data_type == 2) + { + this->importPixelData(); + } + else + { + throw core::InvalidParameterException(" cube has wrong data type!"); + } + } + + template + void VirtuosDoseAccessor::importPixelData() + { + doseData.clear(); + + int dimX = (*_pPointerOnVirtuosCube)->dimx; + int dimY = (*_pPointerOnVirtuosCube)->dimy; + int dimZ = (*_pPointerOnVirtuosCube)->dimz; + + GridVolumeType pixelSpacing = (*_pPointerOnVirtuosCube)->pixdist; + GridVolumeType sliceThickness = (*_pPointerOnVirtuosCube)->slicedist; + + TPixelType** * access_pointer = (TPixelType***)(*_pPointerOnVirtuosCube)->cube_direct_access; + + for (int k = 0; k < dimZ; k++) + { + for (int j = 0; j < dimY; j++) + { + for (int i = 0; i < dimX; i++) + { + TPixelType voxel_value = access_pointer[k][j][i]; + doseData.push_back(voxel_value * _doseScalingFactor); + } + + }//end for j + }//end for k + } + + void VirtuosDoseAccessor::assembleGeometricInfo() + { + if (!_pPointerOnVirtuosCube) + { + throw core::NullPointerException(" _pPointerOnVirtuosCube must not be NULL!"); + } + + _geoInfo.setNumColumns((*_pPointerOnVirtuosCube)->dimx); + + _geoInfo.setNumRows((*_pPointerOnVirtuosCube)->dimy); + + _geoInfo.setNumSlices((*_pPointerOnVirtuosCube)->dimz); + + if (_geoInfo.getNumColumns() == 0 || _geoInfo.getNumRows() == 0 || _geoInfo.getNumSlices() == 0) + { + throw core::InvalidDoseException("Empty Virtuos dose!") ; + } + + OrientationMatrix orientation; + _geoInfo.setOrientationMatrix(orientation); + + WorldCoordinate3D imagePositionPatient; + imagePositionPatient(0) = (*this->_pPointerOnVirtuosCube)->pixdist / 2; + imagePositionPatient(1) = (*this->_pPointerOnVirtuosCube)->pixdist / 2; + + if (!((*this->_pPointerOnVirtuosCube)->pos_list)) + { + throw core::InvalidDoseException("Empty Virtuos dose!") ; + } + + imagePositionPatient(2) = (*this->_pPointerOnVirtuosCube)->pos_list[0].position; + + _geoInfo.setImagePositionPatient(imagePositionPatient); + + SpacingVectorType3D spacingVector; + spacingVector(0) = (*_pPointerOnVirtuosCube)->pixdist; + spacingVector(1) = (*_pPointerOnVirtuosCube)->pixdist; + _geoInfo.setSpacing(spacingVector); + + if (_geoInfo.getPixelSpacingRow() == 0 || _geoInfo.getPixelSpacingColumn() == 0) + { + throw core::InvalidDoseException("Pixel spacing = 0!"); + } + + spacingVector(2) = (*_pPointerOnVirtuosCube)->slicedist; + + if (spacingVector(2) == 0) + { + std::cerr << "sliceThickness == 0! It will be replaced with pixelSpacingRow=" << + _geoInfo.getPixelSpacingRow() + << "!" << std::endl; + spacingVector(2) = spacingVector(0); + } + + _geoInfo.setSpacing(spacingVector); + } + + + DoseTypeGy VirtuosDoseAccessor::getDoseAt(const VoxelGridID aID) const + { + return doseData.at(aID); + } + + DoseTypeGy VirtuosDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + VoxelGridID aVoxelGridID; + + if (_geoInfo.convert(aIndex, aVoxelGridID)) + { + return getDoseAt(aVoxelGridID); + } + else + { + return -1; + } + } + + void VirtuosDoseAccessor::freeVirtuosData() + { + if (*(this->_pPointerOnVirtuosCube) != NULL) + { + closecube((*(this->_pPointerOnVirtuosCube))); + nc_free_cubeinfo((*(this->_pPointerOnVirtuosCube))); + delete this->_pPointerOnVirtuosCube; + + // initialize attributes again + //this->_pPointerOnVirtuosCube = new Cubeinfo*; + //*(this->_pPointerOnVirtuosCube) = create_cubeinfo(0); + } + + } + + } + } +} + diff --git a/code/io/virtuos/rttbVirtuosDoseAccessor.h b/code/io/virtuos/rttbVirtuosDoseAccessor.h new file mode 100644 index 0000000..8876c15 --- /dev/null +++ b/code/io/virtuos/rttbVirtuosDoseAccessor.h @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// 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 __VIRTUOS_DOSE_ACCESSOR_H +#define __VIRTUOS_DOSE_ACCESSOR_H + +#include +#include +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + +#include "ncfile.h" + +namespace rttb +{ + namespace io + { + namespace virtuos + { + + /*! @class VirtuosDoseAccessor + @brief This class gives access to dose information from Virtuos Cubeinfo + */ + class VirtuosDoseAccessor: public core::DoseAccessorInterface + { + private: + Cubeinfo** _pPointerOnVirtuosCube; + + + /*! absolute Gy dose/doseGridScaling*/ + std::vector doseData; + + double _doseGridScaling; + + IDType _doseUID; + + DoseTypeGy _prescribedDose; + DoseTypeGy _normalizationDose; + + DoseTypeGy _doseScalingFactor; + + bool _freeVirtuosData;//if virtuos cube info should be closed + + + /*! close virtuos data cube. + */ + void freeVirtuosData(); + + /*! load actual pixel data from virtuos cube to doseData. TPixelType is usually int but may be float for + special virtuos cubes (data_type = 2). + @pre virtuos cube contains data (dimensions are at least 1) + + */ + template + void importPixelData(); + + protected: + /*! @brief import dose data and relevant geometric information + @throw NullPointerException Thrown if _pPointerOnVirtuosCube is NULL + @throw InvalidDoseException Thrown if one dimension of the virtuos cube is zero + @throw InvalidParameterException Thrown if _pPointerOnVirtuosCube is invalid + */ + bool begin(); + + /*! @brief get all required geometrical data from virtuos cube + */ + void assembleGeometricInfo(); + + + public: + ~VirtuosDoseAccessor(); + + /*! @brief Constructor. Initialisation with a Cubeinfo pointer. + @param normalizationDose is defined as (prescribedDose*1000)/maxDoseInGy. Default is 1 Gy. + @param prescribedDose the does that was planned in the reference point in Gy. Default is 1 Gy. + @param freeVirtuoData If virtuos cube info should be closed, freeVirtuosData should be true. + @throw NullPointerException Thrown if _pPointerOnVirtuosCube is NULL + @throw InvalidDoseException Thrown if one dimension of the virtuos cube is zero + @throw InvalidParameterException Thrown if aPointerOnVirtuosCube is invalid + */ + VirtuosDoseAccessor(Cubeinfo* aPointerOnVirtuosCube, bool freeVirtuosData, + DoseTypeGy normalizationDose = 1, DoseTypeGy prescribedDose = 1); + + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + /*! @return dose value at given grid position. If position is invalid return -1 + */ + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + const IDType getDoseUID() const + { + return _doseUID; + }; + + const DoseTypeGy getPrescribedDose() const + { + return _prescribedDose; + }; + + const DoseTypeGy getNormalizationDose() const + { + return _normalizationDose; + }; + }; + } + } +} + +#endif diff --git a/code/io/virtuos/rttbVirtuosDoseFileDoseAccessorGenerator.cpp b/code/io/virtuos/rttbVirtuosDoseFileDoseAccessorGenerator.cpp new file mode 100644 index 0000000..8a8a979 --- /dev/null +++ b/code/io/virtuos/rttbVirtuosDoseFileDoseAccessorGenerator.cpp @@ -0,0 +1,143 @@ +// ----------------------------------------------------------------------- +// 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 "rttbVirtuosDoseFileDoseAccessorGenerator.h" +#include "rttbVirtuosDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "rttbIndexOutOfBoundsException.h" + +//VIRTUOS +#include "pln1file.h" +#include "plt_type.h" +#include "rtp_type.h" + + +namespace rttb +{ + namespace io + { + namespace virtuos + { + + VirtuosDoseFileDoseAccessorGenerator::~VirtuosDoseFileDoseAccessorGenerator() + { + } + + + VirtuosDoseFileDoseAccessorGenerator::VirtuosDoseFileDoseAccessorGenerator( + const FileNameType aVirtuosDoseFileName, DoseTypeGy normalizationDose, DoseTypeGy prescribedDose): + _pPointerOnVirtuosCube(new Cubeinfo*) + { + _doseFileName = aVirtuosDoseFileName; + _normalizationDose = normalizationDose; + _prescribedDose = prescribedDose; + + + } + + + void VirtuosDoseFileDoseAccessorGenerator::initializeVirtuosCube(FileNameType aVirtuosDoseFileName) + { + if (aVirtuosDoseFileName.empty()) + { + throw core::InvalidParameterException("empty File Name"); + } + + size_t gzPosition = aVirtuosDoseFileName.find(".gz"); + + if (gzPosition != std::string::npos) + { + aVirtuosDoseFileName.erase(gzPosition, aVirtuosDoseFileName.length()); + } + + nc_init_cubeinfo(*_pPointerOnVirtuosCube); + + int opencubeErrorCode = opencube(aVirtuosDoseFileName.c_str() , *_pPointerOnVirtuosCube); + + if (opencubeErrorCode != 0) + { + std::stringstream opencubeErrorCodeAsStringStream; + opencubeErrorCodeAsStringStream << opencubeErrorCode; + throw core::InvalidParameterException(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"); + } + + } + + VirtuosDoseFileDoseAccessorGenerator::DoseAccessorPointer + VirtuosDoseFileDoseAccessorGenerator::generateDoseAccessor() + { + //check if file names are valid + if (!boost::filesystem::exists(_doseFileName)) + { + throw core::InvalidParameterException("invalid dose file name"); + } + + //initialize cube pointer + *_pPointerOnVirtuosCube = create_cubeinfo(0); + + if (!(_doseFileName.empty())) + { + //initialize dose import + + this->initializeVirtuosCube(_doseFileName); + + if (_prescribedDose == 0) + { + _prescribedDose = 1; + } + + if (_normalizationDose == 0) + { + _normalizationDose = 1; + } + + _doseAccessor = boost::make_shared(*_pPointerOnVirtuosCube, true, + _normalizationDose, _prescribedDose); + + + } + else + { + throw core::InvalidParameterException("empty dose file name"); + } + + return _doseAccessor; + } + + + } + } +} + diff --git a/code/io/virtuos/rttbVirtuosDoseFileDoseAccessorGenerator.h b/code/io/virtuos/rttbVirtuosDoseFileDoseAccessorGenerator.h new file mode 100644 index 0000000..10c82cf --- /dev/null +++ b/code/io/virtuos/rttbVirtuosDoseFileDoseAccessorGenerator.h @@ -0,0 +1,92 @@ +// ----------------------------------------------------------------------- +// 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 __VIRTUOS_DOSE_FILE_DOSE_ACCESSOR_GENERATOR_H +#define __VIRTUOS_DOSE_FILE_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + +#include "ncfile.h" + +namespace rttb +{ + namespace io + { + namespace virtuos + { + + /*! @class VirtuosDoseFileDoseAccessorGenerator + @brief Load dose data from a Virtuos dose file and generate DoseAccessor. + @warning Manual dose normalization is done as no virtuos plan file is given! I.e., the DoseAccessor returns pixels having no physical unit (!= Gy) in case of wrong values. It's the responsibility of the user to specify the correct values. Consider using VirtuosPlanFileDoseAccessorGenerator instead! + */ + class VirtuosDoseFileDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + private: + FileNameType _doseFileName; + DoseTypeGy _normalizationDose; + DoseTypeGy _prescribedDose; + Cubeinfo **_pPointerOnVirtuosCube; + + /*! prepare virtuos dose cube for data import. Actual data import is performed in importPixelData() + and assembleGeometricInfo(). + @pre filename needs to point to an existing file. + @throw InvalidParameterException Thrown if aVirtuosDoseFileName is invalid or if virtuos cube can not be read + */ + void initializeVirtuosCube(FileNameType aVirtuosDoseFileName); + + VirtuosDoseFileDoseAccessorGenerator(); + + + protected: + + + + public: + + ~VirtuosDoseFileDoseAccessorGenerator(); + + /*! @brief Constructor. Initialisation with a Virtuos-RT dose file with name aVirtuosDoseFileName. + @param normalizationDose is defined as (prescribedDose*1000)/maxDoseInGy. + @param prescribedDose the does that was planned in the reference point in Gy. + */ + VirtuosDoseFileDoseAccessorGenerator(const FileNameType aVirtuosDoseFileName, + DoseTypeGy normalizationDose, DoseTypeGy prescribedDose); + + /*@brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @throw InvalidParameterException Thrown if one of the file names is empty. + */ + DoseAccessorPointer generateDoseAccessor() ; + + }; + } + } +} + +#endif diff --git a/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp b/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp new file mode 100644 index 0000000..e71b8b4 --- /dev/null +++ b/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.cpp @@ -0,0 +1,200 @@ +// ----------------------------------------------------------------------- +// 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"); + 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); + //@todo soll wohl trotz Fehler weiter laufen (vgl. alte Implementierung) + if(errorcode!=0) { + //throw std::string ("Virtuos Routines unable to create VOI Model! "); + std::cerr << "voi_create_voi_model_dirolab error: error code "<_patient); + + //@todo soll wohl trotz Fehler weiter laufen (vgl. alte Implementierung) + 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 "<_patient->getNumberOfVois(); + + float firstSliceInFrame=(*_pPointerOnVirtuosCube)->pos_list[0].position; + + double sliceThickness=(*_pPointerOnVirtuosCube)->slicedist; + + float lastSliceInFrame=((*_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 + + int errorcode = 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 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"); + 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<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/io/virtuos/rttbVirtuosFileStructureSetGenerator.h b/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.h new file mode 100644 index 0000000..961bc20 --- /dev/null +++ b/code/io/virtuos/rttbVirtuosFileStructureSetGenerator.h @@ -0,0 +1,105 @@ +// ----------------------------------------------------------------------- +// 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) +*/ + +/* Changes in Architecture: +The DICOM specific classes will be removed and transfered to the corresponding IO classes. +This class should only provide general structure functionality. +*/ + +#ifndef __VIRTUOS_FILE_STRUCTURE_SET_GENERATOR_H +#define __VIRTUOS_FILE_STRUCTURE_SET_GENERATOR_H + +#include + +#include +#include + +// Virtuos includes +#include "ncfile.h" +#include "voi_man.hxx" + +#include "rttbBaseType.h" +#include "rttbStrVectorStructureSetGenerator.h" +#include "rttbStructure.h" + + +namespace rttb{ + namespace io{ + namespace virtuos{ + + /*! @class VirtuosFileStructureSetGenerator + @brief Genereate a structure set from a corresponding Virtuos data. + */ + class VirtuosFileStructureSetGenerator: public core::StrVectorStructureSetGenerator + { + public: + typedef core::StructureSet::StructTypePointer StructTypePointer; + typedef StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + + private: + // Pointer to Virtuos image data cube + Cubeinfo **_pPointerOnVirtuosCube; + + //Virtuos Patient Pointer + VoiModel *_patient; + + IDType _UID; + FileNameType _VDXFileName; + FileNameType _CTXFileName; + + + /*! open virtuos ctx cube + @throw InvalidParameterException if ctx cube could not be opened or dose dimensions are invalid + */ + void initializeVirtuosCube(FileNameType aVirtuosCTXFileName); + + /*! import virtuos vdx contours from file + @throw InvalidParameterException if one of the file names is empty. + @throw InvalidParameterException if aVirtuosVDXFileName, does not end with '.vdx'. + */ + void importStructureSet(FileNameType aVirtuosVDXFileName, FileNameType aVirtuosCTXFileName); + + + public: + /*! @brief Constructor + @param aVirtuosVDXFileName a Virtuos structure set .vdx file name + @param aVirtuosCTXFileName a Virtuos CT .ctx file name + + */ + VirtuosFileStructureSetGenerator(FileNameType aVirtuosVDXFileName, FileNameType aVirtuosCTXFileName); + + /*! @brief Destructor + Free Virtuos Cubeinfo. + */ + ~VirtuosFileStructureSetGenerator(); + + /*! @brief generate structure set + @return return shared pointer of StructureSet + @exception InvalidParameterException Thrown if loadFile and read failed + @exception InvalidParameterException throw if structure data invalid. + */ + StructureSetPointer generateStructureSet(); + }; + } + } +} + +#endif diff --git a/code/io/virtuos/rttbVirtuosPlanFileDoseAccessorGenerator.cpp b/code/io/virtuos/rttbVirtuosPlanFileDoseAccessorGenerator.cpp new file mode 100644 index 0000000..5dd0aad --- /dev/null +++ b/code/io/virtuos/rttbVirtuosPlanFileDoseAccessorGenerator.cpp @@ -0,0 +1,172 @@ +// ----------------------------------------------------------------------- +// 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 "rttbVirtuosPlanFileDoseAccessorGenerator.h" +#include "rttbVirtuosDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "rttbIndexOutOfBoundsException.h" + +//VIRTUOS +#include "pln1file.h" +#include "plt_type.h" +#include "rtp_type.h" + + +namespace rttb +{ + namespace io + { + namespace virtuos + { + + VirtuosPlanFileDoseAccessorGenerator::~VirtuosPlanFileDoseAccessorGenerator() + { + } + + VirtuosPlanFileDoseAccessorGenerator::VirtuosPlanFileDoseAccessorGenerator( + const FileNameType aVirtuosDoseFileName, const FileNameType aVirtuosPlanFileName): + _pPointerOnVirtuosCube(new Cubeinfo*) + { + _doseFileName = aVirtuosDoseFileName; + _planFileName = aVirtuosPlanFileName; + + } + + + + void VirtuosPlanFileDoseAccessorGenerator::initializeVirtuosCube(FileNameType aVirtuosDoseFileName) + { + if (aVirtuosDoseFileName.empty()) + { + throw core::InvalidParameterException("empty File Name"); + } + + size_t gzPosition = aVirtuosDoseFileName.find(".gz"); + + if (gzPosition != std::string::npos) + { + aVirtuosDoseFileName.erase(gzPosition, aVirtuosDoseFileName.length()); + } + + nc_init_cubeinfo(*_pPointerOnVirtuosCube); + + int opencubeErrorCode = opencube(aVirtuosDoseFileName.c_str() , *_pPointerOnVirtuosCube); + + if (opencubeErrorCode != 0) + { + std::stringstream opencubeErrorCodeAsStringStream; + opencubeErrorCodeAsStringStream << opencubeErrorCode; + throw core::InvalidParameterException(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"); + } + + } + + VirtuosPlanFileDoseAccessorGenerator::DoseAccessorPointer + VirtuosPlanFileDoseAccessorGenerator::generateDoseAccessor() + { + //initialize cube pointer + *_pPointerOnVirtuosCube = create_cubeinfo(0); + + //check if file names are valid + if (!boost::filesystem::exists(_doseFileName)) + { + throw core::InvalidParameterException("invalid dose file name"); + } + + if (!boost::filesystem::exists(_planFileName)) + { + throw core::InvalidParameterException("invalid plan file name"); + } + + if (!(_doseFileName.empty())) + { + //import relevant plan data + if (_planFileName.empty()) + { + throw core::InvalidParameterException("empty plan file name"); + } + + size_t plnPosition = _planFileName.find(".pln"); + + if (plnPosition == std::string::npos) + { + throw core::InvalidParameterException("plan file name invalid!"); + } + + RTPType virtuosPlan; + rtp_plan_init(&virtuosPlan, RTP_S_DEVICE_COORD_IEC); + rtp_plan_clear(&virtuosPlan, FALSE, RTP_S_DEVICE_COORD_IEC); + int error = rtp_plan_read(const_cast(_planFileName.c_str()), &virtuosPlan); + + if (error) + { + std::stringstream expSStr; + expSStr << "Read virtuos plan: rtp_plan_read returned error code "; + expSStr << error; + throw core::InvalidParameterException(expSStr.str()); + } + + DoseTypeGy prescribedDose = virtuosPlan.dose_info.prescr_dose; + DoseTypeGy normalizationDose = virtuosPlan.dose_info.norm_dose; + + if (prescribedDose == 0) + { + prescribedDose = 1; + } + + if (normalizationDose == 0) + { + normalizationDose = 1; + } + + + this->initializeVirtuosCube(_doseFileName); + + _doseAccessor = boost::make_shared(*_pPointerOnVirtuosCube, true, + normalizationDose, prescribedDose); + + } + else + { + throw core::InvalidParameterException("empty dose file name"); + } + + return _doseAccessor; + } + + + } + } +} + diff --git a/code/io/virtuos/rttbVirtuosPlanFileDoseAccessorGenerator.h b/code/io/virtuos/rttbVirtuosPlanFileDoseAccessorGenerator.h new file mode 100644 index 0000000..e05f79c --- /dev/null +++ b/code/io/virtuos/rttbVirtuosPlanFileDoseAccessorGenerator.h @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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 __VIRTUOS_PLAN_FILE_DOSE_ACCESSOR_GENERATOR_H +#define __VIRTUOS_PLAN_FILE_DOSE_ACCESSOR_GENERATOR_H + +#include +#include +#include + +#include "rttbDoseAccessorGeneratorBase.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + +#include "ncfile.h" + +namespace rttb +{ + namespace io + { + namespace virtuos + { + + /*! @class VirtuosPlanFileDoseAccessorGenerator + @brief Load dose data from a Virtuos dose file and a Virtuos plan file, generate DoseAccessor. + @detail Through the use of the plan file, the dose file can be normalized, i.e. the DoseAccessor returns values in Gy. + */ + class VirtuosPlanFileDoseAccessorGenerator: public core::DoseAccessorGeneratorBase + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + + private: + FileNameType _doseFileName; + FileNameType _planFileName; + + Cubeinfo **_pPointerOnVirtuosCube; + + /*! prepare virtuos dose cube for data import. Actual data import is performed in importPixelData() + and assembleGeometricInfo(). + @pre filename needs to point to an existing file. + @throw InvalidParameterException Thrown if aVirtuosDoseFileName is invalid or if virtuos cube can not be read + */ + void initializeVirtuosCube(FileNameType aVirtuosDoseFileName); + + VirtuosPlanFileDoseAccessorGenerator(); + + protected: + + + + public: + ~VirtuosPlanFileDoseAccessorGenerator(); + /*! @brief Constructor. Initialisation with a Virtuos dose file and a Virtuos plan file. Information for dose scaling, + etc. is taken from the corresponding plan file aVirtuosPlanFileName. + + */ + VirtuosPlanFileDoseAccessorGenerator(const FileNameType aVirtuosDoseFileName, + const FileNameType aVirtuosPlanFileName); + + /*@brief Generate DoseAccessor + @return Return shared pointer of DoseAccessor. + @throw InvalidParameterException Thrown if one of the file names is empty. + @throw InvalidParameterException Thrown if plan file or virtuos cube could not be read. + */ + DoseAccessorPointer generateDoseAccessor() ; + + }; + } + } +} + +#endif diff --git a/code/masks/CMakeLists.txt b/code/masks/CMakeLists.txt new file mode 100644 index 0000000..024c513 --- /dev/null +++ b/code/masks/CMakeLists.txt @@ -0,0 +1 @@ +RTTB_CREATE_MODULE(RTTBMasks DEPENDS RTTBCore PACKAGE_DEPENDS Boost DCMTK) \ No newline at end of file diff --git a/code/masks/DoseIteratorInterface_LEGACY.cpp b/code/masks/DoseIteratorInterface_LEGACY.cpp new file mode 100644 index 0000000..8b52e02 --- /dev/null +++ b/code/masks/DoseIteratorInterface_LEGACY.cpp @@ -0,0 +1,84 @@ +// ----------------------------------------------------------------------- +// 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 "DoseIteratorInterface_LEGACY.h" +using namespace std; + +#include +#include +#include +#include +#include + +namespace rttb{ + + namespace masks{ + namespace legacy{ + + IDType DoseIteratorInterface::getDoseUID(){ + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id=generator(); + + std::stringstream ss; + ss << id; + + _doseUID=ss.str(); + return _doseUID; + + } + + IDType DoseIteratorInterface::getPatientUID(){ + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id=generator(); + + std::stringstream ss; + ss << id; + + _patientUID=ss.str(); + return _patientUID; + + } + + int DoseIteratorInterface::size(){ + this->start(); + int size=0; + while(this->hasNextVoxel()){ + this->next(); + size++; + } + return size; + } + } + + } +} + + + + + + + + + + diff --git a/code/masks/DoseIteratorInterface_LEGACY.h b/code/masks/DoseIteratorInterface_LEGACY.h new file mode 100644 index 0000000..17d9498 --- /dev/null +++ b/code/masks/DoseIteratorInterface_LEGACY.h @@ -0,0 +1,115 @@ +// ----------------------------------------------------------------------- +// 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_ITERATOR_INTERFACE_H +#define __DOSE_ITERATOR_INTERFACE_H + + +#include +#include +#include + +#include "DoseVoxel_LEGACY.h" +#include "rttbPhysicalInfo.h" + + +namespace rttb{ + + namespace masks + { + namespace legacy { + /*! @class DoseIteratorInterface + * @brief This class represents the dose iterator interface. + */ + class DoseIteratorInterface: public core::PhysicalInfo + { + protected: + /*! @brief Dose voxel index (x,y,z)*/ + VoxelGridIndex3D _doseIndex; + + /*! @brief Dose UID*/ + IDType _doseUID; + + /*! @brief Patient UID*/ + IDType _patientUID; + + /*! @brief Current DoseVoxel*/ + DoseVoxel _currentVoxel; + + /*! @brief Read dose and structure information + * It will be called in constructor or if dose or structure is resetted + */ + virtual bool begin() = 0; + + + public: + /*! @brief Set the position before the first index. It must be called before hasNextVoxel() and next()! + */ + virtual void start()=0; + + /*! @brief Test if next voxel exists + * @pre start() must be called before it is called! + */ + virtual bool hasNextVoxel() const = 0; + + /*! @brief Return dose value (in Gy) of the next index. After next() is called, current index of this iterator is at the next index. + * @pre hasNextVoxel() must =true before it is called! + */ + virtual DoseTypeGy next() = 0; + + //RALF: man sollte zumindest so ein interface auch einbauen um einen viruellen Call zu sparen + //virtual bool next(DoseTypeGy& nextValue) = 0; + + /*! @brief Return volume of one voxel (in cm3)*/ + virtual DoseVoxelVolumeType getDeltaV() const = 0; + + /*! @brief Return geometric information*/ + virtual core::GeometricInfo getGeometricInfo() const=0; + + /*! @brief If masked dose iterator, return the voxel proportion inside a given structure, value 0~1; Otherwise, 1*/ + virtual double getCurrentVoxelProportion() const=0; + + /*! @brief Get dose uid. If there exists uuid for the dose, you should rewrite the method; otherwise, a uid will be + * randomly generated using boost::uuid library. + */ + IDType getDoseUID(); + + /*! @brief Get patient uid. If there exists patient uuid for the dose, you should rewrite the method; otherwise, a uid will be + * randomly generated using boost::uuid library. + */ + IDType getPatientUID(); + + /*! @brief Return the current RTVoxel Object*/ + virtual const DoseVoxel& getCurrentRTVoxel()=0; + + /*! @brief Return the size of the dose iterator*/ + virtual int size(); + + + + + + + }; + } + } +} + +#endif diff --git a/code/masks/DoseIterator_LEGACY.cpp b/code/masks/DoseIterator_LEGACY.cpp new file mode 100644 index 0000000..e4532b5 --- /dev/null +++ b/code/masks/DoseIterator_LEGACY.cpp @@ -0,0 +1,163 @@ +// ----------------------------------------------------------------------- +// 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 "DoseIterator_LEGACY.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbIndexOutOfBoundsException.h" + + +using namespace std; + + +namespace rttb{ + + namespace masks { +namespace legacy + + { + + + DoseIterator::~DoseIterator() + { + + } + + DoseIterator::DoseIterator(core::GeometricInfo& aGeoInfo, DRTDoseIOD* doseIOD, DRTDoseIOD* maskIOD): doseData (aGeoInfo.getNumberOfVoxels(), 0){ + this->count=-1; + + _dose = doseIOD; + _mask = maskIOD; + + setDoseInfo(aGeoInfo); + } + + void DoseIterator::start() { + this->count=-1; + } + + bool DoseIterator::setDoseInfo(core::GeometricInfo& aGeoInfo){ + /* get Columns (0028,0011) of the dicom dose _dose*/ + column = aGeoInfo.getNumColumns(); + + /*! get Rows (0028,0010) of the dicom dose _dose*/ + row = aGeoInfo.getNumRows(); + + /* get NumberOfFrames (0028,0008) of the dicom dose _dose*/ + numberOfFrames=aGeoInfo.getNumSlices(); + //std::cout << "numberOfFrames: "<< numberOfFrames << std::endl; + if(column==0 || row==0 || numberOfFrames==0){ + throw rttb::core::InvalidDoseException("Empty dicom dose!"); + } + + /* get DoseGridScaling (3004,000e) of the dicom dose _dose*/ + OFString doseGridScalingStr; + this->_dose->getDoseGridScaling(doseGridScalingStr); + this->doseGridScaling=1; + + imagePositionPatient = aGeoInfo.getImagePositionPatient(); + imageOrientationRow = aGeoInfo.getImageOrientationRow(); + imageOrientationColumn = aGeoInfo.getImageOrientationColumn(); + + /* get PixelSpacing (0028,0030) of the dicom dose _dose*/ + pixelSpacingRow=aGeoInfo.getPixelSpacingRow(); + pixelSpacingColumn=aGeoInfo.getPixelSpacingColumn(); + + /* get SliceThickness (0018,0050) of the dicom dose _dose*/ + sliceThickness=aGeoInfo.getSliceThickness(); + + return true; + + } + + + + DoseTypeGy DoseIterator::next(){ + + int size=this->pixelDataIndexInStructure.size(); + if(count<(size-1)){ + count++; + DoseTypeGy dose; + int i=pixelDataIndexInStructure[count]; + dose=doseData.at(i)*this->doseGridScaling; + + return dose; + } + else{ + throw core::IndexOutOfBoundsException("hasNextVoxel()==false!"); + + } + + } + + bool DoseIterator::hasNextVoxel() const{ + + int size=this->pixelDataIndexInStructure.size(); + if(count<(size-1)){ + return true; + } + else + return false; + + } + + DoseVoxelVolumeType DoseIterator::getDeltaV() const{ + return this->pixelSpacingColumn*this->pixelSpacingRow*this->sliceThickness/1000; + } + + core::GeometricInfo DoseIterator::getGeometricInfo() const{ + core::GeometricInfo ginfo; + ginfo.setNumColumns(this->column); + ginfo.setNumRows(this->row); + OrientationMatrix orientation(0); + orientation(0,0) = this->imageOrientationRow.x(); + orientation(1,0) = this->imageOrientationRow.y(); + orientation(2,0) = this->imageOrientationRow.z(); + orientation(0,1) = this->imageOrientationColumn.x(); + orientation(1,1) = this->imageOrientationColumn.y(); + orientation(2,1) = this->imageOrientationColumn.z(); + WorldCoordinate3D ortho = this->imageOrientationRow.cross(this->imageOrientationColumn); + orientation(0,2) = ortho.x(); + orientation(1,2) = ortho.y(); + orientation(2,2) = ortho.z(); + ginfo.setOrientationMatrix(orientation); + ginfo.setImagePositionPatient(this->imagePositionPatient); + ginfo.setNumSlices(this->numberOfFrames); + ginfo.setPixelSpacingColumn(this->pixelSpacingColumn); + ginfo.setPixelSpacingRow(this->pixelSpacingRow); + ginfo.setSliceThickness(this->sliceThickness); + return ginfo; + } + + double DoseIterator::getCurrentVoxelProportion() const{ + return 1; + } + + const DoseVoxel& DoseIterator::getCurrentRTVoxel(){ + return _currentVoxel; + } + + + } + + } +} + diff --git a/code/masks/DoseIterator_LEGACY.h b/code/masks/DoseIterator_LEGACY.h new file mode 100644 index 0000000..82f976a --- /dev/null +++ b/code/masks/DoseIterator_LEGACY.h @@ -0,0 +1,156 @@ +// ----------------------------------------------------------------------- +// 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 __DICOM_DOSE_ITERATOR_USE_MASK_H +#define __DICOM_DOSE_ITERATOR_USE_MASK_H + +#include +#include +#include + + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/dcmrt/drtdose.h" + +#include "DoseIteratorInterface_LEGACY.h" +#include "DoseVoxel_LEGACY.h" +#include "rttbGeometricInfo.h" + + +namespace rttb{ + namespace masks{ +namespace legacy + { + + /*! @class DoseIterator + * @brief This class represents the iterator of the dose voxels in a DRTDoseIOD Object within the >0 region of a image mask + */ + + class DoseIterator: public DoseIteratorInterface + { + + + private: + /*! DICOM-RT dose pointer*/ + DRTDoseIOD* _dose; + + /*! DICOM-RT mask pointer. It must have the same image properties as _dose. The structure is masked with 1.*/ + DRTDoseIOD* _mask; + + + + /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ + std::vector doseData; + /*! Columns (0028,0011) of the dicom dose _dose*/ + Uint16 column; + /*! Rows (0028,0010) of the dicom dose _dose*/ + Uint16 row; + /*! NumberOfFrames (0028,0008) of the dicom dose _dose*/ + Uint16 numberOfFrames; + /*! DoseGridScaling (3004,000e) of the dicom dose _dose*/ + double doseGridScaling; + /*! ImagePositionPatient (0020,0032) of the dicom dose _dose*/ + WorldCoordinate3D imagePositionPatient; + /*! ImageOrientationPatient (0020,0037) of the dicom dose _dose*/ + ImageOrientation imageOrientationRow; + ImageOrientation imageOrientationColumn; + /*! PixelSpacing (0028,0030) of the dicom dose _dose*/ + double pixelSpacingRow; + double pixelSpacingColumn; + /*! SliceThickness (0018,0050) of the dicom dose _dose*/ + double sliceThickness; + + + /*! The current position in pixelDataIndexInStructure*/ + int count; + + /*! The vector stores all indices with pixel data >0 in the mask*/ + std::vector pixelDataIndexInStructure; + + + + + /*! @brief Get information from dose distribution:column, row, numberOfFramesdose, + * GridScaling, imageOrientation, pixelSpacing, getSliceThickness() and absolute dose value in each voxel + */ + + bool setDoseInfo(core::GeometricInfo& aGeoInfo); + + + + + + + protected: + + /*! @brief Traverse dose distribution, get the dose voxels in the structure: doseVoxelInStructure. + * It will be called in constructor or if structure is resetted. + * @exception RTTBNullPointerException Thrown if _dose or _structure is NULL + * @exception RTTBInvalidDoseException Thrown if _dose is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + * @exception RTTBDcmrtException Throw if dcmrt error + */ + bool begin() { start(); return true;}; + + + public: + /*! @brief Destructor*/ + virtual ~DoseIterator(); + + /*! @brief Standard constructor + * @exception RTTBNullPointerException Thrown if _dose or _mask is NULL + * @exception RTTBInvalidDoseException Thrown if _dose is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 + * @exception RTTBDcmrtException Throw if dcmrt error + */ + DoseIterator(core::GeometricInfo& aGeoInfo, DRTDoseIOD* doseIOD, DRTDoseIOD* maskIOD); + + /*! @brief Set the position before the first index. + * It must be called before hasNextVoxel() and next()! + */ + void start(); + + + /*! @brief Test if next voxel existiert + * @pre start() must be called before it is called! + */ + bool hasNextVoxel() const; + + /*! @brief Return dose value (in Gy) of the next index + * @pre hasNextVoxel() must =true before it is called! + * @exception RTTBIndexOutOfBoundsException Thrown if hasNextVoxel()==false + */ + DoseTypeGy next(); + + /*! @brief Volume of one voxel (in cm3)*/ + DoseVoxelVolumeType getDeltaV() const; + + /*! @brief Return geometric information*/ + core::GeometricInfo getGeometricInfo() const; + + /*! @brief Return 1? To be modified...*/ + double getCurrentVoxelProportion() const; + + /*! @brief Return the current RTVoxel Object*/ + const DoseVoxel& getCurrentRTVoxel(); + }; +} + } +} + +#endif diff --git a/code/masks/DoseVoxel_LEGACY.cpp b/code/masks/DoseVoxel_LEGACY.cpp new file mode 100644 index 0000000..628255e --- /dev/null +++ b/code/masks/DoseVoxel_LEGACY.cpp @@ -0,0 +1,867 @@ +// ----------------------------------------------------------------------- +// 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 "DoseVoxel_LEGACY.h" +#include "rttbInvalidParameterException.h" +#include "rttbContour_LEGACY.h" + +using namespace std; + +namespace rttb{ + namespace masks{ +namespace legacy{ + + DoseVoxel::DoseVoxel(){ + //default 1 + _proportionInStr=1; + } + + DoseVoxel::DoseVoxel(const VoxelGridIndex3D &aDoseIndex, const core::GeometricInfo* aGeometricInfo){ + if(aDoseIndex(0)<0 || aDoseIndex(1)<0 || aDoseIndex(2)<0){ + std::cout << aDoseIndex.toString()<getPixelSpacingRow()<=0 || aGeometricInfo->getPixelSpacingColumn()<=0 || aGeometricInfo->getSliceThickness() <=0) + throw rttb::core::InvalidParameterException("Pixel spacing and slice thickness must not be zero! "); + else{ + _voxelIndex3D.x=aDoseIndex(0); + _voxelIndex3D.y=aDoseIndex(1); + _voxelIndex3D.z=aDoseIndex(2); + _geoInfo=aGeometricInfo; + + WorldCoordinate3D currentWorld(0); + aGeometricInfo->indexToWorldCoordinate(aDoseIndex,currentWorld); + _x=currentWorld(0); + _y=currentWorld(1); + _z=currentWorld(2); + //std::cout << "zIndex:"<getPixelSpacingRow(); + _deltaY=aGeometricInfo->getPixelSpacingColumn(); + _deltaZ=aGeometricInfo->getSliceThickness(); + } + + //default 1 + _proportionInStr=1; + } + + DoseVoxel::DoseVoxel(const LegacyDoseVoxelIndex3D &aDoseIndex, const rttb::core::GeometricInfo* aGeometricInfo){ + if(aDoseIndex.x<0 || aDoseIndex.y<0 || aDoseIndex.z<0){ + std::cout << aDoseIndex.toString()<getPixelSpacingRow()<=0 || aGeometricInfo->getPixelSpacingColumn()<=0 || aGeometricInfo->getSliceThickness() <=0) + throw rttb::core::InvalidParameterException("Pixel spacing and slice thickness must not be zero! "); + else{ + _voxelIndex3D=aDoseIndex; + _geoInfo=aGeometricInfo; + + // old + //_x=aGeometricInfo->imageOrientationRow.x*aGeometricInfo->pixelSpacingRow*(aDoseIndex.x)+aGeometricInfo->imageOrientationColumn.x*aGeometricInfo->pixelSpacingColumn*(aDoseIndex.y)+aGeometricInfo->imagePositionPatient.x; + //_y=aGeometricInfo->imageOrientationRow.y*aGeometricInfo->pixelSpacingRow*(aDoseIndex.x)+aGeometricInfo->imageOrientationColumn.y*aGeometricInfo->pixelSpacingColumn*(aDoseIndex.y)+aGeometricInfo->imagePositionPatient.y; + //_z=aGeometricInfo->imagePositionPatient.z + aGeometricInfo->sliceThickness *(aDoseIndex.z); + + //Martina + WorldCoordinate3D currentWorld(0); + VoxelGridIndex3D currentIndex(aDoseIndex.x,aDoseIndex.y,aDoseIndex.z); + aGeometricInfo->indexToWorldCoordinate(currentIndex,currentWorld); + _x=currentWorld(0); + _y=currentWorld(1); + _z=currentWorld(2); + + //Lanlan + //_x=aGeometricInfo->imageOrientationRow.x*aGeometricInfo->pixelSpacingRow*aDoseIndex.x+aGeometricInfo->imageOrientationColumn.x*aGeometricInfo->pixelSpacingColumn*aDoseIndex.y+aGeometricInfo->imagePositionPatient.x-0.5*aGeometricInfo->pixelSpacingRow; + //_y=aGeometricInfo->imageOrientationRow.y*aGeometricInfo->pixelSpacingRow*aDoseIndex.x+aGeometricInfo->imageOrientationColumn.y*aGeometricInfo->pixelSpacingColumn*aDoseIndex.y+aGeometricInfo->imagePositionPatient.y-0.5*aGeometricInfo->pixelSpacingColumn; + + //_z=aGeometricInfo->imagePositionPatient.z + aGeometricInfo->sliceThickness *(aDoseIndex.z)-0.5*aGeometricInfo->sliceThickness; + + //std::cout << "zIndex:"<getPixelSpacingRow(); + _deltaY=aGeometricInfo->getPixelSpacingColumn(); + _deltaZ=aGeometricInfo->getSliceThickness(); + + } + + //default 1 + _proportionInStr=1; + } + + + bool DoseVoxel::operator==(const DoseVoxel& doseVoxel) const{ + return (_x==doseVoxel._x)&& (_y==doseVoxel._y)&& (_z==doseVoxel._z) + && (_deltaX==doseVoxel._deltaX) && (_deltaY==doseVoxel._deltaY)&& (_deltaZ==_deltaZ); + } + + bool DoseVoxel::operator<(const DoseVoxel& doseVoxel) const{ + WorldCoordinate3D leftP=doseVoxel.getLeftUpperPoint(); + WorldCoordinate3D deltaP=doseVoxel.getDelta(); + if(_zindexToWorldCoordinate(aDoseIndex , aWorldCoordinate)){ + _x = aWorldCoordinate(0); + _y = aWorldCoordinate(1); + _z = aWorldCoordinate(2); + } + else{ + //something went wrong! + } + + /* //Martina + _x=aGeometricInfo->getImageOrientationRow()(0)*aGeometricInfo->getPixelSpacingRow()*(aDoseIndex(0)-0.5)+aGeometricInfo->getImageOrientationColumn()(0)*aGeometricInfo->getPixelSpacingColumn()*(aDoseIndex(1)-0.5)+aGeometricInfo->getImagePositionPatient()(0); + _y=aGeometricInfo->getImageOrientationRow()(1)*aGeometricInfo->getPixelSpacingRow()*(aDoseIndex(0)-0.5)+aGeometricInfo->getImageOrientationColumn()(1)*aGeometricInfo->getPixelSpacingColumn()*(aDoseIndex(1)-0.5)+aGeometricInfo->getImagePositionPatient()(1); + _z=aGeometricInfo->getImagePositionPatient()(2) + aGeometricInfo->getSliceThickness() *(aDoseIndex(2)-0.5); +*/ + //Lanlan + //_x=aGeometricInfo->getImageOrientationRow()(0)*aGeometricInfo->getPixelSpacingRow()*aDoseIndex(0)+aGeometricInfo->getImageOrientationColumn()(0)*aGeometricInfo->getPixelSpacingColumn()*aDoseIndex(1)+aGeometricInfo->getImagePositionPatient()(0)-0.5*aGeometricInfo->getPixelSpacingRow(); + //_y=aGeometricInfo->getImageOrientationRow()(1)*aGeometricInfo->getPixelSpacingRow()*aDoseIndex(0)+aGeometricInfo->getImageOrientationColumn()(1)*aGeometricInfo->getPixelSpacingColumn()*aDoseIndex(1)+aGeometricInfo->getImagePositionPatient()(1)-0.5*aGeometricInfo->getPixelSpacingColumn(); + + //_z=aGeometricInfo->getImagePositionPatient()(2) + aGeometricInfo->getSliceThickness() *(aDoseIndex(2))-0.5*aGeometricInfo->getSliceThickness(); + + //std::cout << "zIndex:"<getPixelSpacingRow(); + _deltaY=aGeometricInfo->getPixelSpacingColumn(); + _deltaZ=aGeometricInfo->getSliceThickness(); + } + + //default 1 + _proportionInStr=1; + +} +void DoseVoxel::setDoseVoxel(const LegacyDoseVoxelIndex3D &aDoseIndex, const rttb::core::GeometricInfo* aGeometricInfo) { + + if(aDoseIndex.x<0 || aDoseIndex.y<0 || aDoseIndex.z<0) + throw rttb::core::InvalidParameterException("Invalid dose index!"); + else{ + _voxelIndex3D=aDoseIndex; + _geoInfo=aGeometricInfo; + + + //Martina + WorldCoordinate3D aWorldCoordinate(0); + VoxelGridIndex3D currentIndex(aDoseIndex.x,aDoseIndex.y,aDoseIndex.z); + if(aGeometricInfo->indexToWorldCoordinate(currentIndex , aWorldCoordinate)){ + _x = aWorldCoordinate(0); + _y = aWorldCoordinate(1); + _z = aWorldCoordinate(2); + } + + //Lanlan + //_x=aGeometricInfo->imageOrientationRow.x*aGeometricInfo->pixelSpacingRow*aDoseIndex.x+aGeometricInfo->imageOrientationColumn.x*aGeometricInfo->pixelSpacingColumn*aDoseIndex.y+aGeometricInfo->imagePositionPatient.x-0.5*aGeometricInfo->pixelSpacingRow; + //_y=aGeometricInfo->imageOrientationRow.y*aGeometricInfo->pixelSpacingRow*aDoseIndex.x+aGeometricInfo->imageOrientationColumn.y*aGeometricInfo->pixelSpacingColumn*aDoseIndex.y+aGeometricInfo->imagePositionPatient.y-0.5*aGeometricInfo->pixelSpacingColumn; + + //_z=aGeometricInfo->imagePositionPatient.z + aGeometricInfo->sliceThickness *(aDoseIndex.z)-0.5*aGeometricInfo->sliceThickness; + + //std::cout << "zIndex:"<getPixelSpacingRow(); + _deltaY=aGeometricInfo->getPixelSpacingColumn(); + _deltaZ=aGeometricInfo->getSliceThickness(); + } + + //default 1 + _proportionInStr=1; + + +} +/*! @brief Set dose voxel with the left upper point and delta + * + */ +void DoseVoxel::setDoseVoxel(WorldCoordinate3D aWorldCoordinate, WorldCoordinate3D aVoxelDelta){ + _x=aWorldCoordinate(0); + _y=aWorldCoordinate(1); + _z=aWorldCoordinate(2); + _deltaX=aVoxelDelta(0); + _deltaY=aVoxelDelta(1); + _deltaZ=aVoxelDelta(2); + +} +void DoseVoxel::setDoseVoxel(LegacyWorldCoordinate3D aWorldCoordinate, LegacyWorldCoordinate3D aVoxelDelta){ + _x=aWorldCoordinate.x; + _y=aWorldCoordinate.y; + _z=aWorldCoordinate.z; + _deltaX=aVoxelDelta.x; + _deltaY=aVoxelDelta.y; + _deltaZ=aVoxelDelta.z; + +} + +/*! @brief Tests if a point is inside the voxel + * + */ +bool DoseVoxel::pointInVoxel(const WorldCoordinate3D& aPoint) const { + if ((aPoint(0) <=(_x+_deltaX) && aPoint(0) >=_x ) && + (aPoint(1) <=(_y+_deltaY) && aPoint(1) >=_y ) && + (aPoint(2) <=(_z+_deltaZ) && aPoint(2) >=_z )) + return true; + else + return false; +} + +bool DoseVoxel::pointInVoxel(const LegacyWorldCoordinate3D& aPoint) const { + if ((aPoint.x <=(_x+_deltaX) && aPoint.x >=_x ) && + (aPoint.y <=(_y+_deltaY) && aPoint.y >=_y ) && + (aPoint.z <=(_z+_deltaZ) && aPoint.z >=_z )) + return true; + else + return false; +} + +/*! @brief Calculates how many relative volume of the voxel inside the structure. + * @return Return the relative volume. >0: voxel in structure, =0: voxel not in structure, <0: error + * @pre aRTStructure must not be NULL + */ +double DoseVoxel::voxelInStructure(const StructureLegacy& aRTStructure) const{ + + //bool voxelInStr=false; + const PolygonSequenceType& strVector=aRTStructure.getStructureVector(); + + int size=strVector.size(); + + if(size==0){ + std::cerr << "Error: structure vector of aRTStructure is empty! "< roi=strVector[i]; + vector roiNext; + if(i==size-1) + roiNext=strVector[0]; + else + roiNext=strVector[i+1]; + + WorldCoordinate3D* p1=roi[0]; + WorldCoordinate3D* p2=roiNext[0]; + + + if(_z>= p1->z && _zz) + roiNumber[0]=i; + else if(_z+_deltaZ/5 >= p1->z && _z+_deltaZ/5 z) + roiNumber[1]=i; + else if(_z+2*_deltaZ/5 >= p1->z && _z+2*_deltaZ/5 z) + roiNumber[2]=i; + else if(_z+3*_deltaZ/5 >= p1->z && _z+3*_deltaZ/5 z) + roiNumber[3]=i; + else if(_z+4*_deltaZ/5 >= p1->z && _z+4*_deltaZ/5 z) + roiNumber[4]=i; + + } + + //get 5^3=125 points from the voxel + for(int i=0; i<5; i++){ + //std::cout <<"------------"< voxelPoints; + //std::cout << "----------------------------Voxel Points: "<_x<x=(_x+k*_deltaX/5); + p->y=(_y+j*_deltaY/5); + p->z=(_z+i*_deltaZ/5); + //std::cout << "("<x<<","<y<<","<z< roi=strVector[roiNumber[i]]; + if(this->pointInPolygon(roi,voxelPoint)) + voxelPointInStructure++; + //} + + //~ + vector::iterator it; + for(it=voxelPoints.begin();it!=voxelPoints.end();it++){ + delete (*it); + } + + + }*/ + + + /*PolygonType roi=strVector[i]; + if(roi.size()==0){ + std::cerr << "Error: corresponding roi of aRTStructure is empty! "<pointInPolygon(roi,p)) + voxelPointInStructure++;*/ + + //return voxelPointInStructure/125; + //return voxelPointInStructure/1; + + std::vector sliceV=this->getZIntersectPolygonIndexVector(aRTStructure); + + for(int i=0;i0: voxel in structure, =0: voxel not in structure, <0: error + * @pre aRTStructure must not be NULL +*/ +double DoseVoxel::voxelInStructure(const StructureLegacy& aRTStructure, int sliceNumber) const{ + + + + const LegacyPolygonSequenceType& strVector=aRTStructure.getLegacyStructureVector(); + int size=strVector.size(); + + if(size==0){ + std::cerr << "Error: structure vector of aRTStructure is empty! "<size){ + std::cerr << "Error: sliceNumber must between -1 and "<getArea(); + }*/ + + + + + //if the next contour not in the same slice, break + if(isize){ + std::cerr << "Error: polygonIndex must between -1 and "<getArea(); + }*/ + + + + //std::cout << voxelPointInStructure/4.0<= last polygon + + //Reduce copying of values, since boost_vector constructors are expensive + //Anmerkung fuer Lanlan zwecks mergen : Eine Kontur, die direkt in einem slice liegt sollte nicht in beiden angrenzenden Slices voxelisiert werden. + if(_z<=strVector[i][0].z && _z+_deltaZ > strVector[i][0].z) + return i; + else + return size;//out of structure and _z bigger than the last structure slice + } + //else + else{ + + //if roi and roiNext in the same slice + if(strVector[i][0].z==strVector[i+1][0].z){ + //if voxel intersect the slice + if(_z<=strVector[i][0].z && _z+_deltaZ>=strVector[i][0].z) + return i; + } + else{ + if(_z+_deltaZ>= strVector[i][0].z && _zz<<", p2(2):"<z<=roiNext, set i+1 + } + + } + } + + } + return -1;//out of structure and _z smaller than the first structure slice + +} + +std::vector DoseVoxel::getZIntersectPolygonIndexVector(const StructureLegacy& aRTStructure) const{ + + + std::vector sliceNumberVector; + + const LegacyPolygonSequenceType& strVector=aRTStructure.getLegacyStructureVector();//strVector is ranked by _z of the contour + int size=strVector.size(); + + if(size==0){ + throw std::out_of_range( "Error: structure vector of aRTStructure is empty! "); + } + + + int firstSlice=this->getCorrespondingSlice(aRTStructure); + if(firstSlice>=0 && firstSlice DoseVoxel::calcIntersectionPoints(WorldCoordinate3D firstPoint, WorldCoordinate3D secondPoint){ + + //4 Segment of the dose voxel:[(_x,_y),(_x+_deltaX,_y)],[(_x+_deltaX,_y),(_x+_deltaX,_y+_deltaY)],[(_x+_deltaX,_y+_deltaY),(_x,_y+_deltaY)],[(_x,_y+_deltaY),(_x,_y)] + + //segment: (y-first(1))*(secont(0)-first(0))=(x-first(0))*(secont(1)-first(1)) + int a=secondPoint(0)-firstPoint(0); + int b=secondPoint(1)-firstPoint(1); + + std::vector intersectionPoints; + + if(a==0){ + if(firstPoint(0)>=_x && firstPoint(0)<=(_x+_deltaX)){ + if(min(firstPoint(1),secondPoint(1))<=_y && max(firstPoint(1), secondPoint(1))>=_y){ + WorldCoordinate3D p; + p(0) = firstPoint(0); + p(1) = _y; + p(2) = _z; + intersectionPoints.push_back(p); + } + if(min(firstPoint(1),secondPoint(1))<=(_y+_deltaY) && max(firstPoint(1), secondPoint(1))>=(_y+_deltaY)){ + WorldCoordinate3D p; + p(0) = firstPoint(0); + p(1) = _y+_deltaY; + p(2) = _z; + intersectionPoints.push_back(p); + } + + } + + } + else if(b==0){ + if(firstPoint(1)>=_y && firstPoint(1)<=(_y+_deltaY)){ + if(min(firstPoint(0),secondPoint(0))<=_x && max(firstPoint(0), secondPoint(0))>=_x){ + WorldCoordinate3D p; + p(0) = _x; + p(1) = firstPoint(1); + p(2) = _z; + intersectionPoints.push_back(p); + } + if(min(firstPoint(0),secondPoint(0))<=(_x+_deltaX) && max(firstPoint(0), secondPoint(0))>=(_x+_deltaX)){ + WorldCoordinate3D p; + p(0) = _x+_deltaX; + p(1) = firstPoint(1); + p(2) = _z; + intersectionPoints.push_back(p); + } + + } + + } + else{ + //1.[(_x,_y),(_x+_deltaX,_y)] + double interX=(_y-firstPoint(1))*a/b+firstPoint(0); + if(interX>=_x && interX<=(_x+_deltaX)){ + WorldCoordinate3D p; + p(0) = interX; + p(1) = _y; + p(2) = _z; + intersectionPoints.push_back(p); + } + + //2.[(_x+_deltaX,_y),(_x+_deltaX,_y+_deltaY)] + double interY=(_x+_deltaX-firstPoint(0))*b/a+firstPoint(1); + if(interY>=_y && interY<=(_y+_deltaY)){ + WorldCoordinate3D p; + p(0) = _x+_deltaX; + p(1) = interY; + p(2) = _z; + intersectionPoints.push_back(p); + } + + //3. [(_x+_deltaX,_y+_deltaY),(_x,_y+_deltaY)] + interX=(_y+_deltaY-firstPoint(1))*a/b+firstPoint(0); + if(interX>=_x && interX<=(_x+_deltaX)){ + WorldCoordinate3D p; + p(0) = interX; + p(1) = _y+_deltaY; + p(2) = _z; + intersectionPoints.push_back(p); + } + + //4.[(_x,_y+_deltaY),(_x,_y)] + interY=(_x-firstPoint(0))*b/a+firstPoint(1); + if(interY>=_y && interY<=(_y+_deltaY)){ + WorldCoordinate3D p; + p(0) = _x; + p(1) = interY; + p(2) = _z; + intersectionPoints.push_back(p); + } + + } + return intersectionPoints; +} + +std::vector DoseVoxel::calcIntersectionPoints(LegacyWorldCoordinate3D firstPoint, LegacyWorldCoordinate3D secondPoint){ + + //4 Segment of the dose voxel:[(_x,_y),(_x+_deltaX,_y)],[(_x+_deltaX,_y),(_x+_deltaX,_y+_deltaY)],[(_x+_deltaX,_y+_deltaY),(_x,_y+_deltaY)],[(_x,_y+_deltaY),(_x,_y)] + + //segment: (y-first.y)*(secont.x-first.x)=(x-first.x)*(secont.y-first.y) + int a=secondPoint.x-firstPoint.x; + int b=secondPoint.y-firstPoint.y; + + std::vector intersectionPoints; + + if(a==0){ + if(firstPoint.x>=_x && firstPoint.x<=(_x+_deltaX)){ + if(min(firstPoint.y,secondPoint.y)<=_y && max(firstPoint.y, secondPoint.y)>=_y){ + LegacyWorldCoordinate3D p(firstPoint.x,_y,_z); + intersectionPoints.push_back(p); + } + if(min(firstPoint.y,secondPoint.y)<=(_y+_deltaY) && max(firstPoint.y, secondPoint.y)>=(_y+_deltaY)){ + LegacyWorldCoordinate3D p(firstPoint.x,_y+_deltaY,_z); + intersectionPoints.push_back(p); + } + + } + + } + else if(b==0){ + if(firstPoint.y>=_y && firstPoint.y<=(_y+_deltaY)){ + if(min(firstPoint.x,secondPoint.x)<=_x && max(firstPoint.x, secondPoint.x)>=_x){ + LegacyWorldCoordinate3D p(_x,firstPoint.y,_z); + intersectionPoints.push_back(p); + } + if(min(firstPoint.x,secondPoint.x)<=(_x+_deltaX) && max(firstPoint.x, secondPoint.x)>=(_x+_deltaX)){ + LegacyWorldCoordinate3D p(_x+_deltaX,firstPoint.y,_z); + intersectionPoints.push_back(p); + } + + } + + } + else{ + //1.[(_x,_y),(_x+_deltaX,_y)] + double interX=(_y-firstPoint.y)*a/b+firstPoint.x; + if(interX>=_x && interX<=(_x+_deltaX)){ + LegacyWorldCoordinate3D p(interX,_y,_z); + intersectionPoints.push_back(p); + } + + //2.[(_x+_deltaX,_y),(_x+_deltaX,_y+_deltaY)] + double interY=(_x+_deltaX-firstPoint.x)*b/a+firstPoint.y; + if(interY>=_y && interY<=(_y+_deltaY)){ + LegacyWorldCoordinate3D p(_x+_deltaX,interY,_z); + intersectionPoints.push_back(p); + } + + //3. [(_x+_deltaX,_y+_deltaY),(_x,_y+_deltaY)] + interX=(_y+_deltaY-firstPoint.y)*a/b+firstPoint.x; + if(interX>=_x && interX<=(_x+_deltaX)){ + LegacyWorldCoordinate3D p(interX,_y+_deltaY,_z); + intersectionPoints.push_back(p); + } + + //4.[(_x,_y+_deltaY),(_x,_y)] + interY=(_x-firstPoint.x)*b/a+firstPoint.y; + if(interY>=_y && interY<=(_y+_deltaY)){ + LegacyWorldCoordinate3D p(_x,interY,_z); + intersectionPoints.push_back(p); + } + + } + return intersectionPoints; +} + +/*! @brief Get the voxel index*/ +const LegacyDoseVoxelIndex3D& DoseVoxel::getVoxelIndex3D() const{ + return _voxelIndex3D; +} + +/*! @brief Get the geometric information*/ +const core::GeometricInfo* DoseVoxel::getGeometricInfo() const{ + return _geoInfo; +} + +void DoseVoxel::setProportionInStr(FractionType aProportionInStr){ + _proportionInStr=aProportionInStr; +} + +FractionType DoseVoxel::getProportionInStr() const{ + return _proportionInStr; +} +} + }//end namespace core +}//end namespace rttb + diff --git a/code/masks/DoseVoxel_LEGACY.h b/code/masks/DoseVoxel_LEGACY.h new file mode 100644 index 0000000..dda073e --- /dev/null +++ b/code/masks/DoseVoxel_LEGACY.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$ (last changed revision) +// @date $Date$ (last change date) +// @author $Author$ (last changed by) +*/ + +#ifndef __RT_DOSE_VOXEL_H +#define __RT_DOSE_VOXEL_H + + +//#include "rttbStructure.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" +#include "rttbBaseType_LEGACY.h" +#include "rttbStructure_LEGACY.h" + +namespace rttb{ + + namespace masks + { +namespace legacy{ + /*! @class DoseVoxel + * @brief This is a class representing a DoseVoxel. + */ + + class DoseVoxel + { + + private: + /*! @brief Voxel index */ + LegacyDoseVoxelIndex3D _voxelIndex3D; + + /*! @brief WorldCoordinate (_x,_y,_z): the left upper point, the point in voxel with minimal x y and z coordinate*/ + WorldCoordinate _x; + WorldCoordinate _y; + WorldCoordinate _z; + /*! @brief(_x+_deltaX, _y+_deltaY,_z+_deltaZ): the point in voxel with maximal x y and z coordinate*/ + WorldCoordinate _deltaX; + WorldCoordinate _deltaY; + WorldCoordinate _deltaZ; + + /*! @brief Geometric information*/ + const core::GeometricInfo* _geoInfo; + + /*! @brief The proportion of voxel which is inside a given structure: 0~1 */ + FractionType _proportionInStr; + + + public: + + /* @brief Standard Constructor*/ + DoseVoxel(); + + /*! @brief Constructor + @param aDoseIndex: a dose voxel index, aGeometricInfo: the geometric information (imagePosition, ImageOrientation, + pixelSpacing and getSliceThickness()) + */ + DoseVoxel(const VoxelGridIndex3D& aDoseIndex, const core::GeometricInfo* aGeometricInfo); + //same method with legacy data structures + DoseVoxel(const LegacyDoseVoxelIndex3D& aDoseIndex, const core::GeometricInfo* aGeometricInfo); + + /*! @brief Operator = + */ + bool operator==(const DoseVoxel& doseVoxel) const; + + /*! @brief Operator < + */ + bool operator<(const DoseVoxel& doseVoxel) const; + + + /*! @brief Initialisation of the dose voxel with dose voxel index and DICOM-RT information. Transform dose index to world coordinate + * @param imagePosition: RT Image Position(3002,0012), + * @param getImageOrientationRow(), getImageOrientationColumn(): Image Orientation (Patient) (0020,0037), + * @param getPixelSpacingRow(), getPixelSpacingColumn(): Pixel Spacing (0028,0030) + * @param getSliceThickness(): Slice Thickness (0018,0050). + * @exception RTTBInvalidParameterException thrown if aDoseIndex /Pixel spacing/slice thickness invalid + * + */ + + //void setDoseIndex(const VoxelGridIndex3D& aDoseIndex, const WorldCoordinate3D& imagePosition, + //const ImageOrientation& getImageOrientationRow(), const ImageOrientation& getImageOrientationColumn(), + //double getPixelSpacingRow(), double getPixelSpacingColumn(), double getSliceThickness()); + void setDoseVoxel(const VoxelGridIndex3D& aDoseIndex, const core::GeometricInfo* aGeometricInfo); + //same method with legacy data structures + void setDoseVoxel(const LegacyDoseVoxelIndex3D& aDoseIndex, const core::GeometricInfo* aGeometricInfo); + + + /*! @brief Initialisation of the dose voxel with world coordinate. Set dose voxel with the left upper point and delta + * + */ + + void setDoseVoxel(WorldCoordinate3D aWorldCoordinate, WorldCoordinate3D aVoxelDelta); + //same method with legacy data structures + void setDoseVoxel(LegacyWorldCoordinate3D aWorldCoordinate, LegacyWorldCoordinate3D aVoxelDelta); + + /*! @brief Tests if a point is inside the voxel + * + */ + bool pointInVoxel(const WorldCoordinate3D& aPoint) const; + //same method with legacy data structures + bool pointInVoxel(const LegacyWorldCoordinate3D& aPoint) const; + + /*! @brief Calculates how many relative volume of the voxel inside the structure. + * @return Return the relative volume. >0: voxel in structure, =0: voxel not in structure, <0: error + */ + double voxelInStructure(const StructureLegacy& aRTStructure) const; + + + /*! @brief Calculates how many relative volume of the voxel inside the structure, with the corresponding polygon index number. + * It will be tested if the dose voxel in any Polygon with the same z coord.. + * @param sliceNumber: fist index number of the polygon stored in strVector (ranked by z coord. of the polygon) if the z coord. + * of the dose voxel between the last Polygon and this Polygon!!! + * + * @return Return the relative volume. >0: voxel in structure, =0: voxel not in structure, <0: error + * @pre aRTStructure must not be NULL + */ + double voxelInStructure(const StructureLegacy& aRTStructure, int sliceNumber) const; + + + /*! @brief Calculates how many relative volume of the voxel inside the corresponding polygon with polygon index number. + * + * @param polygonIndex: the index number of the polygon stored in strVector (ranked by z coord. of the polygon) + * + * @return Return the relative volume. >0: voxel in structure, =0: voxel not in structure, <0: error + * @pre aRTStructure must not be NULL + */ + double voxelInStructureAtIndex(const StructureLegacy& aRTStructure, int polygonIndex) const; + + + /*! @brief Tests if a point is inside a polygon. + * @return Return true if the point is inside the polygon or the point is on an edge of the polygon + ! Make sure that the point and the list of positions lies both in the z-plane + */ + //bool pointInPolygon(const PolygonType& aPolygon, const WorldCoordinate3D& aPoint) const; + + /*! @brief Tests if the voxel intersect the polygon defined by the given list of positions. + ! Make sure that the point and the list of positions lies both in the z-plane + */ + //public: bool voxelInROI(vector aRoi); + + + + + + /*! @brief get the first corresponding slice number: + * !!!Important: not the actual slice number, but the index number of the polygon stored in the structure vector (ranked by z coord. of the polygon) + if the z coord. of the dose voxel between the last Polygon and this Polygon!!! + * @return i from (0~strVector.size()-1): voxel between the i-th and (i+1)-th polygon or _z on the i-th polygon; + * -1: out of structure and _z smaller than z coordi.of the first polygon + * strVector.size(): out of structure and _z bigger than z coordi.of the last polygon + */ + + int getCorrespondingSlice(const StructureLegacy& aRTStructure) const; + + /*! @brief Get polygon indexs in the same contour-slice, which has intersection with this voxel. If the voxel intersect > 1 Slice, the slice with smallert + * index will be used. + * @return Return vector of index number of the polygons stored in the structure vector(ranked by z coord. of the polygon), if size=0: out of structure + */ + + std::vector getZIntersectPolygonIndexVector(const StructureLegacy& aRTStructure) const; + + /*! @brief Get the left upper point of the dose voxel in world coordinate. + * + */ + WorldCoordinate3D getLeftUpperPoint() const; + LegacyWorldCoordinate3D getLeftUpperPointLegacy() const; + + /*! @brief Get (_deltaX, _deltaY, _deltaZ) of the dose voxel in world coordinate. + * + */ + WorldCoordinate3D getDelta() const; + LegacyWorldCoordinate3D getDeltaLegacy() const; + + /*! @brief Calculate the interseciton points with the segment [firstPoint, secontPoint]*/ + std::vector calcIntersectionPoints(WorldCoordinate3D firstPoint, WorldCoordinate3D secondPoint); + //same method with legacy data structures + std::vector calcIntersectionPoints(LegacyWorldCoordinate3D firstPoint, LegacyWorldCoordinate3D secondPoint); + + /*! @brief Get the voxel index*/ + const LegacyDoseVoxelIndex3D& getVoxelIndex3D() const; + + /*! @brief Get the geometric information*/ + const core::GeometricInfo* getGeometricInfo() const; + + /*! @brief Set the voxel proportion inside a given structure*/ + void setProportionInStr(FractionType aProportionInStr); + + /*! @brief Get the voxel proportion inside a given structure*/ + FractionType getProportionInStr() const; + }; +} + } +} + +#endif diff --git a/code/masks/files.cmake b/code/masks/files.cmake new file mode 100644 index 0000000..14215d6 --- /dev/null +++ b/code/masks/files.cmake @@ -0,0 +1,30 @@ +SET(CPP_FILES + rttbOTBMaskAccessor.cpp + rttbMask.cpp + DoseVoxel_LEGACY.cpp + DoseIteratorInterface_LEGACY.cpp + DoseIterator_LEGACY.cpp + rttbContour_LEGACY.cpp + rttbIterativePolygonInTermsOfIntersections_LEGACY.cpp + rttbPolygonInfo_LEGACY.cpp + rttbSelfIntersectingStructureException.cpp + rttbStructure_LEGACY.cpp + rttbGenericMutableMaskAccessor.cpp + ) + +SET(H_FILES + rttbMaskType_LEGACY.h + rttbMask.h + rttbOTBMaskAccessor.h + DoseVoxel_LEGACY.h + DoseIteratorInterface_LEGACY.h + DoseIterator_LEGACY.h + rttbContour_LEGACY.h + rttbIterativePolygonInTermsOfIntersections_LEGACY.h + rttbField_LEGACY.h + rttbPolygonInfo_LEGACY.h + rttbBaseType_LEGACY.h + rttbSelfIntersectingStructureException.h + rttbStructure_LEGACY.h + rttbGenericMutableMaskAccessor.h +) diff --git a/code/masks/rttbBaseType_LEGACY.h b/code/masks/rttbBaseType_LEGACY.h new file mode 100644 index 0000000..16a7e01 --- /dev/null +++ b/code/masks/rttbBaseType_LEGACY.h @@ -0,0 +1,254 @@ +// ----------------------------------------------------------------------- +// 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 __RT_BASE_TYPE_H +#define __RT_BASE_TYPE_H + +#include +#include +#include +#include +#include +#include + +#include "rttbBaseType.h" + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + typedef double LegacyWorldCoordinate1D; + + struct LegacyWorldCoordinate2D{ + LegacyWorldCoordinate1D x; + LegacyWorldCoordinate1D y; + + std::string toString() const{ + std::stringstream sstr; + sstr<<"("<x = 0; + this->y = 0; + this->z = 0; + } + + LegacyWorldCoordinate3D( LegacyWorldCoordinate1D a, LegacyWorldCoordinate1D b, LegacyWorldCoordinate1D c){ + this->x = a; + this->y = b; + this->z = c; + } + + LegacyWorldCoordinate3D(rttb::WorldCoordinate3D aCoordinate){ + this->x = aCoordinate.x(); + this->y = aCoordinate.y(); + this->z = aCoordinate.z(); + } + + bool operator==(const LegacyWorldCoordinate3D& p) const + { + return(x==p.x && y==p.y && z==p.z); + } + + /** assigment operator + * @param copy LegacyWorldCoordinate3D object to be copied + */ + LegacyWorldCoordinate3D& operator=(const LegacyWorldCoordinate3D& copy){ + if (this != ©) + { + x=copy.x; + y=copy.y; + z=copy.z; + } + return *this; + } + //vector cross product (not included in boost.ublas) + LegacyWorldCoordinate3D cross(LegacyWorldCoordinate3D aVector) const{ + LegacyWorldCoordinate3D result; + LegacyWorldCoordinate1D x = this->x; + LegacyWorldCoordinate1D y = this->y; + LegacyWorldCoordinate1D z = this->z; + + result.x = y*aVector.z-z*aVector.y; + result.y = z*aVector.x-x*aVector.z; + result.z = x*aVector.y-y*aVector.x; + + return result; + } + + }LegacyImageOrientation; + + + + + + typedef int LegacyDoseVoxelIndex1D; + + + struct LegacyDoseVoxelIndex3D{ + LegacyDoseVoxelIndex1D x; + LegacyDoseVoxelIndex1D y; + LegacyDoseVoxelIndex1D z; + + std::string toString() const{ + std::stringstream sstr; + sstr<<"("<index.z) + return false; + else{ + if(yindex.y) + return false; + else{ + if(x LegacyPolygonType; + + typedef std::vector LegacyPolygonSequenceType; + + + typedef struct{ + + LegacyWorldCoordinate1D x_begin; + + LegacyWorldCoordinate1D x_end; + + LegacyWorldCoordinate1D y_begin; + + LegacyWorldCoordinate1D y_end; + + LegacyDoseVoxelIndex2D index_begin; + + LegacyDoseVoxelIndex2D index_end; + + void Init(){ + x_begin = -1000000; + x_end = -1000000; + y_begin = -1000000; + y_end = -1000000; + index_begin.x = 0; + index_begin.y = 0; + index_end.x = 0; + index_end.y = 0; + + } + + }LegacyArea2D; + + typedef struct{ + LegacyWorldCoordinate2D begin; + LegacyWorldCoordinate2D end; + }LegacySegment2D; + + typedef struct{ + LegacyWorldCoordinate3D begin; + LegacyWorldCoordinate3D end; + }LegacySegment3D; + + /*! @brief area_vector specifies an image region. So its a vector of 2d areas. That means it is a 3D area. + */ + typedef std::vector area_vector; + typedef std::vector< std::vector > vector_of_area_vectors; + +} + + } + +} + +#endif + diff --git a/code/masks/rttbContour_LEGACY.cpp b/code/masks/rttbContour_LEGACY.cpp new file mode 100644 index 0000000..7ec0657 --- /dev/null +++ b/code/masks/rttbContour_LEGACY.cpp @@ -0,0 +1,442 @@ +// ----------------------------------------------------------------------- +// 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 "rttbContour_LEGACY.h" + +#include +#include +#include + + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + + + Contour::Contour(LegacyPolygonType aPolygon) + { + _polygon=aPolygon; + } + + /*Contour::Contour(int i){ + + }*/ + + Contour::~Contour(){ + } + + const LegacyPolygonType& Contour::getPolygon() const{ + return _polygon; + } + + + /*calculate contour area*/ + double Contour::getArea() const{ + + + double area=0; + size_t numberOfPoints=_polygon.size(); + for(int j1=0, j2=1; j1=std::min(q1.x,q2.x))&& + (std::max(q1.x,q2.x)>=std::min(p1.x,p2.x))&& + (std::max(p1.y,p2.y)>=std::min(q1.y,q2.y))&& + (std::max(q1.y,q2.y)>=std::min(p1.y,p2.y))&& + (multiply(q1,p2,p1)*multiply(p2,q2,p1)>=0)&& + (multiply(p1,q2,q1)*multiply(q2,p2,q1)>=0)); + //if(intersect) std::cout << "intersect: "<= pnt1.y ); + + GridIndexType i=0; + + do{ + i++; + + if(i==_polygon.size()) + pnt2=_polygon[0]; + else + pnt2 = _polygon[i]; + + //if the point lies on an edge of the polygon, return true + if((pnt2.y-aPoint.y)*(pnt1.x-pnt2.x)==(pnt2.x-aPoint.x)*(pnt1.y-pnt2.y) && + (aPoint.x>=std::min(pnt1.x,pnt2.x)) && (aPoint.x <=std::max(pnt1.x,pnt2.x)) && + (aPoint.y>=std::min(pnt1.y,pnt2.y)) && (aPoint.y <=std::max(pnt1.y,pnt2.y))) + return true; + + //else + flag2=( aPoint.y >= pnt2.y ); + if(flag1 != flag2) + { + if(((pnt2.y-aPoint.y)*(pnt1.x-pnt2.x)>=(pnt2.x-aPoint.x)*(pnt1.y-pnt2.y)) + ==flag2) + inside=!inside; + } + pnt1=pnt2; + flag1=flag2; + } while(i<_polygon.size()); + + //} + + + return inside; + } + + + + bool Contour::point2DInPolygon(const LegacyWorldCoordinate2D& aPoint) const{ + if(this->_polygon.size()>0){ + LegacyWorldCoordinate3D p; + p.x = aPoint.x; + p.y = aPoint.y; + p.z = this->_polygon.at(0).z; + return this->pointInPolygon(p); + } + else{ + std::cerr << "_polygon is empty! "<< std::endl; + return false; + } + } + + NumberOfEndpointsType Contour::getNumberOfEndpoints() const{ + + return _polygon.size(); + } + + LegacyPolygonType Contour::getMinimalBox() const{ + LegacyPolygonType box; + + WorldCoordinate maxX; + WorldCoordinate maxY; + WorldCoordinate minX; + WorldCoordinate minY; + WorldCoordinate z; + for(GridIndexType i=0;i<_polygon.size();i++){ + LegacyWorldCoordinate3D p=_polygon.at(i); + z=p.z; + if(i==0){ + minX=p.x; + minY=p.y; + maxX=p.x; + maxY=p.y; + } + if(p.xmaxX) + maxX=p.x; + if(p.ymaxY) + maxY=p.y; + + } + + LegacyWorldCoordinate3D pLU; + pLU.x = minX; pLU.y = minY; pLU.z = z; + LegacyWorldCoordinate3D pRU; + pRU.x = maxX; pRU.y = minY; pRU.z = z; + LegacyWorldCoordinate3D pRB; + pRB.x = maxX; pRB.y = maxY; pRB.z = z; + LegacyWorldCoordinate3D pLB; + pLB.x = minX; pLB.y = maxY; pLB.z = z; + box.push_back(pLU); + box.push_back(pRU); + box.push_back(pRB); + box.push_back(pLB); + return box; + } + + Contour* Contour::calcIntersection(Contour& contour2){ + + LegacyPolygonType box1=this->getMinimalBox(); + LegacyWorldCoordinate3D box1P1=box1.at(0); + LegacyWorldCoordinate3D box1P2=box1.at(1); + LegacyWorldCoordinate3D box1P3=box1.at(2); + LegacyWorldCoordinate3D box1P4=box1.at(3); + LegacyPolygonType box2=contour2.getMinimalBox(); + LegacyWorldCoordinate3D box2P1=box2.at(0); + LegacyWorldCoordinate3D box2P2=box2.at(1); + LegacyWorldCoordinate3D box2P3=box2.at(2); + LegacyWorldCoordinate3D box2P4=box2.at(3); + + Contour box1Contour=Contour(box1); + //if box1 and box2 have no intersection, that means no intersection between this contour and contour2 + if(!box1Contour.pointInPolygon(box2P1) && !box1Contour.pointInPolygon(box2P2) + && !box1Contour.pointInPolygon(box2P3) && !box1Contour.pointInPolygon(box2P4) ){ + //std::cout << "No intersection!"< segments1InBox2; + for(GridIndexType i=0;i<_polygon.size();i++){ + LegacyWorldCoordinate3D segmentL=_polygon.at(i);//segment left corner + LegacyWorldCoordinate3D segmentR;//segment right corner + if(i==_polygon.size()-1) + segmentR=_polygon.at(0); + else + segmentR=_polygon.at(i+1); + + //if this segment not inside box2 + if(!(std::max(segmentL.x,segmentR.x)box2P2.x + || (std::max(segmentL.y,segmentR.y)box2P3.y))){ + LegacySegment3D segment; + segment.begin=segmentL; + segment.end=segmentR; + segments1InBox2.push_back(segment); + } + } + + //Remove segments of contour2 which not inside box1 + std::vector segments2InBox1; + LegacyPolygonType polygon2=contour2.getPolygon(); + for(GridIndexType i=0;ibox1P2.x + || (std::max(segmentL.y,segmentR.y)box1P3.y))){ + LegacySegment3D segment; + segment.begin=segmentL; + segment.end=segmentR; + segments2InBox1.push_back(segment); + } + } + + //Get the points of this contour inside contour2 and all intersect points + std::vector points1InContour2;//store points of this contour inside contour2 and all intersect points + std::vector::iterator it; + std::vector::iterator it2; + for(it=segments1InBox2.begin();it!=segments1InBox2.end();it++){ + LegacySegment3D segment=*it; + if(contour2.pointInPolygon(segment.begin)) + points1InContour2.push_back(segment.begin); + + //test: if segment has intersection with any segment in segments2InBox1 + for(it2=segments2InBox1.begin();it2!=segments2InBox1.end();it2++){ + if(Contour::intersect(segment.begin,segment.end,(*it2).begin,(*it2).end)){ + LegacyWorldCoordinate3D interP=Contour::calcInterPoint(segment.begin,segment.end,(*it2).begin,(*it2).end); + if(Contour(points1InContour2).contains(interP)) + points1InContour2.push_back(interP); + } + } + } + //sort points1InContour2 clockwise + Contour tempContour=Contour(points1InContour2); + tempContour.sortClockwise(); + points1InContour2=tempContour.getPolygon(); + + + //Get the points of contour2 inside this contour and all intersect points + std::vector points2InContour1;//store points of contour2 inside this contour and all intersect points + for(it=segments2InBox1.begin();it!=segments2InBox1.end();it++){ + LegacySegment3D segment=*it; + if(pointInPolygon(segment.begin)) + points2InContour1.push_back(segment.begin); + + //test: if segment has intersection with any segment in segments2InBox1 + for(it2=segments1InBox2.begin();it2!=segments1InBox2.end();it2++){ + if(Contour::intersect(segment.begin,segment.end,(*it2).begin,(*it2).end)){ + LegacyWorldCoordinate3D interP=Contour::calcInterPoint(segment.begin,segment.end,(*it2).begin,(*it2).end); + if(Contour(points2InContour1).contains(interP)) + points2InContour1.push_back(interP); + } + } + } + //sort points1InContour2 clockwise + tempContour=Contour(points2InContour1); + tempContour.sortClockwise(); + points2InContour1=tempContour.getPolygon(); + + + LegacyPolygonType intersectionPolygon=points1InContour2; + LegacyPolygonType::iterator itP; + for(itP=points2InContour1.begin();itP!=points2InContour1.end();itP++){ + if(!Contour(intersectionPolygon).contains((*itP))) + intersectionPolygon.push_back((*itP)); + + } + //sort intersectionPolygon + Contour* intersection=new Contour(intersectionPolygon); + intersection->sortClockwise(); + + return intersection; + } + + /*! @brief Sort the points in _polygon in clockwise order*/ + void Contour::sortClockwise(){ + size_t numberOfP=_polygon.size(); + if(numberOfP>=3){ + LegacyWorldCoordinate3D p1=_polygon.at(0); + LegacyWorldCoordinate3D p2=_polygon.at(1); + LegacyWorldCoordinate3D p3=_polygon.at(2); + + LegacyWorldCoordinate2D pInPolygon = {(p1.x+p2.x+p3.x)/3,(p1.y+p2.y+p3.y)/3};//get a point inside the polygon + + std::map pointsI; + std::map pointsII; + std::map pointsIII; + std::map pointsIV; + for(int i=0;i=0 && dy>=0){ + pointsI.insert(std::pair(sin*(-1.0),p)); + } + else if(dx>=0 && dy<0) + pointsIV.insert(std::pair(sin*(-1.0),p)); + else if(dx<0 && dy<0) + pointsIII.insert(std::pair(sin,p)); + else + pointsII.insert(std::pair(sin,p)); + } + + LegacyPolygonType sorted; + std::map::iterator it; + for(it=pointsII.begin();it!=pointsII.end();it++) + sorted.push_back((*it).second); + for(it=pointsI.begin();it!=pointsI.end();it++) + sorted.push_back((*it).second); + for(it=pointsIV.begin();it!=pointsIV.end();it++) + sorted.push_back((*it).second); + for(it=pointsIII.begin();it!=pointsIII.end();it++) + sorted.push_back((*it).second); + + _polygon=sorted; + + } + + } + + std::string Contour::toString() const{ + std::stringstream sstr; + sstr<<"{"; + for(GridIndexType i=0;i<_polygon.size();i++) + sstr<<_polygon.at(i).toString()<<", "; + sstr<<"}"; + return sstr.str(); + } + + bool Contour::contains(const LegacyWorldCoordinate3D& aPoint) const{ + for(GridIndexType i=0;i<_polygon.size();i++){ + if(aPoint==_polygon.at(i)) + return true; + } + return false; + } +}//end namespace legacy + }//end namespace masks +}//end namespace rttb diff --git a/code/masks/rttbContour_LEGACY.h b/code/masks/rttbContour_LEGACY.h new file mode 100644 index 0000000..c7b67f3 --- /dev/null +++ b/code/masks/rttbContour_LEGACY.h @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// 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 __CONTOUR_H +#define __CONTOUR_H + + +#include + + +#include "rttbBaseType.h" +#include "rttbBaseType_LEGACY.h" + + + + +namespace rttb{ + + namespace masks{ + namespace legacy{ + + typedef size_t NumberOfEndpointsType; + + /*! @class Contour + * @brief This is a class representing a RT Contour. A RT contour is either a single point (for a point ROI) or more than + * one point (representing an open or closed polygon). + */ + class Contour + { + + private: + /*! @brief WorldCoordinate3D in mm + */ + LegacyPolygonType _polygon; + + static double multiply(const LegacyWorldCoordinate3D& p1,const LegacyWorldCoordinate3D& p2, const LegacyWorldCoordinate3D& p0); + + static LegacyWorldCoordinate2D calcInterPoint2D(const LegacyWorldCoordinate2D& p1, const LegacyWorldCoordinate2D& p2, const LegacyWorldCoordinate2D& q1, const LegacyWorldCoordinate2D& q2); + + public: + + //Contour(int i); + /*! @brief Contour Constructor + @param aPolygon: a polygon used to generate the contour + */ + Contour(LegacyPolygonType aPolygon); + + /*! @brief Destructor + */ + ~Contour(); + + /*! @brief Calculate contour area using Formel 0.5*abs(x1*y2-y1*x2+x2*y3-y2*x3+...+xn*y1-yn*x1) + @return Return a double value of contour area + */ + double getArea() const; + + /*! @brief Tests if a point is inside _polygon. If the point lies on an edge of the polygon, return true + *! Make sure that the point and the list of positions lies both in the z-plane + */ + bool pointInPolygon(const LegacyWorldCoordinate3D& aPoint) const; + + /*! @brief Tests if a 2Dpoint is inside _polygon in 2D. If the point lies on an edge of the polygon, return true + * + */ + bool point2DInPolygon(const LegacyWorldCoordinate2D& aPoint) const; + + /*! @brief Get the polygon + */ + const LegacyPolygonType& getPolygon() const; + + /*! @brief Tests if a point is inside the polygon defined by the given list of positions. + ! Make sure that the point and the list of positions lies both in the z-plane + */ + //bool pointInStructure(const WorldCoordinate3D& aPoint) const; + + /*! @brief Get the number of end points of the polygon*/ + NumberOfEndpointsType getNumberOfEndpoints() const; + + /*! @brief Get the minimal bounding box + * @return Return Polygon{{minX,minY,z},{maxX,minY,z},{maxX,maxY,z},{minX,maxY,z}} + */ + LegacyPolygonType getMinimalBox() const; + + /*! @brief Get the intersection of this Contour and contour2 + ! Make sure that the 2 Contours lies both in the z-plane + * @return Return NULL if no intersection + */ + Contour* calcIntersection(Contour& contour2); + + /*! @brief Sort the points in _polygon in clockwise order*/ + void sortClockwise(); + + /*! @brief Calculate the intersection between the two segments (p1,p2) and (q1,q2) + * Make sure that the points lies in the same z-plane + */ + static LegacyWorldCoordinate3D calcInterPoint(const LegacyWorldCoordinate3D& p1, const LegacyWorldCoordinate3D& p2, const LegacyWorldCoordinate3D& q1, const LegacyWorldCoordinate3D& q2); + + /*! @brief Tests if an intersection exists between two segments (p1, p2) and (q1, q2) */ + static bool intersect(const LegacyWorldCoordinate3D& p1, const LegacyWorldCoordinate3D& p2, const LegacyWorldCoordinate3D& q1, const LegacyWorldCoordinate3D& q2); + + std::string toString() const; + + /*! @brief If aPoint stored in _polygon + */ + bool contains(const LegacyWorldCoordinate3D& aPoint) const; + + }; + } + } +} + +#endif diff --git a/code/masks/rttbField_LEGACY.h b/code/masks/rttbField_LEGACY.h new file mode 100644 index 0000000..63480b9 --- /dev/null +++ b/code/masks/rttbField_LEGACY.h @@ -0,0 +1,594 @@ +// ----------------------------------------------------------------------- +// 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 __FIELD_H +#define __FIELD_H + +#include +#include + +#include "rttbBaseType.h" +#include "rttbMaskType_LEGACY.h" +#include "rttbBaseType_LEGACY.h" +#include "rttbIndexOutOfBoundsException.h" + +namespace rttb{ + namespace masks{ + namespace legacy{ + /*! @class BaseField + @brief This is a class representing a field to be inherited. + */ + class BaseField + { + public: + BaseField(){} + + BaseField( Uint16 dimx_in , Uint16 dimy_in, Uint16 dimz_in ):dimx(dimx_in), dimy(dimy_in), dimz(dimz_in){} + + /*! @return extend of the field in x-direction. + */ + virtual Uint16 GetDimX(){ return dimx; } + + /*! @return extend of the field in y-direction. + */ + virtual Uint16 GetDimY(){ return dimy; } + + /*! @return extend of the field in z-direction. + */ + virtual Uint16 GetDimZ(){ return dimz; } + + protected: + Uint16 dimx; + Uint16 dimy; + Uint16 dimz; + }; + + /*! @class FieldOfScalar + @brief This is a class representing a field of scalar values + @todo uses UnsignedIndexList and other types from legacy code. Needs to be reimplemented. Close connection to + legacy mask need to be resolved. + */ + template + class FieldOfScalar : public BaseField + { + public: + /*! @param dimx_in field extend in direction x. + @param dimy_in field extend in direction y. + @param dimz_in field extend in direction z. + */ + FieldOfScalar( Uint16 dimx_in , Uint16 dimy_in, Uint16 dimz_in ): BaseField( dimx_in , dimy_in , dimz_in ) + { + create(); + } + + ~FieldOfScalar(){ + clear(); + } + + typedef TYPE type; + + /*! @brief Get the data stored in this FieldOfScalar at index (x,y,z) + @return value of interest + @exception IndexOutOfBoundsException + */ + inline TYPE GetData( Uint16 x, Uint16 y, Uint16 z ); + + /*! @brief Set all values to zero. + */ + void SetZero(); + + /*! @brief Set value at index (x,y,z) to value. + @exception IndexOutOfBoundsException + */ + inline void PutData( Uint16 x, Uint16 y, Uint16 z , TYPE value ); + + /*! @brief Check whether a 3D block of the length block_length with upper left corner (x,y,z) has the + intensity of value. + @return True if the entire block does have the value of interest. + */ + bool CheckBlockSameIntensity( Uint16 x, Uint16 y, Uint16 z , Uint16 block_length , TYPE value ); + + /*! @brief Set 3D block of the length block_length with upper left corner (x,y,z) to the value of input + parameter "value". + */ + void SetBlockThisIntensity( Uint16 x, Uint16 y, Uint16 z , Uint16 block_length , TYPE value ); + + /*! @brief This function investigates a specific area of the field, given by the parameter av. Those voxels + that are yet not clearly determined to be located inside or outside the contour and that are not part of + the border region are of interest. Depending on the neighbours of these voxels they are now defined as + inside or outside, if possible. + + @param av Specifies the region of interest within the field. + @param indexListInside The function fills this list with voxels that are determined inside during this + procedure. + @param indexListOutside The function fills this list with voxels that are determined outside during this + procedure. + @param unclear_intensity Intensity or value (does not necessarily have to be an intensity) that + characterizes uncertainty with respect to association (inside/outside). + @param inside_intensity Intensity or value (does not necessarily have to be an intensity) that + characterizes inside. + @param outside_intensity Intensity or value (does not necessarily have to be an intensity) that + characterizes outside. + */ + void GetBorderRegion( masks::legacy::area_vector av, + std::vector< masks::legacy::UnsignedIndexList >& indexListInside , + std::vector< masks::legacy::UnsignedIndexList >& indexListOutside, TYPE unclear_intensity, + TYPE inside_intensity , TYPE outside_intensity ); + + + /*! @returns true in case one of the voxels within the regarded block does have the value "value". + @param x x-position of the upper left corner of the block under investigation. + @param y y-position of the upper left corner of the block under investigation. + @param z z-position of the upper left corner of the block under investigation. + @param block_length lenght of the block that is processed. + */ + bool CheckOneOutOfThisBlockHasSameIntensity( Uint16 x, Uint16 y, Uint16 z , Uint16 block_length , TYPE value ); + + private: + /*! Return to initial state. + */ + void clear(); + + /*! Initialize all. + */ + void create(); + + TYPE* data; + + /*! Number of values within a slice. + */ + int slice_size; + /*! Number of values within a raw. + */ + Uint16 raw_size; + /*! Number of values within the field. + */ + int cube_size; + }; + + template + void FieldOfScalar::SetZero(){ + int size = ( dimz * dimy * dimx ) * sizeof(TYPE); + memset( data , 0 , size ); + } + + template + inline TYPE FieldOfScalar::GetData( Uint16 x, Uint16 y, Uint16 z ){ + TYPE val; + int offset; + + offset = ( z * slice_size + y * raw_size + x ) ; + + if(offset<0) + { + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + + if( offset <= ( cube_size - 1 ) ) + { + val = *( (TYPE*) data + offset ); + return val; + } + else{ + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + } + + template + inline void FieldOfScalar::PutData( Uint16 x, Uint16 y, Uint16 z, TYPE value ){ + int offset; + + if( x >= dimx ) + { + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + if( y >= dimy ) + { + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + if( z >= dimz ) + { + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + + offset = ( z * slice_size + y * raw_size + x ) ; + + if(offset<0) + { + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + + if( offset <= ( cube_size - 1 ) ) + { + *( (TYPE*) data + offset ) = value; + } + else{ + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + } + + template + void FieldOfScalar::clear(){ + free(data); + } + + template + void FieldOfScalar::create(){ + int size = ( dimz * dimy * dimx ) * sizeof(TYPE); + data = (type*) malloc( size ); + slice_size = dimy * dimx; + raw_size = dimx; + cube_size = dimy * dimx * dimz; + + memset( data , 0, cube_size * sizeof(TYPE) ); + } + + template + bool FieldOfScalar::CheckBlockSameIntensity( Uint16 x, Uint16 y, Uint16 z , Uint16 block_length , TYPE value ){ + if( x >= dimx ) + { + assert(0); + } + if( y >= dimy ) + { + assert(0); + } + if( z >= dimz ) + { + assert(0); + } + + Uint16 x_block_length = block_length; + if( ( (dimx-1) - x ) <= x_block_length ) + { + x_block_length = ( (dimx-1) - x ); + } + Uint16 y_block_length = block_length; + if( ( (dimy-1) - y ) <= y_block_length ) + { + y_block_length = ( (dimy-1) - y ); + } + + TYPE* local_data_pointer; + + for( int pos = 0 ; pos < y_block_length ; pos++ ){ + + int offset = ( z * slice_size + ( y + pos ) * raw_size + x ) ; + + if( offset <= ( cube_size - 1 ) ) + { + local_data_pointer = (TYPE*) (data + offset); + + for( Uint16 counter = 0 ; counter < x_block_length ; counter++ ){ + + if( *(local_data_pointer) != value ) + { + return false; + } + local_data_pointer++; + } + } // if offset + else{ + assert(0); + } + } // for pos + return true; + } + + template + void FieldOfScalar::SetBlockThisIntensity( Uint16 x, Uint16 y, Uint16 z , Uint16 block_length , TYPE value ){ + if( x >= dimx ) + { + assert(0); + } + if( y >= dimy ) + { + assert(0); + } + if( z >= dimz ) + { + assert(0); + } + + Uint16 x_block_length = block_length; + if( ( (dimx-1) - x ) <= x_block_length ) + { + x_block_length = ( (dimx-1) - x ); + } + Uint16 y_block_length = block_length; + if( ( (dimy-1) - y ) <= y_block_length ) + { + y_block_length = ( (dimy-1) - y ); + } + + TYPE* local_data_pointer; + + for( int pos = 0 ; pos < y_block_length ; pos++ ){ + int offset = ( z * slice_size + ( y + pos ) * raw_size + x ) ; + + if( offset <= ( cube_size - 1 ) ) + { + local_data_pointer = (TYPE*) (data + offset); + + for( Uint16 counter = 0 ; counter < x_block_length ; counter++ ){ + *(local_data_pointer) = value; + local_data_pointer++; + } + } // if offset + else{ + assert(0); + } + } // for pos + } + + template + void FieldOfScalar::GetBorderRegion( masks::legacy::area_vector av, + std::vector< masks::legacy::UnsignedIndexList >& indexListInside , + std::vector< masks::legacy::UnsignedIndexList >& indexListOutside, TYPE unclear_intensity, TYPE inside_intensity , + TYPE outside_intensity ){ + for( GridIndexType z = 0 ; z < dimz ; z++ ){ + for( GridIndexType y = av.at(z).index_begin.y ; y <= av.at(z).index_end.y ; y++ ){ + for( GridIndexType x = av.at(z).index_begin.x ; x <= av.at(z).index_end.x ; x++ ){ + + TYPE* data_pointer; + TYPE* data_pointer_check_neighbour; + + int offset = ( z * slice_size + y * raw_size + x ) ; + + data_pointer = (TYPE*) (data + offset); + + if( *data_pointer == unclear_intensity ) + { + bool its_inside = false; + bool its_outside = false; + + if( x > av.at(z).index_begin.x ) + { + int offset_neighbour = ( z * slice_size + y * raw_size + (x-1) ) ; + data_pointer_check_neighbour = (TYPE*) (data + offset_neighbour); + if( *data_pointer_check_neighbour == inside_intensity )its_inside = true; + if( *data_pointer_check_neighbour == outside_intensity)its_outside = true; + } + + if( x < av.at(z).index_end.x ) + { + int offset_neighbour = ( z * slice_size + y * raw_size + (x+1) ) ; + data_pointer_check_neighbour = (TYPE*) (data + offset_neighbour); + if( *data_pointer_check_neighbour == inside_intensity ) its_inside = true; + if( *data_pointer_check_neighbour == outside_intensity) its_outside = true; + } + + if( y > av.at(z).index_begin.y ) + { + int offset_neighbour = ( z * slice_size + (y-1) * raw_size + x ) ; + data_pointer_check_neighbour = (TYPE*) (data + offset_neighbour); + if( *data_pointer_check_neighbour == inside_intensity )its_inside = true; + if( *data_pointer_check_neighbour == outside_intensity)its_outside = true; + } + + if( y < av.at(z).index_end.y ) + { + int offset_neighbour = ( z * slice_size + (y+1) * raw_size + x ) ; + data_pointer_check_neighbour = (TYPE*) (data + offset_neighbour); + if( *data_pointer_check_neighbour == inside_intensity ) its_inside = true; + if( *data_pointer_check_neighbour == outside_intensity) its_outside = true; + } + + if( its_inside && its_outside ) + { + assert(0); + } + if( its_inside || its_outside ) + { + masks::legacy::LegacyUnsignedIndex3D index = {x,y,z}; + + if( its_inside ) + { + indexListInside.at(z).push_back(index); + } + if( its_outside ) + { + indexListOutside.at(z).push_back(index); + } + } + } + } + } + } + } + + template + bool FieldOfScalar::CheckOneOutOfThisBlockHasSameIntensity( Uint16 x, Uint16 y, Uint16 z , Uint16 block_length , + TYPE value ){ + if( x >= dimx ) + { + assert(0); + } + if( y >= dimy ) + { + assert(0); + } + if( z >= dimz ) + { + assert(0); + } + + bool its_fine = false; + + Uint16 x_block_length = block_length; + if( ( (dimx-1) - x ) <= x_block_length ) + { + x_block_length = ( (dimx-1) - x ); + } + Uint16 y_block_length = block_length; + if( ( (dimy-1) - y ) <= y_block_length ) + { + y_block_length = ( (dimy-1) - y ); + } + + TYPE* local_data_pointer; + + for( int pos = 0 ; pos < y_block_length ; pos++ ){ + + int offset = ( z * slice_size + ( y + pos ) * raw_size + x ) ; + + if( offset <= ( cube_size - 1 ) ) + { + local_data_pointer = (TYPE*) data + offset; + + for( int counter = 0 ; counter < x_block_length ; counter++ ){ + + if( *local_data_pointer == value ) + { + its_fine = true; + } + local_data_pointer++; + } + } // if offset + else{ + assert(0); + } + } // for pos + return its_fine; + } + + /*! @class FieldOfPointer + @brief This is a class representing a field of pointers to whatever. + */ + template + class FieldOfPointer : public BaseField + { + public: + FieldOfPointer( Uint16 dimx_in , Uint16 dimy_in, Uint16 dimz_in ): BaseField( dimx_in , dimy_in , dimz_in ) + { + create(); + } + + ~FieldOfPointer(){ + clear(); + } + + typedef TYPE type; + + /*! @brief Get the data stored in this FieldOfPointer at index (x,y,z) + @see FieldOfScalar. + */ + inline TYPE GetData( Uint16 x, Uint16 y, Uint16 z ); + + /*! @brief Set all entries to Null + @see FieldOfScalar. Here zero is NULL. + */ + void SetZero(); + + /*! @brief Set value at index (x,y,z) + @see FieldOfScalar. + */ + inline void PutData( Uint16 x, Uint16 y, Uint16 z , TYPE value ); + + private: + + void clear(); + + /*! Initialize all. + */ + void create(); + + TYPE* data; + + /*! Number of values within a slice. + */ + int slice_size; + /*! Number of values in a raw. + */ + Uint16 raw_size; + /*! Number of values within a cube. + */ + int cube_size; + }; + + template + void FieldOfPointer::SetZero(){ + for( int offset = 0 ; offset < cube_size ; offset++ ){ + delete *( (TYPE*) data + offset ); + *( (TYPE*) data + offset ) = NULL; + } + } + + template + inline TYPE FieldOfPointer::GetData( Uint16 x, Uint16 y, Uint16 z ){ + TYPE val; + int offset; + + offset = ( z * slice_size + y * raw_size + x ) ; + + if( offset <= ( cube_size - 1 ) ) + { + val = *( (TYPE*) data + offset ); + return val; + } + else{ + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + } + + + template + inline void FieldOfPointer::PutData( Uint16 x, Uint16 y, Uint16 z, TYPE value ){ + int offset; + + offset = ( z * slice_size + y * raw_size + x ) ; + + if( offset <= ( cube_size - 1 ) ) + { + delete *( (TYPE*) data + offset ); + *( (TYPE*) data + offset ) = value; + } + else{ + throw core::IndexOutOfBoundsException( " Out of range ! " ); + } + } + + template + void FieldOfPointer::clear(){ + for( int offset = 0 ; offset < cube_size ; offset++ ) delete *( (TYPE*) data + offset ); + free(data); + } + + template + void FieldOfPointer::create(){ + int size = ( dimz * dimy * dimx ) * sizeof(TYPE); + data = (type*) malloc( size ); + slice_size = dimy * dimx; + raw_size = dimx; + cube_size = dimy * dimx * dimz; + for( int offset = 0 ; offset < cube_size ; offset++ ){ + *( (TYPE*) data + offset ) = NULL; + } + } + } + } +} + + +#endif + + + + diff --git a/code/masks/rttbGenericMutableMaskAccessor.cpp b/code/masks/rttbGenericMutableMaskAccessor.cpp new file mode 100644 index 0000000..07cc417 --- /dev/null +++ b/code/masks/rttbGenericMutableMaskAccessor.cpp @@ -0,0 +1,178 @@ +// ----------------------------------------------------------------------- +// 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 "rttbGenericMutableMaskAccessor.h" +#include "rttbNullPointerException.h" + +#include + +#include +#include +#include + +namespace rttb +{ + + namespace masks + { + + + GenericMutableMaskAccessor::GenericMutableMaskAccessor(const core::GeometricInfo& aGeometricInfo) : + _geoInfo(aGeometricInfo), _spRelevantVoxelVector(MaskVoxelListPointer()) + { + //generate new structure set uid + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _maskUID = "GenericMutableMask_" + ss.str(); + } + + GenericMutableMaskAccessor::~GenericMutableMaskAccessor() {} + + void GenericMutableMaskAccessor::updateMask() {} + + GenericMutableMaskAccessor::MaskVoxelListPointer + GenericMutableMaskAccessor::getRelevantVoxelVector() + { + return _spRelevantVoxelVector; + } + + GenericMutableMaskAccessor::MaskVoxelListPointer GenericMutableMaskAccessor::getRelevantVoxelVector( + float lowerThreshold) + { + MaskVoxelListPointer filteredVoxelVectorPointer(new MaskVoxelList); + // filter relevant voxels + GenericMutableMaskAccessor::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 GenericMutableMaskAccessor::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) + { + GenericMutableMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); + + while (it != _spRelevantVoxelVector->end()) + { + if ((*it).getVoxelGridID() == aID) + { + voxel = (*it); + return true; + } + + it++; + } + + //aID is not in mask + voxel.setRelevantVolumeFraction(0); + } + + return false; + } + + bool GenericMutableMaskAccessor::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; + } + } + + void GenericMutableMaskAccessor::setMaskAt(VoxelGridID aID, const core::MaskVoxel& voxel) + { + //check if ID is valid + if (!_geoInfo.validID(aID)) + { + return; + } + + //determine how a given voxel on the dose grid is masked + if (_spRelevantVoxelVector) + { + GenericMutableMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); + + while (it != _spRelevantVoxelVector->end()) + { + if ((*it).getVoxelGridID() == aID) + { + (*it) = voxel; + return; + } + + it++; + } + + //not sID is not found in existing voxels + _spRelevantVoxelVector->push_back(voxel); + } + } + + void GenericMutableMaskAccessor::setMaskAt(const VoxelGridIndex3D& aIndex, + const core::MaskVoxel& voxel) + { + //convert VoxelGridIndex3D to VoxelGridID + VoxelGridID aVoxelGridID; + + if (_geoInfo.convert(aIndex, aVoxelGridID)) + { + setMaskAt(aVoxelGridID, voxel); + } + } + + void GenericMutableMaskAccessor::setRelevantVoxelVector(MaskVoxelListPointer aVoxelListPointer) + { + _spRelevantVoxelVector = MaskVoxelListPointer(aVoxelListPointer); + } + + } +} \ No newline at end of file diff --git a/code/masks/rttbGenericMutableMaskAccessor.h b/code/masks/rttbGenericMutableMaskAccessor.h new file mode 100644 index 0000000..71153a1 --- /dev/null +++ b/code/masks/rttbGenericMutableMaskAccessor.h @@ -0,0 +1,108 @@ +// ----------------------------------------------------------------------- +// 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 __GENERIC_MUTABLE_MASK_ACCESSOR_H +#define __GENERIC_MUTABLE_MASK_ACCESSOR_H + +#include "rttbMutableMaskAccessorInterface.h" +#include "rttbBaseType.h" +#include "rttbMaskVoxel.h" + +namespace rttb +{ + namespace masks + { + + /*! @class GenericMutableMaskAccessor + @brief Default implementation of MutableMaskAccessorInterface. + @see MutableMaskAccessorInterface + */ + class GenericMutableMaskAccessor: public core::MutableMaskAccessorInterface + { + public: + typedef core::MutableMaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef core::MutableMaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + + private: + core::GeometricInfo _geoInfo; + + /*! vector containing list of mask voxels*/ + MaskVoxelListPointer _spRelevantVoxelVector; + + IDType _maskUID; + + GenericMutableMaskAccessor(const + GenericMutableMaskAccessor&); //not implemented on purpose -> non-copyable + GenericMutableMaskAccessor& operator=(const + GenericMutableMaskAccessor&);//not implemented on purpose -> non-copyable + + public: + ~GenericMutableMaskAccessor(); + + GenericMutableMaskAccessor(const core::GeometricInfo& aGeometricInfo); + + /*! @brief initialize mask structure if _spRelevantVoxelVector was not previously initialized*/ + 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 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 if 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 is true if dose 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; + }; + + /*! @brief give access to GeometricInfo*/ + inline const core::GeometricInfo& getGeometricInfo() const + { + return _geoInfo; + }; + + IDType getMaskUID() const + { + return _maskUID; + }; + + + void setMaskAt(VoxelGridID aID, const core::MaskVoxel& voxel); + + void setMaskAt(const VoxelGridIndex3D& gridIndex, const core::MaskVoxel& voxel); + + void setRelevantVoxelVector(MaskVoxelListPointer aVoxelListPointer); + + }; + } +} +#endif \ No newline at end of file diff --git a/code/masks/rttbIterativePolygonInTermsOfIntersections_LEGACY.cpp b/code/masks/rttbIterativePolygonInTermsOfIntersections_LEGACY.cpp new file mode 100644 index 0000000..4dcfb3a --- /dev/null +++ b/code/masks/rttbIterativePolygonInTermsOfIntersections_LEGACY.cpp @@ -0,0 +1,1525 @@ +// ----------------------------------------------------------------------- +// 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) +*/ + +//Relocate? + +#include "rttbMask.h" +#include "rttbIterativePolygonInTermsOfIntersections_LEGACY.h" + +#include "rttbNullPointerException.h" + +#include +#include +#include +#include +#include + + + + +// Generelle Anmerkung: +// Koordinatensystem von Bild und Strukturen auf Kompatibilitaet pruefen! +// Strukturen auf Gutartigkeit pruefen: +// Ein Polygonzug darf sich nicht mit sich selbst schneiden. Ist eine Struktur innerhalb eines Slices durch +// zwei Polygonzuege repraesentiert, so muessen diese distinkt sein, duerfen eineander also nicht ueberschneiden! + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + + IterativePolygonInTermsOfIntersections::IterativePolygonInTermsOfIntersections(){ + number_of_intersections = -1; + } + + + bool IterativePolygonInTermsOfIntersections::selfTest(){ + + std::cout<< " Hallo, ich bin die funktion selfTest von IterativePolygonInTermsOfIntersections und ich bin noch nicht implementiert. Trotzdem returne ich true. Das musst du aendern. " < ( the_intersection_index_ref.intersection_index + ahead ) ){ + + if( the_intersection_index_ref.column_raw_or_unified == 0 ){ + + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index ).column_intersections.at( the_intersection_index_ref.intersection_index + ahead ); + + } + else if( the_intersection_index_ref.column_raw_or_unified == 1 ){ + + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index ).raw_intersections.at( the_intersection_index_ref.intersection_index + ahead ); + + } + else{ + + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index ).intersections_raw_and_column.at( the_intersection_index_ref.intersection_index + ahead ); + + } + + } + else{ + + if( GetRestpectiveIntersectionVectorLength( the_intersection_index_ref ) <= ( the_intersection_index_ref.intersection_index + ahead ) ){ + + GridIndexType control_index = ( ( the_intersection_index_ref.intersection_index + ahead ) - GetRestpectiveIntersectionVectorLength( the_intersection_index_ref ) ); + if( ( control_index >= 0 ) && ( control_index < GetRestpectiveIntersectionVectorLength( the_intersection_index_ref ) ) ){ + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index ).intersections_raw_and_column.at( ( ( the_intersection_index_ref.intersection_index + ahead ) - GetRestpectiveIntersectionVectorLength( the_intersection_index_ref ) ) ); + } + else assert(0); // the_intersection_index_ref and ahead have been initially unreasonalble + + } + + } + + return coordinates_return; + + } + + + bool IterativePolygonInTermsOfIntersections::PointIndexIsFine( IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_ref ){ + + if( ( Polygon_Intersections.size() > ( the_intersection_index_ref.point_index ) ) && ( ( the_intersection_index_ref.point_index ) >= 0 ) ){ + return true; + } + else return false; + + } + + + LegacyWorldCoordinate3D IterativePolygonInTermsOfIntersections::GetRestpectivePologonPointInVoxelCoordinates( IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_ref ){ + + LegacyWorldCoordinate3D coordinates_return; + if( PointIndexIsFine( the_intersection_index_ref ) ){ + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index ).contour_point_voxel_coord; + } + else{ + // exception to be implemented + assert(0); + } + return coordinates_return; + + } + + + LegacyWorldCoordinate3D IterativePolygonInTermsOfIntersections::GetRestpectivePologonPointInVoxelCoordinatesFurther( IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_ref , int ahead ){ + + // Baustelle zyklisch machen + + LegacyWorldCoordinate3D coordinates_return; + + if( Polygon_Intersections.size() > ( the_intersection_index_ref.point_index + ahead ) ){ + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index + ahead ).contour_point_voxel_coord; + } + else{ + + if( ( ahead - ( Polygon_Intersections.size() - the_intersection_index_ref.point_index ) ) >= Polygon_Intersections.size() ) assert( 0 ); // this must never happen. Probably the contour consists of just one point. That does not make any sense in context of radiotherapy. + + coordinates_return = Polygon_Intersections.at( ahead - ( Polygon_Intersections.size() - the_intersection_index_ref.point_index ) ).contour_point_voxel_coord; + + } + + return coordinates_return; + + } + + + LegacyWorldCoordinate3D IterativePolygonInTermsOfIntersections::GetRestpectivePologonPointInVoxelCoordinatesPrevious( IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_ref , unsigned int back ){ + + LegacyWorldCoordinate3D coordinates_return; + + if( ( ( the_intersection_index_ref.point_index ) >= back ) && ( ( the_intersection_index_ref.point_index ) < ( Polygon_Intersections.size() + back ) ) ){ + + coordinates_return = Polygon_Intersections.at( the_intersection_index_ref.point_index - back ).contour_point_voxel_coord; + + } + else{ + + if( the_intersection_index_ref.point_index < back ){ + + int control_index = ( Polygon_Intersections.size() - ( back - the_intersection_index_ref.point_index ) ); + + if( control_index < 0 ){ + assert(0); // This must never happen. Probably the contour consists of just one point. Each point has already been regarded. That does not make any sense in radiotherapy. + } + + coordinates_return = Polygon_Intersections.at( Polygon_Intersections.size() - ( back - the_intersection_index_ref.point_index ) ).contour_point_voxel_coord; + + } + + if( ! ( ( the_intersection_index_ref.point_index ) < ( Polygon_Intersections.size() + back ) ) ){ + + assert(0); // that can't be right the_intersection_index_ref.point_index must have been initially unreasonable + + } + + } + + return coordinates_return; + + } + + + LegacyWorldCoordinate3D IterativePolygonInTermsOfIntersections::GetFirstPointThisPolygon( IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_ref ){ + + LegacyWorldCoordinate3D first_one_this_Polygon(0,0,0); + + if( Polygon_Intersections.size() > 0 ){ + first_one_this_Polygon = Polygon_Intersections.at( 0 ).contour_point_voxel_coord; + return first_one_this_Polygon; + } + else{ + assert(0); + // exception implementieren + } + + return first_one_this_Polygon; + + } + + + + void IterativePolygonInTermsOfIntersections::ResetIntersectionIndex( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_set_to ){ + current_index_internal.point_index = intersection_set_to.point_index ; + current_index_internal.intersection_index = intersection_set_to.intersection_index ; + current_index_internal.column_raw_or_unified = intersection_set_to.column_raw_or_unified ; + } + + + // index goes in for debug and will be removed later ... + bool IterativePolygonInTermsOfIntersections::RunForwardToNextIntersection( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref , LegacyDoseVoxelIndex3D the_dose_index ){ + + + bool success = false; + bool done = false; + bool success_first_increment = false; + + RememberPosition(); + + if( ThisIntersection( point_of_interest_start_ref ) ){ + + snippet_intermediate_content_ref.clear(); + + if( ! CloseWithoutIncrement( point_of_interest_start_ref, point_of_interest_end_ref , snippet_intermediate_content_ref ) ){ + + PeriodicPolygonPointIncrement(); + + AppendThisIntermediatePoint( snippet_intermediate_content_ref ); + + while( !done ){ + + // done = AppendIntermediatePointOrCloseForward( point_of_interest_start_ref , point_of_interest_end_ref , snippet_intermediate_content_ref ); + done = CloseWithoutIncrement( point_of_interest_start_ref, point_of_interest_end_ref , snippet_intermediate_content_ref ); + + if( !done ){ + + PeriodicPolygonPointIncrement(); + + AppendThisIntermediatePoint( snippet_intermediate_content_ref ); + + } + + } + + } + + } + else assert(0); + + CleanOut( point_of_interest_start_ref, point_of_interest_end_ref , snippet_intermediate_content_ref ); + + JumpToRememberedPosition(); + + return success; + + } + + + void IterativePolygonInTermsOfIntersections::CleanOut( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ){ + + if( snippet_intermediate_content_ref.size() > 0 ){ + + bool im_done_a = false; + while( (!im_done_a) && (snippet_intermediate_content_ref.size() > 0) ){ + + if( ( point_of_interest_start_ref.x == snippet_intermediate_content_ref.at( 0 ).x ) && ( point_of_interest_start_ref.y == snippet_intermediate_content_ref.at( 0 ).y ) )snippet_intermediate_content_ref.erase( snippet_intermediate_content_ref.begin() ); + else im_done_a = true; + + } + + bool im_done_b = false; + while( (!im_done_b) && (snippet_intermediate_content_ref.size() > 0) ){ + + if( ( point_of_interest_end_ref.x == snippet_intermediate_content_ref.at( snippet_intermediate_content_ref.size() - 1 ).x ) && ( point_of_interest_end_ref.y == snippet_intermediate_content_ref.at( snippet_intermediate_content_ref.size() - 1 ).y ) )snippet_intermediate_content_ref.pop_back(); + else im_done_b = true; + + } + + } + + } + + + bool IterativePolygonInTermsOfIntersections::RunBackwardToNextIntersection( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ){ + + bool success = false; + bool done = false; + bool success_first_increment = false; + + RememberPosition(); + + if( ThisIntersection( point_of_interest_start_ref ) ){ + + snippet_intermediate_content_ref.clear(); + + + if( ! CloseWithoutDecrement( point_of_interest_start_ref, point_of_interest_end_ref , snippet_intermediate_content_ref ) ){ + + + AppendThisIntermediatePoint( snippet_intermediate_content_ref ); + + PeriodicPolygonPointDecrement(); + + while( ( !done ) ){ + + done = CloseWithoutDecrement( point_of_interest_start_ref, point_of_interest_end_ref , snippet_intermediate_content_ref ); + + if( !done ){ + + AppendThisIntermediatePoint( snippet_intermediate_content_ref ); + + PeriodicPolygonPointDecrement(); + + } + + } + + } + + } + else assert(0); // This must never happen ! + + CleanOut( point_of_interest_start_ref, point_of_interest_end_ref , snippet_intermediate_content_ref ); + + JumpToRememberedPosition(); + + return success; + + } + + + bool IterativePolygonInTermsOfIntersections::AppendIntermediatePointOrCloseForward( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ){ + + bool got_it = false; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() != 0 ){ + + + while( current_index_internal.intersection_index < ( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1 ) ){ + + current_index_internal.intersection_index++; + LegacyWorldCoordinate2D point_of_interest = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + + + } + + + if( ( static_cast( point_of_interest_start_ref.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at(0).x) ) || ( static_cast( point_of_interest_start_ref.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at(0).y ) ) ){ + + got_it = true; + + point_of_interest_end_ref = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( 0 ); + + snippet_intermediate_content_ref.push_back( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord ); + + } + else{ + + // Anmerkung : man kann die else Bedingung auch oben in die if Bedingung mit reinpacken, je nachdem, was der code-reviewer leserlicher findet + // sie sind gleich. Die Frage ist nun, ob es Zwischenpunkte gab, ob die Punkte also noch, oder wieder gleich sind. + if( snippet_intermediate_content_ref.size() > 0 ){ + + got_it = true; + + point_of_interest_end_ref = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( 0 ); + + snippet_intermediate_content_ref.push_back( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord ); + + } + + } + + + } + else{ + + snippet_intermediate_content_ref.push_back( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord ); + + } + + return got_it; + + } + + + + bool IterativePolygonInTermsOfIntersections::AppendIntermediatePointOrCloseBackward( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ){ + + + if( current_index_internal.intersection_index != ( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1 ) ) assert(0); //this should not happen + + while( current_index_internal.intersection_index > 0 ){ + + current_index_internal.intersection_index--; + LegacyWorldCoordinate2D point_of_interest = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + if( ( static_cast( point_of_interest.x ) != static_cast( point_of_interest_start_ref.x ) ) && ( static_cast( point_of_interest.y ) != static_cast( point_of_interest_start_ref.y ) ) ){ + + point_of_interest_end_ref = point_of_interest; + return true; + + } + + } + + + snippet_intermediate_content_ref.push_back( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord ); + + return false; + + } + + + void IterativePolygonInTermsOfIntersections::AppendThisIntermediatePoint( std::vector& snippet_intermediate_content_ref ){ + snippet_intermediate_content_ref.push_back( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord ); + } + + + bool IterativePolygonInTermsOfIntersections::IncrementNeeded(){ + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() <= ( current_index_internal.intersection_index + 1 ) ) return true; + return false; + } + + + bool IterativePolygonInTermsOfIntersections::DecrementNeeded(){ + bool is_needed = false; + if( current_index_internal.intersection_index < 1 ) is_needed = true; + return is_needed; + } + + + bool IterativePolygonInTermsOfIntersections::CloseWithoutIncrement( LegacyWorldCoordinate2D& point_of_interest_start_ref, LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ){ + + LegacyWorldCoordinate2D point_of_interest; + + if( CheckIndex() ){ + + point_of_interest = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + + if( ( ( static_cast( point_of_interest.x ) != static_cast( point_of_interest_start_ref.x ) ) || ( static_cast( point_of_interest.y ) != static_cast( point_of_interest_start_ref.y ) ) ) || OneDiffers( snippet_intermediate_content_ref , point_of_interest_start_ref ) ){ + + point_of_interest_end_ref = point_of_interest; + + return true; + + } + + } + + if( ! IncrementNeeded() ){ + + while( current_index_internal.intersection_index < ( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1 ) ){ + + current_index_internal.intersection_index++; + + point_of_interest = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + + if( ( static_cast( point_of_interest.x ) != static_cast( point_of_interest_start_ref.x ) ) || ( static_cast( point_of_interest.y ) != static_cast( point_of_interest_start_ref.y ) ) ){ + + + point_of_interest_end_ref = point_of_interest; + + + return true; + + } + + } + + return false; + + } + else return false; + + } + + + + bool IterativePolygonInTermsOfIntersections::OneDiffers( std::vector& snippet_intermediate_content_ref , LegacyWorldCoordinate2D& point_of_interest_start_ref ){ + + for( GridIndexType i = 0 ; i < snippet_intermediate_content_ref.size() ; i++ ){ + + LegacyWorldCoordinate3D a_point = snippet_intermediate_content_ref.at( i ); + + if( ( a_point.x != point_of_interest_start_ref.x ) || ( a_point.y != point_of_interest_start_ref.y ) )return true; + + } + + return false; + + } + + + + bool IterativePolygonInTermsOfIntersections::CloseWithoutDecrement( LegacyWorldCoordinate2D& point_of_interest_start_ref, LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ){ + + LegacyWorldCoordinate2D point_of_interest; + + if(CheckIndex()){ + + point_of_interest = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + + if( ( ( static_cast( point_of_interest.x ) != static_cast( point_of_interest_start_ref.x ) ) || ( static_cast( point_of_interest.y ) != static_cast( point_of_interest_start_ref.y ) ) ) || OneDiffers( snippet_intermediate_content_ref , point_of_interest_start_ref ) ){ + + point_of_interest_end_ref = point_of_interest; + + return true; + + } + + } + + if( ! DecrementNeeded() ){ + + while( current_index_internal.intersection_index > 0 ){ + + current_index_internal.intersection_index--; + + point_of_interest = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + + if( ( static_cast( point_of_interest.x ) != static_cast( point_of_interest_start_ref.x ) ) || ( static_cast( point_of_interest.y ) != static_cast( point_of_interest_start_ref.y ) ) ){ + + point_of_interest_end_ref = point_of_interest; + + return true; + + } + + } + + return false; + + + } + else return false; + + } + + + + void IterativePolygonInTermsOfIntersections::PeriodicPolygonPointIncrement(){ + if( current_index_internal.point_index < ( Polygon_Intersections.size() - 1 ) ){ + current_index_internal.point_index++; + } + else{ + current_index_internal.point_index = 0; + } + current_index_internal.intersection_index = 0; + } + + + void IterativePolygonInTermsOfIntersections::PeriodicPolygonPointDecrement(){ + if( current_index_internal.point_index > 0 ){ + current_index_internal.point_index--; + } + else{ + current_index_internal.point_index = ( Polygon_Intersections.size() - 1 ); + } + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ) current_index_internal.intersection_index = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1 ; + else current_index_internal.intersection_index = 0; + } + + + + /// + /// returns true, in case intersection + /// + bool IterativePolygonInTermsOfIntersections::CheckIsIntersection( LegacyWorldCoordinate2D& edge_coord , IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_ref ){ + // Baustelle all control paths should return a value + + LegacyWorldCoordinate2D local_current_position = {0,0}; + + RememberPosition(); + + ResetIntersectionIndex(); + + bool was_able_to_increment = true; + + while( was_able_to_increment == true ){ + + // if( CheckToBeRegarded() ){ + + if( ThisIntersection( local_current_position ) ){ + + + if( ( static_cast( edge_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( edge_coord.y ) == static_cast( local_current_position.y ) ) ){ + + IterativePolygonInTermsOfIntersections::WhichIntersection& current_index_internal_ref = current_index_internal; + CopyIntersectionIndex( the_intersection_index_ref , current_index_internal_ref ); + + JumpToRememberedPosition(); + + return true; + + } + + } + + // } + + was_able_to_increment = IncremtentIntersection(); + + } + + JumpToRememberedPosition(); + + return false; + + } + + + void IterativePolygonInTermsOfIntersections::CopyIntersectionIndex( IterativePolygonInTermsOfIntersections::WhichIntersection& to_ref , IterativePolygonInTermsOfIntersections::WhichIntersection& from_ref ){ + to_ref.point_index = from_ref.point_index; + to_ref.intersection_index = from_ref.intersection_index; + to_ref.column_raw_or_unified = from_ref.column_raw_or_unified; + } + + + bool IterativePolygonInTermsOfIntersections::IncremtentIntersection(){ + + + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > ( current_index_internal.intersection_index + 1 ) ){ + current_index_internal.intersection_index++; + return true; + } + else{ + + bool success = false; + + while( success == false ){ + + if( Polygon_Intersections.size() > ( current_index_internal.point_index + 1 ) ){ + + current_index_internal.intersection_index = 0; + current_index_internal.point_index++; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ){ + + return true; + + } + + } + else return false; + + } + + } + + + + return false; + + } + + + bool IterativePolygonInTermsOfIntersections::IncremtentIntersectionPeriodically(){ + + if( ( CheckIndex() ) && ( ThereAreIntersections() ) ){ + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > ( current_index_internal.intersection_index + 1 ) ){ + current_index_internal.intersection_index++; + return true; + } + else{ + + bool success = false; + + while( success == false ){ + + if( Polygon_Intersections.size() > ( current_index_internal.point_index + 1 ) ){ + + current_index_internal.intersection_index = 0; + current_index_internal.point_index++; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ){ + + return true; + + } + + } + else{ + + current_index_internal.intersection_index = 0; + current_index_internal.point_index = 0; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ){ + + return true; + + } + + } + + } + + } + + } + else return false; + return false; + + } + + + bool IterativePolygonInTermsOfIntersections::CheckIndex(){ + + if( Polygon_Intersections.size() > current_index_internal.point_index ){ + + if( ( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > current_index_internal.intersection_index ) ){ + return true; + } + else return false; + + } + else return false; + + } + + + // Caution : This function can end up with a bad intersedction index. + bool IterativePolygonInTermsOfIntersections::DecrementPointOrIntersectionPeriodically(){ + + + if( /*CheckIndex() &&*/ ( ThereAreIntersections() ) ){ + + if( 0 < ( current_index_internal.intersection_index ) ){ + current_index_internal.intersection_index--; + return true; + } + else{ + + if( ( 0 < ( current_index_internal.point_index ) ) ){ + + + current_index_internal.point_index--; + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ) current_index_internal.intersection_index = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1; + else current_index_internal.intersection_index = 0; + + return true; + + + } + else if( Polygon_Intersections.size() > 0 ){ + + current_index_internal.point_index = Polygon_Intersections.size() - 1; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ) current_index_internal.intersection_index = ( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1 ); + else current_index_internal.intersection_index = 0; + + + } + else assert(0); // this should never happen since there are intersections, see ThereAreIntersections() + + } + + } + else{ + std::cout<< " could not decrement !!!!!!!" < 0 ) current_index_internal.intersection_index = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1; + else current_index_internal.intersection_index = 0; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ){ + + return true; + + } + + } + + if( current_index_internal.point_index != 0 ){ + assert(0); // This should never happen. + } + else if( Polygon_Intersections.size() > 0 ){ + + current_index_internal.point_index = Polygon_Intersections.size() - 1; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ) current_index_internal.intersection_index = ( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1 ); + else{ + + + while( ( 0 < ( current_index_internal.point_index ) ) ){ + + current_index_internal.point_index--; + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ) current_index_internal.intersection_index = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() - 1; + else current_index_internal.intersection_index = 0; + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > 0 ){ + + return true; + + } + + } + + assert(0); // should never get here, since there are interesections + + } + + } + else assert(0); // this should never happen since there are intersections, see ThereAreIntersections() + + } + + } + else return false; + return false; + } + + + bool IterativePolygonInTermsOfIntersections::CheckToBeRegarded(){ + + return Polygon_Intersections.at( current_index_internal.point_index ).to_be_Regarded; + + } + + + bool IterativePolygonInTermsOfIntersections::PreviousIntersectionPeriodically( LegacyWorldCoordinate2D& the_intersection ){ + + if( DecrementIntersectionPeriodically() ){ + the_intersection = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + return true; + } + else{ + the_intersection.x =0; + the_intersection.y = 0; + return false; + } + return false; + + } + + + + bool IterativePolygonInTermsOfIntersections::IsInside( LegacyWorldCoordinate3D& point_check , LegacyDoseVoxelIndex3D& the_dose_index ){ + + if( ( point_check.x > the_dose_index.x ) && ( point_check.x < ( the_dose_index.x + 1 ) ) && ( point_check.y > the_dose_index.y ) && ( point_check.y < ( the_dose_index.y + 1 ) ) ){ + + return true; + + } + else return false; + + } + + + + + + bool IterativePolygonInTermsOfIntersections::ThisIntersection( LegacyWorldCoordinate2D& the_intersection ){ + + if( CheckIndex() ){ + the_intersection = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + return true; + } + else{ + the_intersection.x =0; + the_intersection.y = 0; + return false; + } + + } + + + void IterativePolygonInTermsOfIntersections::ShowSelf(){ + + if( Polygon_Intersections.size() > 0 ){ + + for( GridIndexType counter = 0 ; counter < Polygon_Intersections.size() ; counter++ ){ + + std::cout<< " Polygon_Intersections.at( counter ).contour_point_voxel_coord.z : " << Polygon_Intersections.at( counter ).contour_point_voxel_coord.z <= 78 ) && ( Polygon_Intersections.at( counter ).contour_point_voxel_coord.x <= 79 ) && ( Polygon_Intersections.at( counter ).contour_point_voxel_coord.y >= 70 ) && ( Polygon_Intersections.at( counter ).contour_point_voxel_coord.y <= 71 ) ){ + std::cout<< " Polygon_Intersections.at( counter ).contour_point_voxel_coord.x : " << Polygon_Intersections.at( counter ).contour_point_voxel_coord.x <= 78 ) && ( Polygon_Intersections.at( counter ).intersections_raw_and_column.at( internal_counter ).x <= 79 ) && ( Polygon_Intersections.at( counter ).intersections_raw_and_column.at( internal_counter ).y >= 70 ) && ( Polygon_Intersections.at( counter ).intersections_raw_and_column.at( internal_counter ).y <= 71 ) ){ + std::cout<< " Polygon bla ns_raw_and_column.at( internal_counter ).x : " << Polygon_Intersections.at( counter ).intersections_raw_and_column.at( internal_counter ).x <( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) ) ){ + +//Hier erst den Kontur-Punkt anschauen +//erst wenn der nicht geht, dann schauen, ob es einen Schnittpunkt gibt und den Pruefen. +//Dabei darauf achten, +//dass der current_index_internal.intersection_index null sein kann, obwohl es kein nulltes Element gibt !! + + + next_point_or_intersection.x =Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x; + next_point_or_intersection.y = Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y; + + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( next_point_or_intersection.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( next_point_or_intersection.y ) ) ) return true; + + } + else{ + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() <= current_index_internal.intersection_index ) assert(0); // this should never happen, since IncrementIntersection should not end up with a bad index + + next_point_or_intersection = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( next_point_or_intersection.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( next_point_or_intersection.y ) ) ) return true; + + + } + + } + else{ + + assert(0); + + return false; + + } + + return false; + + + } + + + + + + bool IterativePolygonInTermsOfIntersections::PreviousPointOrIntersectionPeriodically( LegacyWorldCoordinate2D& previous_point_or_intersection , LegacyWorldCoordinate2D& point_or_intersection , LegacyDoseVoxelIndex3D& the_dose_index ){ + + if( /*( IsInside( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord , the_dose_index ) ) && */ ( current_index_internal.intersection_index == 0 ) && ( ( static_cast( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) ) ){ + + point_or_intersection.x =Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x; + point_or_intersection.y = Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y; + + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) )return true; + + } + else{ + + + if( DecrementPointOrIntersectionPeriodically() ){ + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > current_index_internal.intersection_index ){ + + point_or_intersection = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) )return true; + + } + else{ + + point_or_intersection.x =Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x; + point_or_intersection.y = Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y; + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) )return true; + + } + + } + else{ + + + bool decrement = true; + + while(decrement){ + + decrement = DecrementPointOrIntersectionPeriodically(); + + if( decrement ){ + + if( Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.size() > current_index_internal.intersection_index ){ + + point_or_intersection = Polygon_Intersections.at( current_index_internal.point_index ).intersections_raw_and_column.at( current_index_internal.intersection_index ); + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) )return true; + + } + else{ + + point_or_intersection.x =Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x; + point_or_intersection.y = Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y; + if( ( static_cast( previous_point_or_intersection.x ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.x ) ) || ( static_cast( previous_point_or_intersection.y ) != static_cast( Polygon_Intersections.at( current_index_internal.point_index ).contour_point_voxel_coord.y ) ) )return true; + + } + + } + + + } + + assert(0); // this must not happen + + + } + return false; + + + } + + return false; + + } + + + int IterativePolygonInTermsOfIntersections::MaximumNumberOfElements(){ + + if( number_of_intersections == (-1) ){ + + int resulting_number = 0; + + for( GridIndexType counter = 0 ; counter < Polygon_Intersections.size() ; counter++ ){ + + resulting_number += Polygon_Intersections.at( counter ).intersections_raw_and_column.size(); + + } + + number_of_intersections = resulting_number; + + return number_of_intersections; + + } + else return number_of_intersections; + + } + + + bool IterativePolygonInTermsOfIntersections::ThereAreIntersections(){ + if( number_of_intersections == (-1) ) number_of_intersections = MaximumNumberOfElements(); + if( number_of_intersections > 0 )return true; + else return false; + } + + + void IterativePolygonInTermsOfIntersections::CreateUnifiedListOfRawAndColumnIntersections(){ + + // iteration along polygon + + for( unsigned int point_index = 0 ; point_index < ( Polygon_Intersections.size() ); point_index++ ){ + + if( Polygon_Intersections.at( point_index ).intersections_raw_and_column.size() == 0 ){ + + bool do_continue = true; + + Polygon_Intersections.at( point_index ).intersections_raw_and_column.clear(); + + unsigned int raw_index = 0; + unsigned int column_index = 0; + + LegacyWorldCoordinate3D coordinates_return = Polygon_Intersections.at( point_index ).contour_point_voxel_coord; + + std::vector raw_intersections_work = Polygon_Intersections.at( point_index ).raw_intersections; + std::vector& raw_intersections_work_ref = raw_intersections_work; + std::vector column_intersections_work = Polygon_Intersections.at( point_index ).column_intersections; + std::vector& column_intersections_work_ref = column_intersections_work; + + double last_distance = -1; + + while( do_continue ){ + + LegacyWorldCoordinate2D coordinates_raw_intersection; + coordinates_raw_intersection.x =0; + coordinates_raw_intersection.y = 0; + + LegacyWorldCoordinate2D coordinates_column_intersection; + coordinates_column_intersection.x =0; + coordinates_column_intersection.y = 0; + + LegacyWorldCoordinate2D coordinates_final_intersection; + coordinates_final_intersection.x =0; + coordinates_final_intersection.y = 0; + + double distance_raw = 1000000000; + double distance_column = 1000000000; + + if( raw_intersections_work.size() > 0 ){ + + coordinates_raw_intersection = raw_intersections_work.at( 0 ); + distance_raw = ( coordinates_raw_intersection.x - coordinates_return.x ) * ( coordinates_raw_intersection.x - coordinates_return.x ) + ( coordinates_raw_intersection.y - coordinates_return.y ) * ( coordinates_raw_intersection.y - coordinates_return.y ); + distance_raw = sqrt( distance_raw ); + if( distance_raw < last_distance )assert(0); // this must never happen + + } + + if( column_intersections_work.size() > 0 ){ + + coordinates_column_intersection = column_intersections_work.at( 0 ); + distance_column = ( coordinates_column_intersection.x - coordinates_return.x ) * ( coordinates_column_intersection.x - coordinates_return.x ) + ( coordinates_column_intersection.y - coordinates_return.y ) * ( coordinates_column_intersection.y - coordinates_return.y ); + distance_column = sqrt( distance_column ); + if( distance_column < last_distance )assert(0); // this must never happen + + } + + if( ( distance_raw != 1000000000 ) && ( distance_column != 1000000000 ) ){ + + if( distance_raw < distance_column ){ + coordinates_final_intersection = raw_intersections_work.at( raw_index ); + Polygon_Intersections.at( point_index ).intersections_raw_and_column.push_back( coordinates_raw_intersection ); + last_distance = distance_raw; + } + else if( distance_raw > distance_column ){ + coordinates_final_intersection = column_intersections_work.at( column_index ); + Polygon_Intersections.at( point_index ).intersections_raw_and_column.push_back( coordinates_column_intersection ); + last_distance = distance_column; + } + else if( distance_raw == distance_column ){ + coordinates_final_intersection = column_intersections_work.at( column_index ); + Polygon_Intersections.at( point_index ).intersections_raw_and_column.push_back( coordinates_column_intersection ); + // man muss diesen Punkt nicht zweimal beachten. + // deshalb nicht : Polygon_Intersections.at( point_index ).intersections_raw_and_column.push_back( coordinates_raw_intersection ); + last_distance = distance_column; + } + + } + else if( distance_raw != 1000000000 ){ + coordinates_final_intersection = raw_intersections_work.at( raw_index ); + Polygon_Intersections.at( point_index ).intersections_raw_and_column.push_back( coordinates_raw_intersection ); + last_distance = distance_raw; + } + else if( distance_column != 1000000000 ){ + coordinates_final_intersection = column_intersections_work.at( column_index ); + Polygon_Intersections.at( point_index ).intersections_raw_and_column.push_back( coordinates_column_intersection ); + last_distance = distance_column; + } + else{ + + do_continue = false; + + } + + /* std::cout << " Here are the values : " << std::endl; + + std::cout << " coordinates_return.x : " << coordinates_return.x << std::endl; + std::cout << " coordinates_return.y : " << coordinates_return.y << std::endl; + + + + std::cout << " coordinates_final_intersection.x : " << coordinates_final_intersection.x << std::endl; + std::cout << " coordinates_final_intersection.y : " << coordinates_final_intersection.y << std::endl; + + for( int counter = 0 ; counter < Polygon_Intersections.at( point_index ).intersections_raw_and_column.size() ; counter++ ){ + + std::cout << " Polygon_Intersections.at( point_index ).intersections_raw_and_column.at( counter ).x : " << Polygon_Intersections.at( point_index ).intersections_raw_and_column.at( counter ).x << std::endl; + std::cout << " Polygon_Intersections.at( point_index ).intersections_raw_and_column.at( counter ).y : " << Polygon_Intersections.at( point_index ).intersections_raw_and_column.at( counter ).y << std::endl; + + } + + for( int counter = 0 ; counter < raw_intersections_work.size() ; counter++ ){ + + std::cout << " raw_intersections_work.at( counter ).x : " << raw_intersections_work.at( counter ).x << std::endl; + std::cout << " raw_intersections_work.at( counter ).y : " << raw_intersections_work.at( counter ).y << std::endl; + + } + + + for( int counter = 0 ; counter < column_intersections_work.size() ; counter++ ){ + + std::cout << " column_intersections_work.at( counter ).x : " << column_intersections_work.at( counter ).x << std::endl; + std::cout << " column_intersections_work.at( counter ).y : " << column_intersections_work.at( counter ).y << std::endl; + + } */ + + + if( ( distance_raw != 1000000000 ) || ( distance_column != 1000000000 ) ) removeThisCoordinateFromBothLists( coordinates_final_intersection , raw_intersections_work_ref , column_intersections_work_ref ); + + } + + } + + } + + } + + + + void IterativePolygonInTermsOfIntersections::removeThisCoordinateFromBothLists( LegacyWorldCoordinate2D the_coordinate , std::vector& raw_intersections_work_ref , std::vector& column_intersections_work_ref ){ + + LegacyWorldCoordinate2D coordinates_this_intersection; + coordinates_this_intersection.x =0; + coordinates_this_intersection.y = 0; + + std::vector::iterator iter; + + + for( GridIndexType counter = 0 ; counter < raw_intersections_work_ref.size() ; counter++ ){ + coordinates_this_intersection = raw_intersections_work_ref.at( counter ); + if( ( coordinates_this_intersection.x == the_coordinate.x ) && ( coordinates_this_intersection.y == the_coordinate.y ) ){ + iter = raw_intersections_work_ref.begin(); + raw_intersections_work_ref.erase( iter ); + } + else break; + } + + for( GridIndexType counter = 0 ; counter < column_intersections_work_ref.size() ; counter++ ){ + coordinates_this_intersection = column_intersections_work_ref.at( counter ); + if( ( coordinates_this_intersection.x == the_coordinate.x ) && ( coordinates_this_intersection.y == the_coordinate.y ) ){ + iter = column_intersections_work_ref.begin(); + column_intersections_work_ref.erase( iter ); + } + else break; + } + + } + + + + bool IterativePolygonInTermsOfIntersections::ItIsAsExpected( masks::legacy::PolygonInTermsOfIntersections Expected_Unified_Polygon_Intersections ){ + + + if( Expected_Unified_Polygon_Intersections.size() != Polygon_Intersections.size() ) return false; + + for( GridIndexType a_counter = 0 ; a_counter < Expected_Unified_Polygon_Intersections.size() ; a_counter++ ){ + + if( ! IdenticalUniList( Expected_Unified_Polygon_Intersections.at( a_counter ) , Polygon_Intersections.at( a_counter ) ) ) return false; + + } + + return true; + + } + + + + bool IterativePolygonInTermsOfIntersections::IdenticalUniList( masks::legacy::PointLevelVector plv_a , masks::legacy::PointLevelVector plv_b ){ + + if( plv_a.intersections_raw_and_column.size() != plv_b.intersections_raw_and_column.size() ) return false; + + LegacyWorldCoordinate2D wco2da; + wco2da.x =0; + wco2da.y = 0; + + LegacyWorldCoordinate2D wco2db; + wco2db.x =0; + wco2db.y = 0; + + for( GridIndexType this_counter = 0 ; this_counter < plv_a.intersections_raw_and_column.size() ; this_counter++ ){ + wco2da = plv_a.intersections_raw_and_column.at( this_counter ); + wco2db = plv_b.intersections_raw_and_column.at( this_counter ); + if( ( static_cast( wco2da.x ) != static_cast( wco2db.x ) ) || ( static_cast( wco2da.y ) != static_cast( wco2db.y ) ) ) return false; + } + + return true; + + } + + + void IterativePolygonInTermsOfIntersections::RememberPosition(){ + + IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_to_ref = current_index_internal_remember; + IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_from_ref = current_index_internal; + + CopyIntersectionIndex( intersection_index_to_ref , intersection_index_from_ref ); + + } + + + void IterativePolygonInTermsOfIntersections::JumpToRememberedPosition(){ + + IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_from_ref = current_index_internal_remember; + IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_to_ref = current_index_internal; + + CopyIntersectionIndex( intersection_index_to_ref , intersection_index_from_ref ); + + } + + + void IterativePolygonInTermsOfIntersections::SetCurrentIntersectionIndex( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_new_ref ){ + + IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_to_ref = current_index_internal; + + CopyIntersectionIndex( intersection_index_to_ref , intersection_index_new_ref ); + + } + + + bool IterativePolygonInTermsOfIntersections::CheckCurrentIntersectionIndexIdentical( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_compare_ref ){ + + if( ( current_index_internal.point_index == intersection_index_compare_ref.point_index ) && ( current_index_internal.intersection_index == intersection_index_compare_ref.intersection_index ) ){ + return true; + } + else{ + return false; + } + + } + + + void IterativePolygonInTermsOfIntersections::MarkRelevantItems( LegacyDoseVoxelIndex3D aDoseIndex ){ + + WhichIntersection the_intersection_index; // the_intersection_index initialized to zero by constructor + + for( the_intersection_index.point_index = 0 ; the_intersection_index.point_index < ( Polygon_Intersections.size() ) ; the_intersection_index.point_index++ ){ + Polygon_Intersections.at( the_intersection_index.point_index ).to_be_Regarded = false; + } + + for( the_intersection_index.point_index = 0 ; the_intersection_index.point_index < ( Polygon_Intersections.size() - 1 ) ; the_intersection_index.point_index++ ){ + + LegacyWorldCoordinate3D wc3da = Polygon_Intersections.at( the_intersection_index.point_index ).contour_point_voxel_coord; + LegacyWorldCoordinate3D wc3db = Polygon_Intersections.at( the_intersection_index.point_index + 1 ).contour_point_voxel_coord; + + if( VoxelInBetween( aDoseIndex , wc3da , wc3db ) ){ + Polygon_Intersections.at( the_intersection_index.point_index ).to_be_Regarded = true; + Polygon_Intersections.at( the_intersection_index.point_index + 1 ).to_be_Regarded = true; + } + + } + + if( Polygon_Intersections.size() > 0 ){ + + LegacyWorldCoordinate3D wc3da = Polygon_Intersections.at( ( Polygon_Intersections.size() - 1 ) ).contour_point_voxel_coord; + LegacyWorldCoordinate3D wc3db = Polygon_Intersections.at( 0 ).contour_point_voxel_coord; + + if( VoxelInBetween( aDoseIndex , wc3da , wc3db ) ){ + Polygon_Intersections.at( ( Polygon_Intersections.size() - 1 ) ).to_be_Regarded = true; + Polygon_Intersections.at( 0 ).to_be_Regarded = true; + } + + } + + } + + + + + bool IterativePolygonInTermsOfIntersections::VoxelInBetween( LegacyDoseVoxelIndex3D aDoseIndex , LegacyWorldCoordinate3D wc3da, LegacyWorldCoordinate3D wc3db ){ + + LegacyWorldCoordinate3D wc3dc_min; + LegacyWorldCoordinate3D wc3dc_max; + + if( wc3da.x <= wc3db.x ){ + wc3dc_min.x =wc3da.x; + wc3dc_max.x =wc3db.x; + } + else{ + wc3dc_min.x =wc3db.x; + wc3dc_max.x =wc3da.x; + } + + if( static_cast(wc3dc_min.x) > static_cast( aDoseIndex.x + 1.1 ) ) return false; + if( static_cast(wc3dc_max.x) < static_cast( aDoseIndex.x - 0.1 ) ) return false; + + if( wc3da.y <= wc3db.y ){ + wc3dc_min.y = wc3da.y; + wc3dc_max.y = wc3db.y; + } + else{ + wc3dc_min.y = wc3db.y; + wc3dc_max.y = wc3da.y; + } + + if( static_cast(wc3dc_min.y) > static_cast( aDoseIndex.y + 1.1 ) ) return false; + if( static_cast(wc3dc_max.y) < static_cast( aDoseIndex.y - 0.1 ) ) return false; + + if( wc3da.z <= wc3db.z ){ + wc3dc_min.z = wc3da.z; + wc3dc_max.z = wc3db.z; + } + else{ + wc3dc_min.z = wc3db.z; + wc3dc_max.z = wc3da.z; + } + + + if( static_cast(wc3dc_min.z) > static_cast( aDoseIndex.z + 1.1 ) ) return false; // it can not be in between + if( static_cast(wc3dc_max.z) < static_cast( aDoseIndex.z - 0.1 ) ) return false; + + return true; // can't be sure + + } + + +}//namespace + }//namespace +}//namespace + + + diff --git a/code/masks/rttbIterativePolygonInTermsOfIntersections_LEGACY.h b/code/masks/rttbIterativePolygonInTermsOfIntersections_LEGACY.h new file mode 100644 index 0000000..3cc2c35 --- /dev/null +++ b/code/masks/rttbIterativePolygonInTermsOfIntersections_LEGACY.h @@ -0,0 +1,151 @@ +// ----------------------------------------------------------------------- +// 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 __IterativePolygonInTermsOfIntersections_H +#define __IterativePolygonInTermsOfIntersections_H + +#include "rttbBaseType.h" +#include "rttbStructure.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbField_LEGACY.h" +#include "rttbMaskType_LEGACY.h" // sollen Typen in Core neu definiert werden? +#include "rttbContour_LEGACY.h" +#include +#include + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + + + + // Die folgende Klasse handhabt die konkrete Speicherung der Schnittpunkte. + // Kennt sich also mit der Speicherung der Schnittpunkte der Konturen mit den Voxelkanten aus. + // Information auf Struktur-Polygonebene. + // Nimmt PolygonInfo den Zugriff auf diese Information ab. + + + + + + + class IterativePolygonInTermsOfIntersections{ + + + public: + + IterativePolygonInTermsOfIntersections(); + ~IterativePolygonInTermsOfIntersections(){}; + + void MarkRelevantItems( LegacyDoseVoxelIndex3D aDoseIndex ); + bool ItIsAsExpected( masks::legacy::PolygonInTermsOfIntersections Expected_Unified_Polygon_Intersections ); + bool CheckToBeRegarded(); + void ShowSelf(); + + class WhichIntersection{ + + public: + + WhichIntersection(){ + point_index = 0; + intersection_index = 0; + column_raw_or_unified = 0; + } + + ~WhichIntersection(){}; + + unsigned int point_index; + unsigned int intersection_index; + unsigned int column_raw_or_unified; + // 0 bedeutet Spalte, 1 bedeutet Zeile und 2 bedeutet vereinheitlichte Liste der Zeilen uns Spaltenschnittpunkte + + }; + + void ResetIntersectionIndex(); + bool ThisIntersection( LegacyWorldCoordinate2D& the_intersection ); + void RememberPosition(); + bool IncremtentIntersection(); + bool IncremtentIntersectionPeriodically(); + bool NextIntersectionPeriodically( LegacyWorldCoordinate2D& the_next_intersection ); + bool NextPointOrIntersectionPeriodically( LegacyWorldCoordinate2D& previous_point_or_intersection , LegacyWorldCoordinate2D& next_point_or_intersection , LegacyDoseVoxelIndex3D& the_dose_index ); + int MaximumNumberOfElements(); + bool ThereAreIntersections(); + void JumpToRememberedPosition(); + bool RunForwardToNextIntersection( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref , LegacyDoseVoxelIndex3D the_dose_index ); + bool RunBackwardToNextIntersection( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ); + void CleanOut( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ); + bool NextIntersection( LegacyWorldCoordinate2D& the_next_intersection ); + bool PreviousPointOrIntersectionPeriodically( LegacyWorldCoordinate2D& previous_point_or_intersection , LegacyWorldCoordinate2D& point_or_intersection , LegacyDoseVoxelIndex3D& the_dose_index ); + bool PreviousIntersectionPeriodically( LegacyWorldCoordinate2D& the_intersection ); + void ResetIntersectionIndex( WhichIntersection& intersection_set_to ); + void SetIntersectionsAndResetIntersectionIndex( const masks::legacy::PolygonInTermsOfIntersections& Polygon_Intersections_In ); + bool CheckIsIntersection( LegacyWorldCoordinate2D& edge_coord , WhichIntersection& the_intersection_index_ref ); + LegacyWorldCoordinate3D GetRestpectivePologonPointInVoxelCoordinates( WhichIntersection& the_intersection_index_ref ); + LegacyWorldCoordinate3D GetRestpectivePologonPointInVoxelCoordinatesFurther( WhichIntersection& the_intersection_index_ref , int ahead ); + LegacyWorldCoordinate3D GetRestpectivePologonPointInVoxelCoordinatesPrevious( WhichIntersection& the_intersection_index_ref , unsigned int back ); + bool selfTest(); + bool CheckCurrentIndexInitForTest(); + void SetCurrentIntersectionIndex( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_new_ref ); + bool CheckCurrentIntersectionIndexIdentical( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_compare_ref ); + void PrintIntersectionIndex(); + + private: + + unsigned int GetRestpectiveIntersectionVectorLength( WhichIntersection& the_intersection_index_ref ); + LegacyWorldCoordinate2D GetRestpectiveIntersection( WhichIntersection& the_intersection_index_ref ); + LegacyWorldCoordinate2D GetRestpectiveIntersectionFurther( WhichIntersection& the_intersection_index_ref , int ahead ); + bool PointIndexIsFine( WhichIntersection& the_intersection_index_ref ); + LegacyWorldCoordinate3D GetFirstPointThisPolygon( WhichIntersection& the_intersection_index_ref ); + bool AppendIntermediatePointOrCloseForward( LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ); + bool AppendIntermediatePointOrCloseBackward(LegacyWorldCoordinate2D& point_of_interest_start_ref , LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ); + void AppendThisIntermediatePoint( std::vector& snippet_intermediate_content_ref ); + bool IncrementNeeded(); + bool DecrementNeeded(); + bool OneDiffers( std::vector& snippet_intermediate_content_ref , LegacyWorldCoordinate2D& point_of_interest_start_ref ); + bool IdenticalUniList( masks::legacy::PointLevelVector plv_a , masks::legacy::PointLevelVector plv_b ); + bool CloseWithoutIncrement( LegacyWorldCoordinate2D& point_of_interest_start_ref, LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ); + bool CloseWithoutDecrement( LegacyWorldCoordinate2D& point_of_interest_start_ref, LegacyWorldCoordinate2D& point_of_interest_end_ref , std::vector& snippet_intermediate_content_ref ); + void PeriodicPolygonPointIncrement(); + void PeriodicPolygonPointDecrement(); + void CopyIntersectionIndex( WhichIntersection& to_ref , WhichIntersection& from_ref ); + bool CheckIndex(); + bool DecrementPointOrIntersectionPeriodically(); + bool DecrementIntersectionPeriodically(); + void CreateUnifiedListOfRawAndColumnIntersections(); + void removeThisCoordinateFromBothLists( LegacyWorldCoordinate2D the_coordinate , std::vector& raw_intersections_work_ref , std::vector& column_intersections_work_ref ); + bool VoxelInBetween( LegacyDoseVoxelIndex3D aDoseIndex , LegacyWorldCoordinate3D wc3da, LegacyWorldCoordinate3D wc3db ); + bool IsInside( LegacyWorldCoordinate3D& point_check , LegacyDoseVoxelIndex3D& the_dose_index ); + + PolygonInTermsOfIntersections Polygon_Intersections; + WhichIntersection current_index_internal; + WhichIntersection current_index_internal_remember; + int number_of_intersections; + + }; + } + } +} + + + +#endif \ No newline at end of file diff --git a/code/masks/rttbMask.cpp b/code/masks/rttbMask.cpp new file mode 100644 index 0000000..bdeb4e2 --- /dev/null +++ b/code/masks/rttbMask.cpp @@ -0,0 +1,3721 @@ +// ----------------------------------------------------------------------- +// 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 "rttbMask.h" + +#include "rttbNullPointerException.h" +#include "rttbSelfIntersectingStructureException.h" + +#include +#include +#include +#include +#include + +using namespace std; + + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + + Mask::Mask( DoseIteratorInterface* aDoseIter , StructureLegacy* aStructure ) { + + MaskField.resize(1); + for(int i = 0 ; i < MaskField.size() ; i++ )MaskField.at(i) = NULL; + + FieldOfIntersections = NULL; + do_detailed_subvox = false; + + this->_doseIter=aDoseIter; + this->_structure=aStructure; + //this->_withSubVoxelFraction=withSubVoxelFraction; + + if(!_doseIter) + throw core::NullPointerException("aDoseIter must not be NULL!"); + if(!_structure) + throw core::NullPointerException("aStructure must not be NULL!"); + + this->_doseIter->start(); + this->GInf.push_back( _doseIter->getGeometricInfo() ); + + //std::cout << "init begin"<~FieldOfScalar(); + //MaskField.resize(0); + + sort(_doseVoxelInStructure.begin(),_doseVoxelInStructure.end()); + this->calcDoseData(); + + this->clearMaskField(); + + } + + Mask::Mask( core::GeometricInfo* aDoseGeoInfo , StructureLegacy* aStructure ) { + + if(!aDoseGeoInfo){ + throw core::NullPointerException("aDoseGeoInfo must not be NULL!"); + } + + MaskField.resize(1); + for(int i = 0 ; i < MaskField.size() ; i++ ) + { + MaskField.at(i) = NULL; + } + + FieldOfIntersections = NULL; + do_detailed_subvox = false; + + if(!aStructure){ + throw core::NullPointerException("aStructure must not be NULL!"); + } + this->_structure=aStructure; + //this->_withSubVoxelFraction=withSubVoxelFraction; + + this->GInf.push_back( *aDoseGeoInfo ); + + //std::cout << "init begin"<~FieldOfScalar(); + //MaskField.resize(0); + + sort(_doseVoxelInStructure.begin(),_doseVoxelInStructure.end()); + + this->clearMaskField(); + + } +/* + Mask::Mask(rttb::core::DoseIteratorInterface *aDoseIter, const std::vector& aDoseVoxelVector){ + _structure=NULL; + FieldOfIntersections = NULL; + do_detailed_subvox = false; + + MaskField.resize(1); + for(int i = 0 ; i < MaskField.size() ; i++ )MaskField.at(i) = NULL; + + _doseIter=aDoseIter; + if(!_doseIter) + throw core::NullPointerException("aDoseIter must not be NULL!"); + this->_doseIter->start(); + this->GInf.push_back( _doseIter->getGeometricInfo() ); + //Get valid DoseVoxel + for(int i=0;icalcDoseData(); + + this->clearMaskField(); + + } + +*/ + + Mask::~Mask(){ + + /*if( FieldOfIntersections != NULL )FieldOfIntersections->~FieldOfPointer(); + + for( int i = 0 ; i < MaskField.size() ; i++ ){ + if(MaskField.at(i)!=NULL)MaskField.at(i)->~FieldOfScalar(); + } + MaskField.resize(1); + for(int i = 0 ; i < MaskField.size() ; i++ )MaskField.at(i) = NULL;*/ + + } + + void Mask::clearMaskField(){ + if( FieldOfIntersections != NULL )FieldOfIntersections->~FieldOfPointer(); + + for( int i = 0 ; i < MaskField.size() ; i++ ){ + if(MaskField.at(i)!=NULL)MaskField.at(i)->~FieldOfScalar(); + } + MaskField.resize(1); + for(int i = 0 ; i < MaskField.size() ; i++ )MaskField.at(i) = NULL; + } + + void Mask::resetDose(DoseIteratorInterface* aDoseIterator){ + _doseIter=aDoseIterator; + if(!_doseIter) + throw core::NullPointerException("aDoseIter must not be NULL!"); + this->_doseIter->start(); + + //if geometric info not changed or if the voxelization external + if(_doseIter->getGeometricInfo()==this->GInf.at(0) || this->_structure==NULL){ + //std::cout << "reset dose."<calcDoseData(); + } + else{ + //std::cout << "recalculate mask"<GInf.push_back( _doseIter->getGeometricInfo() ); + + Init(); + + Calc(); + sort(_doseVoxelInStructure.begin(),_doseVoxelInStructure.end()); + this->calcDoseData(); + + this->clearMaskField(); + + } + } + + + /*const std::vector& Mask::getDoseVoxelIndexInStructure() const{ + return doseVoxelIndexInStructure; + }*/ + + /*! @brief Get the dose voxels which lie inside the structure + * @return Return the vector of DoseVoxel which are inside the structure (with voxel proportion information) + */ + const std::vector& Mask::getDoseVoxelInStructure() const{ + return this->_doseVoxelInStructure; + } + + /*! @brief Set the dose voxels which lie inside the structure + * @return Set the vector of DoseVoxel which are inside the structure (with voxel proportion information) + */ + void Mask::setDoseVoxelInStructure(std::vector aDoseVoxelVector){ + _doseVoxelInStructure=aDoseVoxelVector; + + } + + const DoseIteratorInterface& Mask::getDoseIterator() const{ + return *_doseIter; + } + + const StructureLegacy& Mask::getRTStructure() const{ + return *_structure; + } + + /*const std::vector Mask::getDoseData() const{ + return _doseData; + }*/ + + + + //get absolute dose data of the voxels which are inside the structure; + void Mask::calcDoseData(){ + _doseData.clear(); + /*this->_doseIter->start(); + std::vector doseDis;//dose distribution + while(_doseIter->hasNextVoxel()){ + doseDis.push_back(_doseIter->next()); + }*/ + + _doseIter->start(); + int count=0; + + std::vector::iterator it=_doseVoxelInStructure.begin(); + while(_doseIter->hasNextVoxel() && it!=_doseVoxelInStructure.end()){ + // Anmerkung von Martina fuer Lanlan: Eigentlich ist der Witz beim Iterator gerade, dass man sich ueber die + // Innereien des Iterators als Programmierer, der ihn einsetzt keine Gedanken machen muss. Hier ist die fehlerfreie Funktion + // des Codes jedoch von einigen Eigenschaften des Iterators, insbesondere von der Richtung in der iteriert wird, + // abhaengig. Das kommt daher, dass der Programmierer den Iterator von innen kannte und Eigenschaften nutzt, die er eigentlich + // nicht kennem wuerde, als Anwendungsprogrammierer des Iterators. Weil der Programmierer den Iterator aber selbst geschrieben hatte, + // deshalb wuste er es hier ... + // Das kann zur Folgen haben, dass nach einer Aenderung in Code des Iterators Mask nicht mehr korrekt + // funktioniert (z.B. wenn die Richtung der iteration im Koordinatensystem sich umkehren wuerde) man kann darueber + // nachdenken ob das problematisch ist .... + // Es ist ja auch so, dass die RTToolbox dafuer gemacht ist erweitert zu werden. Es koennen andere Iteratoren dazu kommen. + // Wollen wir hier spezifischer werden was den Typ des erlaubten Iterators betrifft? Das wuerde helfen. + double doseValue=_doseIter->next(); + LegacyDoseVoxelIndex3D doseVoxel=(*it).getVoxelIndex3D(); + if(count==doseVoxel.z*GInf.at(0).getNumColumns()*GInf.at(0).getNumRows()+doseVoxel.y*GInf.at(0).getNumColumns()+doseVoxel.x){ + _doseData.push_back(doseValue); + it++; + } + count++; + } + + } + + + void Mask::Calc(){ + //std::cout << "calc begin"<= 0 ; resolution_level-- ){ + + //std::cout << "GetIntersections"<= 0 ; resolution_level-- ){ + + for( int index_z = 0 ; index_z < GInf.at(resolution_level).getNumSlices() ; index_z++ ){ + + LegacyUnsignedIndex3D index_internal; + index_internal.x = 0; + index_internal.y = 0; + index_internal.z = index_z; + + for( int resolution_level = ( GInf.size() - 1 ) ; resolution_level >= 0 ; resolution_level-- ){ + + if( ( MarkInterestingArea.at(resolution_level).at( index_z ).index_end.x != 0 ) && ( MarkInterestingArea.at(resolution_level).at( index_z ).index_end.y != 0 ) ){ + + for( index_internal.x = MarkInterestingArea.at(resolution_level).at( index_z ).index_begin.x ; index_internal.x <= MarkInterestingArea.at(resolution_level).at( index_z ).index_end.x ; index_internal.x++ ){ + for( index_internal.y = MarkInterestingArea.at(resolution_level).at( index_z ).index_begin.y ; index_internal.y <= MarkInterestingArea.at(resolution_level).at( index_z ).index_end.y ; index_internal.y++ ){ + + + if( MaskField.at( resolution_level )->GetData( index_internal.x, index_internal.y, index_internal.z ) == brightness_inside ){ + + CheckUppwardBlockSameIntensity( resolution_level , index_internal, brightness_inside ); + + } + + if( MaskField.at( resolution_level )->GetData( index_internal.x, index_internal.y, index_internal.z ) == brightness_outside ){ + + CheckUppwardBlockSameIntensity( resolution_level , index_internal, brightness_outside ); + + } + + if( MaskField.at( resolution_level )->GetData( index_internal.x, index_internal.y, index_internal.z ) == brightness_border ){ + + CheckUppwardOneOutOfThisBlockHasSameIntensity( resolution_level , index_internal , brightness_border ); + + } + + if( MaskField.at( resolution_level )->GetData( index_internal.x, index_internal.y, index_internal.z ) == brightness_unknown )assert(0); // This should not happen since each voxel must be regarded. + + + + + + } // for( index_internal.y + + } // for( index_internal.x + + + } // if( ( MarkInterestingArea.at(resolution_level).at( index_z + + } // for( int resolution_level + + } + + } // resolution_level + + } + + + void Mask::CheckUppwardBlockSameIntensity( int resolution_level_in , LegacyUnsignedIndex3D index_in , int brightness ){ + + if( resolution_level_in > 0 ){ + + for( int resolution_level = ( resolution_level_in - 1 ) ; resolution_level >= 0 ; resolution_level-- ){ + + int resolution_difference = ( resolution_level_in - resolution_level ); + + double block_length = GetBlockLengthThisResolutionLevel( resolution_difference ); + + LegacyUnsignedIndex3D index_internal; + index_internal.x = index_in.x * block_length; + index_internal.y = index_in.y * block_length; + index_internal.z = index_in.z; + + if( ( index_internal.x >= MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_begin.x ) && ( index_internal.x <= ( MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_end.x - block_length ) ) && ( index_internal.y >= MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_begin.y ) && ( index_internal.y <= ( MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_end.y - block_length ) ) ){ + if( ( index_in.x >= MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_begin.x ) && ( index_in.x <= ( MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_end.x - block_length ) ) && ( index_in.y >= MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_begin.y ) && ( index_in.y <= ( MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_end.y - block_length ) ) ){ + + bool its_fine = MaskField.at( resolution_level )->CheckBlockSameIntensity( index_internal.x , index_internal.y, index_internal.z , block_length , brightness ); + + + assert( its_fine ); + + } + } + + } + + } + + } + + + void Mask::SetUppwardBlockThisIntensity( int resolution_level_in , LegacyUnsignedIndex3D index_in , int brightness ){ + + if( resolution_level_in > 0 ){ + + for( int resolution_level = ( resolution_level_in - 1 ) ; resolution_level >= 0 ; resolution_level-- ){ + + int resolution_difference = ( resolution_level_in - resolution_level ); + + double block_length = GetBlockLengthThisResolutionLevel( resolution_difference ); + + LegacyUnsignedIndex3D index_internal; + index_internal.x = index_in.x * block_length; + index_internal.y = index_in.y * block_length; + index_internal.z = index_in.z; + + + + if( ( index_internal.x >= MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_begin.x ) && ( index_internal.x <= ( MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_end.x - block_length ) ) && ( index_internal.y >= MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_begin.y ) && ( index_internal.y <= ( MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_end.y - block_length ) ) ){ + if( ( index_in.x >= MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_begin.x ) && ( index_in.x <= ( MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_end.x - block_length ) ) && ( index_in.y >= MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_begin.y ) && ( index_in.y <= ( MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_end.y - block_length ) ) ){ + + MaskField.at( resolution_level )->SetBlockThisIntensity( index_internal.x , index_internal.y, index_internal.z , block_length , brightness ); + + } + } + + } + + } + + } + + + void Mask::CheckUppwardOneOutOfThisBlockHasSameIntensity( int resolution_level_in , LegacyUnsignedIndex3D index_in , int brightness ){ + + if( ( resolution_level_in > 0 ) && ( resolution_level_in < (MaskField.size()-1) ) ){ + + for( int resolution_level = ( resolution_level_in - 1 ) ; resolution_level >= 0 ; resolution_level-- ){ + + int resolution_difference = ( resolution_level_in - resolution_level ); + + double block_length = GetBlockLengthThisResolutionLevel( resolution_difference ); + + LegacyUnsignedIndex3D index_internal; + index_internal.x = index_in.x * block_length; + index_internal.y = index_in.y * block_length; + index_internal.z = index_in.z; + + if( ( index_internal.x >= MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_begin.x ) && ( index_internal.x <= ( MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_end.x - block_length ) ) && ( index_internal.y >= MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_begin.y ) && ( index_internal.y <= ( MarkInterestingArea.at(resolution_level).at( index_internal.z ).index_end.y - block_length ) ) ){ + if( ( index_in.x >= MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_begin.x ) && ( index_in.x <= ( MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_end.x - block_length ) ) && ( index_in.y >= MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_begin.y ) && ( index_in.y <= ( MarkInterestingArea.at(resolution_level_in).at( index_in.z ).index_end.y - block_length ) ) ){ + + bool its_fine = MaskField.at( resolution_level )->CheckOneOutOfThisBlockHasSameIntensity( index_internal.x , index_internal.y, index_internal.z , block_length , brightness ); + + assert( its_fine ); + + } + + } + + } + + } + + } + + + bool Mask::ContourFine(){ + + //std::cout << "ContourFine begin"<getLegacyStructureVector(); + LegacyPolygonSequenceType& strVector_ref = strVector; + + correspoinding_structure_vector str_v; + std::vector wich_slice; + + for(unsigned int struct_index = 0 ; struct_index < Intersections.at(0).size() ; struct_index++ ){ + + wich_slice.clear(); + + /* if( strVector.at( struct_index ).size() < 2 ){ + std::cerr<< " The Polygon consists of just one point ! That doesn't make sense in Radiotherapy. Program will be terminated. " < ( i+1 ) ) secondPoint = pitoiA.at( i+1 ).contour_point_voxel_coord; + else secondPoint = pitoiA.at( 0 ).contour_point_voxel_coord; + + for( ; itB!=pitoiB.end() ;itB++ ){ + //for( ; j ( j+1 ) ) fourthPoint = pitoiB.at( j+1 ).contour_point_voxel_coord; + else fourthPoint = pitoiB.at( 0 ).contour_point_voxel_coord; + + //std::cout < firstPoint.x ){ + x_min = firstPoint.x; + x_max = secondPoint.x; + } + + bool it_is_in_between = 1; + if( (x < x_min) || (x > x_max) ) it_is_in_between = 0; + + + if( (beta>0)&&(beta<1) && it_is_in_between ){ + return true; + } + else return false; + + } + else return false; + + } + + if( ( secondPoint.x - firstPoint.x ) == 0 ){ + + if( ( fourthPoint.x - thirdPoint.x ) != 0 ){ + + float beta = ( firstPoint.x - thirdPoint.x ) / ( fourthPoint.x - thirdPoint.x ); + + float y = ( fourthPoint.y - thirdPoint.y ) * beta + thirdPoint.y; + + float y_min = secondPoint.y; + float y_max = firstPoint.y; + + if( secondPoint.y > firstPoint.y ){ + y_min = firstPoint.y; + y_max = secondPoint.y; + } + + bool it_is_in_between = 1; + if( (y < y_min) || (y > y_max) ) it_is_in_between = 0; + + if( (beta>0)&&(beta<1) && it_is_in_between ){ + + return true; + } + else return false; + } + else return false; + + } + + + if( ( ( secondPoint.y - firstPoint.y ) == 0 ) || ( ( secondPoint.x - firstPoint.x ) == 0 ) ) assert(0); + + + if( ( ( ( fourthPoint.x - thirdPoint.x ) / ( secondPoint.x - firstPoint.x ) ) - ( ( fourthPoint.y - thirdPoint.y ) / ( secondPoint.y - firstPoint.y ) ) ) == 0 ){ + return false; // parallel, schneiden sich nicht ! + } + + + if( ( ( ( fourthPoint.x - thirdPoint.x ) / ( secondPoint.x - firstPoint.x ) ) - ( ( fourthPoint.y - thirdPoint.y ) / ( secondPoint.y - firstPoint.y ) ) ) == 0 ) assert( 0 ); + + + float beta = ( ( ( thirdPoint.y - firstPoint.y ) / ( secondPoint.y - firstPoint.y ) ) - ( ( thirdPoint.x - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) ) ) / ( ( ( fourthPoint.x - thirdPoint.x ) / ( secondPoint.x - firstPoint.x ) ) - ( ( fourthPoint.y - thirdPoint.y ) / ( secondPoint.y - firstPoint.y ) ) ) ; + float alpha = ( ( fourthPoint.x - thirdPoint.x ) * beta + thirdPoint.x - firstPoint.x ) / ( secondPoint.x - firstPoint.x ); + + if( ( (beta>0)&&(beta<1) ) && ( (alpha>0) && (alpha<1) ) ){ // falls diese Bedingung nicht erfuellt ist, gibt es zwar einen Schnittpunkt der Geraden, aber nicht im relevanten Bereich + + return true; + + } + + return false; + + } + + + + + + void Mask::FloodFill4( UnsignedIndexList indexList,int oldcolor,int newcolor,int resolution_level) + { + + + LegacyUnsignedIndex3D index; + index.x = 0; + index.y = 0; + index.z = 0; + + while(!indexList.empty()){ + LegacyUnsignedIndex3D frontIndex=indexList.front(); + index.z = frontIndex.z; + if(MaskField.at( resolution_level )->GetData( frontIndex.x, frontIndex.y, frontIndex.z )==oldcolor){ + //insert index + if(frontIndex.y+1<=MarkInterestingArea.at(resolution_level).at( frontIndex.z ).index_end.y && MaskField.at( resolution_level )->GetData( frontIndex.x, frontIndex.y+1, frontIndex.z )==oldcolor){ + index.x=frontIndex.x; + index.y=frontIndex.y+1; + indexList.push_back(index); + } + if(frontIndex.y-1>=MarkInterestingArea.at(resolution_level).at( frontIndex.z ).index_begin.y && MaskField.at( resolution_level )->GetData( frontIndex.x, frontIndex.y-1, frontIndex.z )==oldcolor ){ + index.x=frontIndex.x; + index.y=frontIndex.y-1; + indexList.push_back(index); + } + if(frontIndex.x-1>=MarkInterestingArea.at(resolution_level).at( frontIndex.z ).index_begin.x && MaskField.at( resolution_level )->GetData( frontIndex.x-1, frontIndex.y, frontIndex.z )==oldcolor){ + index.x=frontIndex.x-1; + index.y=frontIndex.y; + indexList.push_back(index); + } + if(frontIndex.x+1<=MarkInterestingArea.at(resolution_level).at( frontIndex.z ).index_end.x && MaskField.at( resolution_level )->GetData( frontIndex.x+1, frontIndex.y, frontIndex.z )==oldcolor){ + index.x=frontIndex.x+1; + index.y=frontIndex.y; + indexList.push_back(index); + } + + //mark newcolor + MaskField.at( resolution_level )->PutData( frontIndex.x, frontIndex.y, frontIndex.z, newcolor ); + SetUppwardBlockThisIntensity( resolution_level , frontIndex , newcolor ); + //delete front + indexList.pop_front(); + + } + else{ + //delete front + indexList.pop_front(); + } + } + + } + + + + void Mask::setInsideVoxelPreviousVersion(int resolution_level){ + + /* //doseVoxelIndexInStructure.clear(); + _doseVoxelInStructure.clear(); + + LegacyUnsignedIndex3D index; + index.x = 0; + index.y = 0; + index.z = 0; + + bool first=true; + + for( index.z = 0 ; index.z < GInf.at(resolution_level).getNumSlices() ; index.z++ ){ + + LegacyUnsignedIndex3D index_internal; + index_internal.x = 0; + index_internal.y = 0; + index_internal.z = index.z; + + /*First: Mark all voxels as unknown*/ +/* if( ( MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x != 0 ) && ( MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y != 0 ) ){ + + for( index_internal.x = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.x ; index_internal.x <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x ; index_internal.x++ ){ + for( index_internal.y = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.y ; index_internal.y <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y ; index_internal.y++ ){ + + //set all no border voxels as outside + if( MaskField.at( resolution_level )->GetData( index_internal.x, index_internal.y, index_internal.z ) != brightness_border ){ + MaskField.at( resolution_level )->PutData( index_internal.x, index_internal.y, index_internal.z, brightness_unknown ); + //MaskField.at( resolution_level )->PutData( index_internal.x, index_internal.y, index_internal.z, brightness_inside ); + + }//end if + else{ + LegacyDoseVoxelIndex3D index2; + index2.x = index_internal.x; + + index2.y = index_internal.y; + + index2.z = index_internal.z; + + DoseVoxel doseVoxel=DoseVoxel(index2,this->GInf.at(resolution_level)); + //std::cout <GInf.at(resolution_level)); + if(doseVoxel.voxelInStructure(*_structure)==0){ + //std::cout << "first outside voxel: "<FloodFill4(index2.x,index2.y,index2.z,brightness_unknown, brightness_outside,resolution_level); + + + //get corresponding polygons + DoseVoxel doseVoxelZPlane=DoseVoxel(index2,this->GInf.at(resolution_level)); + std::vector sliceV=doseVoxelZPlane.getZIntersectPolygonIndexVector(*_structure); + + for(int i=0;igetLegacyStructureVector().at(sliceV.at(i)); + + Contour contour=Contour(polygon);//get the contour in this slice + + LegacyPolygonType box=contour.getMinimalBox(); + Contour boxContour=Contour(box); + + bool firstVoxelInStr=false; + + + /*Mark all voxels inside a polygon of the structure: using FloodFill4*/ +/* for( index2.x = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.x ; index2.x <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x && !firstVoxelInStr ; index2.x++ ){ + + for( index2.y = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.y ; index2.y <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y && !firstVoxelInStr ; index2.y++ ){ + if(MaskField.at( resolution_level )->GetData( index2.x, index2.y, index2.z )==brightness_unknown) { + + DoseVoxel doseVoxel=DoseVoxel(index2,this->GInf.at(resolution_level)); + + //if doseVoxel inside the box + + LegacyWorldCoordinate2D luP={doseVoxel.getLeftUpperPoint().x,doseVoxel.getLeftUpperPoint().y}; + + //if left upper point of this voxel inside the box + + if(luP.x>=box.at(0).x && luP.y>=box.at(0).y && luP.x<=box.at(2).x && luP.y<=box.at(2).y){ + + if(doseVoxel.voxelInStructure(*_structure)==1){ + + //std::cout << "first inside voxel: "<FloodFill4(index2.x,index2.y,index2.z,brightness_unknown,brightness_inside,resolution_level); + + }//end for i + + //check unknown voxels + int sizeUnknown=0; + for( index2.x = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.x ; index2.x <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x ; index2.x++ ){ + for( index2.y = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.y ; index2.y <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y ; index2.y++ ){ + if(MaskField.at( resolution_level )->GetData(index2.x,index2.y,index2.z)==brightness_unknown){ + //std::cout << index2.toString()<<"---"; + //sizeUnknown++; + DoseVoxel doseVoxel=DoseVoxel(index2,this->GInf.at(resolution_level)); + double voxelInStr=doseVoxel.voxelInStructure(*_structure); + if(voxelInStr>0){ + this->FloodFill4(index2.x,index2.y,index2.z,brightness_unknown,brightness_inside,resolution_level); + } + else{ + this->FloodFill4(index2.x,index2.y,index2.z,brightness_unknown, brightness_outside,resolution_level); + } + } + } + } + /*if(sizeUnknown!=0) + std::cout << "Unknown: "< indexListInside, std::vector< UnsignedIndexList > indexListOutside ){ + + + _doseVoxelInStructure.clear(); + + LegacyUnsignedIndex3D index; + index.x = 0; + index.y = 0; + index.z = 0; + + bool first=true; + + for( index.z = 0 ; index.z < GInf.at(resolution_level).getNumSlices() ; index.z++ ){ + + if( indexListOutside.at(index.z).size() == 0 ){ + + LegacyDoseVoxelIndex3D index2; + + index2.x = 0; + index2.y = 0; + index2.z = index.z; + /*Mark all voxels outside the structure: using FloodFill4*/ + bool firstVoxelOutStr=false; + for( index2.x = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.x ; index2.x <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x && !firstVoxelOutStr ; index2.x++ ){ + for( index2.y = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.y ; index2.y <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y && !firstVoxelOutStr ; index2.y++ ){ + + DoseVoxel doseVoxel=DoseVoxel(index2,&(this->GInf.at(resolution_level))); + if(doseVoxel.voxelInStructure(*_structure)==0){ + + LegacyUnsignedIndex3D indexUint; + indexUint.x = static_cast(index2.x); + indexUint.y = static_cast(index2.y); + indexUint.z = static_cast(index2.z); + + indexListOutside.at(index.z).push_back( indexUint ); + firstVoxelOutStr=true; + break; + + }//end if + + }//end for index2.y + if(firstVoxelOutStr) + break; + }//end for index2.x + + } + + if( indexListOutside.at(index.z).size() > 0 ) + this->FloodFill4(indexListOutside.at(index.z),brightness_unknown, brightness_outside,resolution_level); + + if( indexListInside.at(index.z).size() == 0 ){ + + LegacyDoseVoxelIndex3D index2; + + index2.x = 0; + index2.y = 0; + index2.z = index.z; + + //get corresponding polygons + DoseVoxel doseVoxelZPlane=DoseVoxel(index2,&(this->GInf.at(resolution_level))); + std::vector sliceV=doseVoxelZPlane.getZIntersectPolygonIndexVector(*_structure); + + for(int i=0;igetLegacyStructureVector().at(sliceV.at(i)); + + Contour contour=Contour(polygon);//get the contour in this slice + + LegacyPolygonType box=contour.getMinimalBox(); + Contour boxContour=Contour(box); + + bool firstVoxelInStr=false; + + + /*Mark all voxels inside a polygon of the structure: using FloodFill4*/ + for( index2.x = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.x ; index2.x <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x && !firstVoxelInStr ; index2.x++ ){ + + for( index2.y = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.y ; index2.y <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y && !firstVoxelInStr ; index2.y++ ){ + if(MaskField.at( resolution_level )->GetData( index2.x, index2.y, index2.z )==brightness_unknown) { + + DoseVoxel doseVoxel=DoseVoxel(index2,&(this->GInf.at(resolution_level))); + + //if doseVoxel inside the box + + LegacyWorldCoordinate2D luP={doseVoxel.getLeftUpperPoint().x(),doseVoxel.getLeftUpperPoint().y()}; + + //if left upper point of this voxel inside the box + + if(luP.x>=box.at(0).x && luP.y>=box.at(0).y && luP.x<=box.at(2).x && luP.y<=box.at(2).y){ + + if(doseVoxel.voxelInStructure(*_structure)==1){ + + //std::cout << "first inside voxel: "<(index2.x); + indexUint.y = static_cast(index2.y); + indexUint.z = static_cast(index2.z); + indexListInside.at(index.z).push_back(indexUint); + } + + }//end for i + + } + + if(indexListInside.at(index.z).size() > 0)this->FloodFill4(indexListInside.at(index.z),brightness_unknown,brightness_inside,resolution_level); + + //check unknown voxels + int sizeUnknown=0; + + LegacyDoseVoxelIndex3D index2; + + index2.x = 0; + index2.y = 0; + index2.z = index.z; + + for( index2.x = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.x ; index2.x <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.x ; index2.x++ ){ + for( index2.y = MarkInterestingArea.at(resolution_level).at( index.z ).index_begin.y ; index2.y <= MarkInterestingArea.at(resolution_level).at( index.z ).index_end.y ; index2.y++ ){ + if(MaskField.at( resolution_level )->GetData(index2.x,index2.y,index2.z)==brightness_unknown){ + //std::cout << index2.toString()<<"---"; + //sizeUnknown++; + DoseVoxel doseVoxel=DoseVoxel(index2,&(this->GInf.at(resolution_level))); + double voxelInStr=doseVoxel.voxelInStructure(*_structure); + if(voxelInStr>0){ + indexListInside.at(index.z).resize(0); + LegacyUnsignedIndex3D indexUint; + indexUint.x = static_cast(index2.x); + indexUint.y = static_cast(index2.y); + indexUint.z = static_cast(index2.z); + indexListInside.at(index.z).push_back(indexUint); + this->FloodFill4(indexListInside.at(index.z),brightness_unknown,brightness_inside,resolution_level); + } + else{ + indexListOutside.at(index.z).resize(0); + LegacyUnsignedIndex3D indexUint; + indexUint.x = static_cast(index2.x); + indexUint.y = static_cast(index2.y); + indexUint.z = static_cast(index2.z); + indexListOutside.at(index.z).push_back(indexUint); + this->FloodFill4(indexListOutside.at(index.z),brightness_unknown, brightness_outside,resolution_level); + } + } + } + } + /*if(sizeUnknown!=0) + std::cout << "Unknown: "<GetDimX() ; x++ ){ + for( int y = 0 ; y < MaskField.at( resolution_level )->GetDimY() ; y++ ){ + for( int z = 0 ; z < MaskField.at( resolution_level )->GetDimZ() ; z++ ){ + + if( MaskField.at( resolution_level )->GetData(x,y,z) == brightness_inside ) counter_inside++; + if( MaskField.at( resolution_level )->GetData(x,y,z) == brightness_outside ) counter_outside++; + if( MaskField.at( resolution_level )->GetData(x,y,z) == brightness_border ) counter_border++; + if( MaskField.at( resolution_level )->GetData(x,y,z) == brightness_unknown ) counter_unknown++; + + } + } + } + + std::cout<< " counter_inside : " << counter_inside < indexListInside; + std::vector< UnsignedIndexList > indexListOutside; + + for( int z = 0 ; z < GInf.at(resolution_level).getNumSlices() ; z++ ){ + UnsignedIndexList a_list; + a_list.resize(0); + indexListInside.push_back(a_list); + indexListOutside.push_back(a_list); + } + + if( resolution_level < ( MaskField.size() - 1 ) )MaskField.at( resolution_level )->GetBorderRegion( MarkInterestingArea.at(resolution_level), indexListInside , indexListOutside, brightness_unknown, brightness_inside, brightness_outside ); + setInsideVoxel( resolution_level , indexListInside, indexListOutside ); + + } + + + + void Mask::SetContentUnknown(){ + + for( int resolution_level = 0 ; resolution_level < GInf.size() ; resolution_level++ ){ + + for( int index_z = 0 ; index_z < GInf.at(resolution_level).getNumSlices() ; index_z++ ){ + + + LegacyUnsignedIndex3D index_internal; + index_internal.x = 0; + index_internal.y = 0; + index_internal.z = index_z; + + + /*First: Mark all voxels as unknown*/ + if( ( MarkInterestingArea.at(resolution_level).at( index_z ).index_end.x != 0 ) && ( MarkInterestingArea.at(resolution_level).at( index_z ).index_end.y != 0 ) ){ + + for( index_internal.x = MarkInterestingArea.at(resolution_level).at( index_z ).index_begin.x ; index_internal.x <= MarkInterestingArea.at(resolution_level).at( index_z ).index_end.x ; index_internal.x++ ){ + for( index_internal.y = MarkInterestingArea.at(resolution_level).at( index_z ).index_begin.y ; index_internal.y <= MarkInterestingArea.at(resolution_level).at( index_z ).index_end.y ; index_internal.y++ ){ + + + //set all no border voxels as outside + if( MaskField.at( resolution_level )->GetData( index_internal.x, index_internal.y, index_internal.z ) != brightness_border ){ + MaskField.at( resolution_level )->PutData( index_internal.x, index_internal.y, index_internal.z, brightness_unknown ); + //MaskField.at( resolution_level )->PutData( index_internal.x, index_internal.y, index_internal.z, brightness_inside ); + + }//end if + + + }//end for index_internal.y + }//end for index_internal.x + //std::cout << std::endl; + + }//end if + + } + + } + + } + + + void Mask::SeperateRegionsQuick(){ + + //doseVoxelIndexInStructure.clear(); + _doseVoxelInStructure.clear(); + + LegacyUnsignedIndex3D index; + index.x = 0; + index.y = 0; + index.z = 0; + + for( index.z = 0 ; index.z < GInf.at(0).getNumSlices() ; index.z++ ){ + + LegacyUnsignedIndex3D index_internal; + index_internal.x = 0; + index_internal.y = 0; + index_internal.z = index.z; + + if( ( MarkInterestingArea.at(0).at( index.z ).index_end.x != 0 ) && ( MarkInterestingArea.at(0).at( index.z ).index_end.y != 0 ) ){ + + for( index_internal.x = MarkInterestingArea.at(0).at( index.z ).index_begin.x ; index_internal.x <= MarkInterestingArea.at(0).at( index.z ).index_end.x ; index_internal.x++ ){ + for( index_internal.y = MarkInterestingArea.at(0).at( index.z ).index_begin.y ; index_internal.y <= MarkInterestingArea.at(0).at( index.z ).index_end.y ; index_internal.y++ ){ + + if( MaskField.at( 0 )->GetData( index_internal.x, index_internal.y, index_internal.z ) != brightness_border ){ + + MaskField.at( 0 )->PutData( index_internal.x, index_internal.y, index_internal.z, brightness_inside ); + + } + + } + } + + } + + index.y = MarkInterestingArea.at(0).at( index.z ).index_begin.y; + + for( index.x = MarkInterestingArea.at(0).at( index.z ).index_begin.x ; index.x <= MarkInterestingArea.at(0).at( index.z ).index_end.x ; index.x++ ){ + + if( MaskField.at( 0 )->GetData( index.x, index.y, index.z ) == brightness_inside ){ + MaskField.at( 0 )->PutData( index.x, index.y, index.z, brightness_outside ); + ConnectArea( brightness_inside , brightness_outside , index , MarkInterestingArea.at(0).at( index.z ).index_begin , MarkInterestingArea.at(0).at( index.z ).index_end ); + } + + } + + index.y = MarkInterestingArea.at(0).at( index.z ).index_end.y; + + for( index.x = MarkInterestingArea.at(0).at( index.z ).index_begin.x ; index.x <= MarkInterestingArea.at(0).at( index.z ).index_end.x ; index.x++ ){ + + if( MaskField.at( 0 )->GetData( index.x, index.y, index.z ) == brightness_inside ){ + MaskField.at( 0 )->PutData( index.x, index.y, index.z, brightness_outside ); + ConnectArea( brightness_inside , brightness_outside , index , MarkInterestingArea.at(0).at( index.z ).index_begin , MarkInterestingArea.at(0).at( index.z ).index_end ); + } + + } + + index.x = MarkInterestingArea.at(0).at( index.z ).index_begin.x; + + for( index.y = MarkInterestingArea.at(0).at( index.z ).index_begin.y ; index.y <= MarkInterestingArea.at(0).at( index.z ).index_end.y ; index.y++ ){ + + if( MaskField.at( 0 )->GetData( index.x, index.y, index.z ) == brightness_inside ){ + MaskField.at( 0 )->PutData( index.x, index.y, index.z, brightness_outside ); + ConnectArea( brightness_inside , brightness_outside , index , MarkInterestingArea.at(0).at( index.z ).index_begin , MarkInterestingArea.at(0).at( index.z ).index_end ); + } + + } + + index.x = MarkInterestingArea.at(0).at( index.z ).index_end.x; + + for( index.y = MarkInterestingArea.at(0).at( index.z ).index_begin.y ; index.y <= MarkInterestingArea.at(0).at( index.z ).index_end.y ; index.y++ ){ + + if( MaskField.at( 0 )->GetData( index.x, index.y, index.z ) == brightness_inside ){ + MaskField.at( 0 )->PutData( index.x, index.y, index.z, brightness_outside ); + ConnectArea( brightness_inside , brightness_outside , index , MarkInterestingArea.at(0).at( index.z ).index_begin , MarkInterestingArea.at(0).at( index.z ).index_end ); + } + + } + + + + } + + + } + + + + + + + void Mask::SetDoseVoxelInStructureVector(){ + + _doseVoxelInStructure.clear(); + + DoseVoxel aDoseVoxel; + + LegacyUnsignedIndex3D index; + index.x = 0; + index.y = 0; + index.z = 0; + + LegacyDoseVoxelIndex3D aDoseIndex; + aDoseIndex.x = 0; + aDoseIndex.y = 0; + aDoseIndex.z = 0; + + double fraction = 0; + + + for( index.z = 0 ; index.z < GInf.at(0).getNumSlices() ; index.z++ ){ + + if( FieldOfIntersections ){ + FieldOfIntersections->SetZero(); + GetIntersectionInformationThisSlice( index.z ); + + } + + for( index.x = 0 ; index.x < GInf.at(0).getNumColumns() ; index.x++ ){ + for( index.y = 0 ; index.y < GInf.at(0).getNumRows() ; index.y++ ){ + + + aDoseIndex.x = index.x; + aDoseIndex.y = index.y; + aDoseIndex.z = index.z; + + + if( brightness_inside == MaskField.at( 0 )->GetData( index.x, index.y, index.z ) ){ + + //doseVoxelIndexInStructure.push_back( aDoseIndex ); + fraction = 1.0; + + + DoseVoxel doseVoxel=DoseVoxel(aDoseIndex,&(GInf.at(0))); + doseVoxel.setProportionInStr(fraction); + this->_doseVoxelInStructure.push_back(doseVoxel); + // und jetzt fraction irgendwie anhaengen an geeignete Struktur und dann raus damit + //std::cout << "inside: "<GetData( index.x, index.y, index.z ) ){ + + + + if( !do_detailed_subvox ){ /*Lanlan*/ + + + DoseVoxel doseVoxel=DoseVoxel(aDoseIndex,&(GInf.at(0))); + fraction=doseVoxel.voxelInStructure(*_structure);//simple test + if(fraction>0){ + //doseVoxelIndexInStructure.push_back( aDoseIndex ); + doseVoxel.setProportionInStr(fraction); + this->_doseVoxelInStructure.push_back(doseVoxel); + } + + + + } + else{ /*Martina..............*/ + + //doseVoxelIndexInStructure.push_back( aDoseIndex ); + + fraction = 1.0; + + + + fraction = GetFractionInside( aDoseIndex ); + + if( ( fraction < 0 ) || ( fraction > 1.0 ) ) assert(0); + + + + DoseVoxel doseVoxel=DoseVoxel(aDoseIndex,&(GInf.at(0))); + doseVoxel.setProportionInStr( fraction ); + _doseVoxelInStructure.push_back( doseVoxel ); + + } + + + + } + } + } + } + + + + + double total_volume = 0; + + for( int counter = 0 ; counter < _doseVoxelInStructure.size() ; counter++ ){ + + total_volume += _doseVoxelInStructure.at( counter ).getProportionInStr(); + + } + + //std::cout<< " total_volume : " << total_volume <= index_begin.x ) && ( start_index.y >= index_begin.y ) && ( start_index.x <= index_end.x ) && ( start_index.y <= index_end.y ) ){ + + std::deque index_list; + + LegacyUnsignedIndex2D this_index; + this_index.x = start_index.x; + this_index.y = start_index.y; + + LegacyUnsignedIndex2D another_index; + another_index.x = 0; + another_index.y = 0; + + MaskField.at( 0 )->PutData( start_index.x, start_index.y, start_index.z, brightness_to ); + index_list.push_back( this_index ); + + int counter = 1; + + while( index_list.size() > 0 ){ + + // std::cout<< " counter " << counter < index_begin.x ){ + another_index.x = this_index.x - 1; + another_index.y = this_index.y; + if( MaskField.at( 0 )->GetData( another_index.x , another_index.y , start_index.z ) == brightness_from ){ + index_list.push_back( another_index ); + counter++; + MaskField.at( 0 )->PutData( another_index.x, another_index.y, start_index.z, brightness_to ); + } + } + + if( this_index.y > index_begin.y ){ + another_index.x = this_index.x; + another_index.y = this_index.y - 1; + if( MaskField.at( 0 )->GetData( another_index.x , another_index.y , start_index.z ) == brightness_from ){ + index_list.push_back( another_index ); + counter++; + MaskField.at( 0 )->PutData( another_index.x, another_index.y, start_index.z, brightness_to ); + } + } + + + if( this_index.x < index_end.x ){ + another_index.x = this_index.x + 1; + another_index.y = this_index.y; + if( MaskField.at( 0 )->GetData( another_index.x , another_index.y , start_index.z ) == brightness_from ){ + index_list.push_back( another_index ); + counter++; + MaskField.at( 0 )->PutData( another_index.x, another_index.y, start_index.z, brightness_to ); + } + } + + if( this_index.y < index_end.y ){ + another_index.x = this_index.x; + another_index.y = this_index.y + 1; + if( MaskField.at( 0 )->GetData( another_index.x , another_index.y , start_index.z ) == brightness_from ){ + index_list.push_back( another_index ); + counter++; + MaskField.at( 0 )->PutData( another_index.x, another_index.y, start_index.z, brightness_to ); + } + } + + + } + + } + + } + + + // die folgende Funktion prueft immer erst, ob es sich um einen Schnittpunkt mit der Ecke handelt. Falls nicht wird er in die Reihe der Kantenschnittpunkte einsortiert. Betrachtet wird lediglich die erste Ecke. + void Mask::SetThisIntersectionToCornerOrEdge( unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D, unsigned int edge_number, IntersectionsThisVoxel& voxel_intersections ){ + + + if(edge_number==0){ + SetThisIntersection( index_x , (index_x+1) , index_y , intersection , coord2D.x, coord2D.y , voxel_intersections, edge_number ); + } + else if(edge_number==1){ + SetThisIntersection( index_y , (index_y+1) , ( index_x + 1 ) , intersection , coord2D.y, coord2D.x , voxel_intersections, edge_number ); + //SetThisIntersectionYIncreasing( index_x , index_y , intersection , coord2D , voxel_intersections ); + } + else if(edge_number==2){ + SetThisIntersection( ( index_x + 1 ) , index_x , ( index_y + 1 ) , intersection , coord2D.x, coord2D.y , voxel_intersections, edge_number ); + //SetThisIntersectionXDecreasing( index_x , index_y , intersection , coord2D , voxel_intersections ); + } + else if(edge_number==3){ + SetThisIntersection( ( index_y + 1 ) , index_y , index_x , intersection , coord2D.y, coord2D.x , voxel_intersections, edge_number ); + //SetThisIntersectionYDecreasing( index_x , index_y , intersection , coord2D , voxel_intersections ); + } + else assert(0); + + } + + + + void Mask::SetThisIntersection( unsigned int corner_of_interest , unsigned int corner_next, unsigned int corner_fixed , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , double coord_of_interest , double coord_fixed, IntersectionsThisVoxel& voxel_intersections , unsigned int edge_number ){ + + if( coord_fixed != static_cast(corner_fixed) ) assert(0); // this should never happen since coord_fixed is the coordinate which is the same all along the edge that starts with corner_fixed + + + if( coord_of_interest == static_cast(corner_of_interest) ){ // falls der Schnittpunkt direkt am Anfang der Kante liegt also der Anfangspunkt der Kante ist + + voxel_intersections.corner_intersections_intersection_coord.at(edge_number).push_back( intersection ); // dann haenge den Schnittpunkt an die Liste der Schnittpunkte mit eben diesem Anfangspunkt der Kante an + return; + + } + else{ + + if( voxel_intersections.edge_intersections_intersection_coord.at(edge_number).size() > 0 ){ // falls die Kante schon Schnittpunkte hat + double first_coord_double = voxel_intersections.edge_intersections_vox_coord.at(edge_number).at(0).at(0); + // In der naechsten Zeile wird geprueft, ob der Schnittpunkt zwischen dem Anfang der Kante und dem ersten bereits bekannten Schnittpunkt liegt. Falls ja, wird er entsprechend eingefuegt. + if( CompareIfBetweenInsertAtBeginning( coord_of_interest , static_cast(corner_of_interest) , first_coord_double , edge_number, intersection , voxel_intersections ) ) return; + } + // naechste Zeile: Pruefe, ob der Schnittpunkt in der Liste der Schnittpunkte mit dieser Kante schon vorhanden ist und falls ja, verlaengere die Liste der Punkte, die eben in diesem Punkt schneiden um den neuen Schnittpunkt. + // Puntemaessig ist das zwar der selbe, der schon mindestens einmal da ist, aber intersection ist ja eine Art Index im PolygonOfIntersections + // und muss dann hier einsortiert werden. Denn das Polygon kann tatsaechlich mehrfach durch den selben Punkt laufen. + // Falls der Schnittpunkt noch nicht vorkommt, aber zwischen zweien ist, die bereits vorkommen, wird er entsprechend eingefuegt. + if( CheckIfIndenticalOrBetweenRespectiveVectorElementsAndDoInsert( coord_of_interest , edge_number, intersection , voxel_intersections ) ) return; + + double that_coord_double = 0; + + // Die folgenden beiden Zeilen sind dafuer zustaendig that_coord_double mit entweder dem letzten bereits bekannten Schnittpunkt dieser Kante, + // oder alternativ mit dem Anfangspunkt der Kante zu belegen. + if( voxel_intersections.edge_intersections_vox_coord.at(edge_number).size() > 0 ) that_coord_double = voxel_intersections.edge_intersections_vox_coord.at(edge_number).at( ( voxel_intersections.edge_intersections_intersection_coord.at(edge_number).size() - 1 ) ).at(0) ; + else that_coord_double = static_cast( corner_of_interest ); + + if( that_coord_double == coord_of_interest ){ + + if( coord_of_interest == static_cast( corner_of_interest ) ) assert( 0 ); // Das kann nicht sein, denn falls der Schnittpunkt wirklich der erste Punkt der Kante waere, muesste das ganz zu Beginn dieser Funktion bereits erkannt worden sein und die Funktion haette dann bereits returned. Falls that_coord_double also coord_of_interest (eizusortierender Punk) ist, kann dieser nicht der Anfangspunkt der Kante sein. + + // der einzusortierende Punkt coord_of_interest ist gerade gleich dem letzten bereits gefundenen Schnittpunkt that_coord_double und wird der Liste seinder Vorkommnisse entsprechend hinzugefuegt. + voxel_intersections.edge_intersections_vox_coord.at(edge_number).at( ( voxel_intersections.edge_intersections_intersection_coord.at(edge_number).size() - 1 ) ).push_back( coord_of_interest ); + voxel_intersections.edge_intersections_intersection_coord.at(edge_number).at( ( voxel_intersections.edge_intersections_intersection_coord.at(edge_number).size() - 1 ) ).push_back( intersection ); + return; + + } + else{ + + // Hier wird geprueft, ob der betrachtete Punkt zwischen dem letzten bereits gefundenen Schnittpunkt und dem Ende der Kante liegt. Falls ja, wird er vor dem Kantenende entsprechend eingefuegt. + if( CompareIfBetweenAppend( coord_of_interest , that_coord_double, static_cast( corner_next ), edge_number, intersection , voxel_intersections ) ){ + + return; + + } + + if( coord_of_interest == static_cast( corner_next ) ) assert(0); // this case is not supposed to be considered here. Its the next edge and should have gone elsewhere. Das Ende der Kante ist der Anfang der naechsten Kante uns haette daher bei dieser naechsten Kante landen muessen. + } + assert( 0 ); // assigned to the wrong edge by programmer. It may be the second corner attachted to this edge, however, it is not supposed to be considered here. + } + + } + + + + void Mask::SetThisIntersectionXIncreasing( unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections , unsigned int edge_number ){ + + if( coord2D.y != static_cast(index_y) ) assert(0); // this should never happen + + if( coord2D.x == static_cast(index_x) ){ + + voxel_intersections.corner_intersections_intersection_coord.at(0).push_back( intersection ); + return; + + } + else{ + + if( voxel_intersections.edge_intersections_intersection_coord.at(0).size() > 0 ){ + + double first_coord_double = voxel_intersections.edge_intersections_vox_coord.at(0).at(0).at(0); + if( CompareIfBetweenInsertAtBeginning( coord2D.x , static_cast(index_x) , first_coord_double , edge_number, intersection , voxel_intersections ) ) return; + + } + + if( CheckIfIndenticalOrBetweenRespectiveVectorElementsAndDoInsert( coord2D.x , edge_number, intersection , voxel_intersections ) ) return; + + double that_coord_double = 0; + + if( voxel_intersections.edge_intersections_vox_coord.at(0).size() > 0 ) that_coord_double = voxel_intersections.edge_intersections_vox_coord.at(0).at( ( voxel_intersections.edge_intersections_intersection_coord.at(0).size() - 1 ) ).at(0) ; + else that_coord_double = static_cast( index_x ); + + + if( that_coord_double == coord2D.x ){ + + if( coord2D.x == static_cast( index_x ) ) assert( 0 ); + + voxel_intersections.edge_intersections_vox_coord.at(0).at( ( voxel_intersections.edge_intersections_intersection_coord.at(0).size() - 1 ) ).push_back( coord2D.x ); + voxel_intersections.edge_intersections_intersection_coord.at(0).at( ( voxel_intersections.edge_intersections_intersection_coord.at(0).size() - 1 ) ).push_back( intersection ); + return; + + } + else{ + + if( CompareIfBetweenAppend( coord2D.x , that_coord_double, static_cast( index_x + 1 ), edge_number, intersection , voxel_intersections ) ) return; + + if( coord2D.x == static_cast( index_x + 1 ) ) assert(0); // this case is not supposed to be treated here. + + } + + assert( 0 ); // assigned to the wrong edge by programmer. It may be the second corner attachted to this edge, however, it is not supposed to be treated here. + + } + + } + + + bool Mask::CompareIfBetweenAppend( double value_to_compare , double small_value_to_compare_with , double big_value_to_compare_with, unsigned int edge_number, IterativePolygonInTermsOfIntersections::WhichIntersection intersection , IntersectionsThisVoxel& voxel_intersections ){ + + if( ( ( value_to_compare < big_value_to_compare_with ) && ( value_to_compare > small_value_to_compare_with ) ) || ( ( value_to_compare > big_value_to_compare_with ) && ( value_to_compare < small_value_to_compare_with ) ) ){ + + std::vector< double > double_vec; + double_vec.push_back(value_to_compare) ; + + std::vector< IterativePolygonInTermsOfIntersections::WhichIntersection > intersection_vec; + intersection_vec.push_back( intersection ); + + voxel_intersections.edge_intersections_vox_coord.at(edge_number).push_back( double_vec ); + voxel_intersections.edge_intersections_intersection_coord.at(edge_number).push_back( intersection_vec ); + return true; + + } + else return false; + + } + + + bool Mask::CheckIfIndenticalOrBetweenRespectiveVectorElementsAndDoInsert( double value_to_compare , unsigned int edge_number, IterativePolygonInTermsOfIntersections::WhichIntersection intersection , IntersectionsThisVoxel& voxel_intersections ){ + + for( int i = 0 ; i < ( static_cast(voxel_intersections.edge_intersections_intersection_coord.at(edge_number).size()) - 1 ) ; i++ ){ + + double the_coord_double = voxel_intersections.edge_intersections_vox_coord.at(edge_number).at(i).at(0) ; + + if( value_to_compare == the_coord_double ){ + + voxel_intersections.edge_intersections_vox_coord.at(edge_number).at(i).push_back( value_to_compare ); + voxel_intersections.edge_intersections_intersection_coord.at(edge_number).at(i).push_back( intersection ); + return true; + + } + else{ + + double another_double = voxel_intersections.edge_intersections_vox_coord.at(edge_number).at(i+1).at(0) ; + + if( ( ( another_double > value_to_compare ) && ( the_coord_double < value_to_compare ) ) || ( ( another_double < value_to_compare ) && ( the_coord_double > value_to_compare ) ) ){ + + std::vector< double > double_vec; + double_vec.push_back( value_to_compare ) ; + std::vector< IterativePolygonInTermsOfIntersections::WhichIntersection > intersection_vec; + intersection_vec.push_back( intersection ); + intersection_coord_iterator_type it = voxel_intersections.edge_intersections_intersection_coord.at(edge_number).begin() + ( i + 1 ); + + voxel_intersections.edge_intersections_vox_coord.at(edge_number).insert( ( voxel_intersections.edge_intersections_vox_coord.at(edge_number).begin() + ( i + 1 ) ) , double_vec ); + voxel_intersections.edge_intersections_intersection_coord.at(edge_number).insert( it , intersection_vec ); + + return true; + + } + } + + } + + return false; + + } + + + bool Mask::CompareIfBetweenInsertAtBeginning( double value_to_compare , double small_value_to_compare_with , double big_value_to_compare_with, unsigned int edge_number, IterativePolygonInTermsOfIntersections::WhichIntersection intersection , IntersectionsThisVoxel& voxel_intersections ){ + + if( ( ( value_to_compare > small_value_to_compare_with ) && ( value_to_compare < big_value_to_compare_with ) ) || ( ( value_to_compare < small_value_to_compare_with ) && ( value_to_compare > big_value_to_compare_with ) ) ){ + + std::vector< double > double_vec; + double_vec.push_back(value_to_compare) ; + std::vector< IterativePolygonInTermsOfIntersections::WhichIntersection > intersection_vec; + intersection_vec.push_back( intersection ); + intersection_coord_iterator_type it = voxel_intersections.edge_intersections_intersection_coord.at(edge_number).begin(); + + voxel_intersections.edge_intersections_vox_coord.at(edge_number).insert( voxel_intersections.edge_intersections_vox_coord.at(edge_number).begin(), double_vec ); + voxel_intersections.edge_intersections_intersection_coord.at(edge_number).insert( it , intersection_vec ); + + return true; + + } + return false; + + } + + + void Mask::SetThisIntersectionYIncreasing( unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections ){ + + if( coord2D.x != static_cast(index_x+1) ) assert(0); // this should never happen + + // to be implemented + + + } + + + void Mask::SetThisIntersectionXDecreasing( unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections ){ + + if( coord2D.y != static_cast(index_y+1) ) assert(0); // this should never happen + + // to be implemented + + } + + + void Mask::SetThisIntersectionYDecreasing( unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections ){ + + if( coord2D.x != static_cast(index_x) ) assert(0); // this should never happen + + // to be implemented + + } + + + void Mask::Init(){ + + SliceStructureCorrespondency.clear(); + do_detailed_subvox = true; + if( do_detailed_subvox ){ + FieldOfIntersections = new rttb::masks::legacy::FieldOfPointer( GInf.at(0).getNumColumns() , GInf.at(0).getNumRows() , 1 ); + } + + for( int i = 0 ; i < MaskField.size() ; i++ ){ + if(MaskField.at(i)!=NULL)MaskField.at(i)->~FieldOfScalar(); + } + MaskField.resize(0); + rttb::masks::legacy::FieldOfScalar* a_field_of_scalar = new rttb::masks::legacy::FieldOfScalar( GInf.at(0).getNumColumns() , GInf.at(0).getNumRows() , GInf.at(0).getNumSlices() ); + MaskField.push_back( a_field_of_scalar ); + + brightness_inside = 50; + brightness_border = 10; + brightness_outside = 0; + brightness_unknown = -10; + + LegacyDoseVoxelIndex3D doseVoxelIndex; + doseVoxelIndex.x = 0; + doseVoxelIndex.y = 0; + doseVoxelIndex.z = 0 ; + + int currentSliceNumber=0; + + LegacyPolygonSequenceType strVector = _structure->getLegacyStructureVector(); + LegacyPolygonSequenceType& strVector_ref = strVector; + StructureInTermsOfIntersections inters; + inters.resize( strVector.size() ); + Intersections.push_back( inters ); + + + StructureInTermsOfIntersections sitoi=Intersections.at(0); + //StructureInTermsOfIntersections:: + + for(unsigned int i = 0 ; i < strVector.size() ; i++ ){ + + Intersections.at(0).at( i ).resize( strVector.at( i ).size() ); + + LegacyWorldCoordinate2D index_coord; + index_coord.x = 0; + index_coord.y = 0; + + LegacyWorldCoordinate3D contour_point_world; + contour_point_world.x = 0; + contour_point_world.y = 0; + contour_point_world.z = 0; + + for(unsigned int j = 0 ; j < strVector.at( i ).size() ; j++ ){ + + contour_point_world = strVector.at( i ).at( j ); + index_coord = GetDoubleIndex2D( contour_point_world.x , contour_point_world.y ); + Intersections.at(0).at( i ).at( j ).contour_point_voxel_coord.x = index_coord.x; + Intersections.at(0).at( i ).at( j ).contour_point_voxel_coord.y = index_coord.y; + Intersections.at(0).at( i ).at( j ).contour_point_voxel_coord.z = GetZIndex(contour_point_world.z); + + + } + + } + + + // iteration slices + for( doseVoxelIndex.z = 0 ; doseVoxelIndex.z < GInf.at(0).getNumSlices() ; doseVoxelIndex.z++ ){ + + correspoinding_structure_vector str_v; + + LegacyWorldCoordinate2D the_index; + the_index.x = 0; + the_index.y = 0; + + DoseVoxel doseVoxel; + //from dose index to worldcoordinate + doseVoxel.setDoseVoxel( doseVoxelIndex , &(GInf.at(0)) ); + + str_v=doseVoxel.getZIntersectPolygonIndexVector(*_structure ); //Lanlan + + + SliceStructureCorrespondency.push_back( str_v ); + + + } + + + InitInterestingArea(); + InitMultiResolution(); + + } + + + + + + void Mask::InitInterestingArea(){ + + MarkInterestingArea.clear(); + + std::vector interesting_area_this_resolution; + + LegacyDoseVoxelIndex3D doseVoxelIndex; + doseVoxelIndex.x = 0; + doseVoxelIndex.y = 0; + doseVoxelIndex.z = 0 ; + + int currentSliceNumber=0; + + LegacyPolygonSequenceType strVector = _structure->getLegacyStructureVector(); + LegacyPolygonSequenceType& strVector_ref = strVector; + + // iteration slices + for( doseVoxelIndex.z = 0 ; doseVoxelIndex.z < GInf.at(0).getNumSlices() ; doseVoxelIndex.z++ ){ + + LegacyWorldCoordinate1D x_min = 1000000000; + LegacyWorldCoordinate1D x_max = -1000000000; + LegacyWorldCoordinate1D y_min = 1000000000; + LegacyWorldCoordinate1D y_max = -1000000000; + + LegacyWorldCoordinate1D x_index_min = 1000000000; + LegacyWorldCoordinate1D x_index_max = -1000000000; + LegacyWorldCoordinate1D y_index_min = 1000000000; + LegacyWorldCoordinate1D y_index_max = -1000000000; + + LegacyWorldCoordinate2D the_index; + the_index.x = 0; + the_index.y = 0; + + DoseVoxel doseVoxel; + //from dose index to worldcoordinate + doseVoxel.setDoseVoxel( doseVoxelIndex ,&( GInf.at(0)) ); + std::vector sliceV = doseVoxel.getZIntersectPolygonIndexVector( *_structure ); + + + + //if( ( strVector.size() != currentSliceNumber ) && ( currentSliceNumber != (-1) ) ){ + for(int indexV=0; indexV x_max ){ + x_max = firstPoint.x; + } + if( firstPoint.y > y_max ){ + y_max = firstPoint.y; + } + + the_index = GetDoubleIndex2D( firstPoint.x , firstPoint.y ); + + if( the_index.x < x_index_min ){ + x_index_min = the_index.x; + } + if( the_index.y < y_index_min ){ + y_index_min = the_index.y; + } + if( the_index.x > x_index_max ){ + x_index_max = the_index.x; + } + if( the_index.y > y_index_max ){ + y_index_max = the_index.y; + } + + } + }//end for indexV + + + if(sliceV.size()>0){ + LegacyArea2D area_2d; + area_2d.x_begin = x_min; + area_2d.x_end = x_max; + area_2d.y_begin = y_min; + area_2d.y_end = y_max; + + int small_enough_x = static_cast(floor( x_index_min ) - 1); + if( small_enough_x < 0 ) small_enough_x = 0; + area_2d.index_begin.x = small_enough_x; + + int small_enough_y = static_cast(floor( y_index_min ) - 1); + if( small_enough_y < 0 ) small_enough_y = 0; + area_2d.index_begin.y = small_enough_y; + + int large_enough_x = static_cast(floor( x_index_max ) + 1); + if( large_enough_x >= GInf.at(0).getNumColumns() ) large_enough_x = ( GInf.at(0).getNumColumns() - 1 ); + area_2d.index_end.x = large_enough_x; + + int large_enough_y = static_cast(floor( y_index_max ) + 1); + if( large_enough_y >= GInf.at(0).getNumRows() ) large_enough_y = ( GInf.at(0).getNumRows() - 1 ); + area_2d.index_end.y = large_enough_y; + interesting_area_this_resolution.push_back( area_2d ); // std::vector + } + else{ + + LegacyArea2D area_2d; + area_2d.Init(); + interesting_area_this_resolution.push_back( area_2d ); + + } + + } + + MarkInterestingArea.push_back( interesting_area_this_resolution ); + + } + + + void Mask::InitMultiResolution(){ + + bool done = false; + + + while( done == false ){ + + int new_dim_x = MaskField.at( 0 )->GetDimX() / static_cast( GetBlockLengthThisResolutionLevel( MaskField.size() ) ) + 1; + int new_dim_y = MaskField.at( 0 )->GetDimY() / static_cast( GetBlockLengthThisResolutionLevel( MaskField.size() ) ) + 1; + + + + if( new_dim_x > 20 && new_dim_y > 20 ){ + + + core::GeometricInfo NewGeomInf; + + NewGeomInf.setImagePositionPatient(GInf.at( GInf.size() - 1 ).getImagePositionPatient()); + + NewGeomInf.setOrientationMatrix(GInf.at( GInf.size() - 1 ).getOrientationMatrix()); + + NewGeomInf.setSliceThickness(GInf.at( GInf.size() - 1 ).getSliceThickness()); + NewGeomInf.setNumSlices(GInf.at( GInf.size() - 1 ).getNumSlices()); + + NewGeomInf.setNumColumns(new_dim_x); + NewGeomInf.setNumRows(new_dim_y); + + double power = 1; + for( int i = 0 ; i < GInf.size() ; i++ ){ + power *= 2; + } + + NewGeomInf.setPixelSpacingRow(GInf.at( 0 ).getPixelSpacingRow() * power); + NewGeomInf.setPixelSpacingColumn(GInf.at( 0 ).getPixelSpacingColumn() * power); + + GInf.push_back( NewGeomInf ); + + rttb::masks::legacy::FieldOfScalar* a_field_of_scalar = new rttb::masks::legacy::FieldOfScalar( GInf.at( GInf.size() - 1 ).getNumColumns() , GInf.at( GInf.size() - 1 ).getNumRows() , GInf.at( GInf.size() - 1 ).getNumSlices() ); + MaskField.push_back( a_field_of_scalar ); + + std::vector interesting_area_this_resolution; + + + for( int z = 0 ; z < MarkInterestingArea.at( 0 ).size() ; z++ ){ + + LegacyArea2D NewArea; + NewArea.x_begin = MarkInterestingArea.at( 0 ).at( z ).x_begin; + NewArea.x_end = MarkInterestingArea.at( 0 ).at( z ).x_end; + NewArea.y_begin = MarkInterestingArea.at( 0 ).at( z ).y_begin; + NewArea.y_end = MarkInterestingArea.at( 0 ).at( z ).y_end; + + NewArea.index_begin.x = ( MarkInterestingArea.at( 0 ).at( z ).index_begin.x / power ); + if( NewArea.index_begin.x > 0 ) NewArea.index_begin.x -= 1; + NewArea.index_begin.y = ( MarkInterestingArea.at( 0 ).at( z ).index_begin.y / power ); + if( NewArea.index_begin.y > 0 ) NewArea.index_begin.y -= 1; + NewArea.index_end.x = ( MarkInterestingArea.at( 0 ).at( z ).index_end.x / power ); + NewArea.index_end.x += 1; + if( NewArea.index_end.x >= MaskField.at( MaskField.size() - 1 )->GetDimX() ) NewArea.index_end.x = ( MaskField.at( MaskField.size() - 1 )->GetDimX() - 1 ); + NewArea.index_end.y = ( MarkInterestingArea.at( 0 ).at( z ).index_end.y / power ); + NewArea.index_end.y += 1; + if( NewArea.index_end.y >= MaskField.at( MaskField.size() - 1 )->GetDimY() ) NewArea.index_end.y = ( MaskField.at( MaskField.size() - 1 )->GetDimY() - 1 ); + + interesting_area_this_resolution.push_back( NewArea ); + + } + + MarkInterestingArea.push_back( interesting_area_this_resolution ); + + } + else done = true; + + } + + /* + std::cout<< " MaskField.size() " << MaskField.size() <GetDimX() : " << MaskField.at( i )->GetDimX() <GetDimY() : " << MaskField.at( i )->GetDimY() <pixelSpacingColumn : " << GInf.at( i ).getPixelSpacingColumn() <sliceThickness : " << GInf.at( i ).getSliceThickness() <column : " << GInf.at( i )getNumColumns() <row : " << GInf.at( i )getNumRows() <numberOfFrames : " << GInf.at( i ).getNumSlices() < 1 ) || ( MarkInterestingArea.at( i ).at( z ).index_end.y > 1 ) ){ + + std::cout<< " z : " << z <getLegacyStructureVector(); + LegacyPolygonSequenceType& strVector_ref = strVector; + StructureInTermsOfIntersections inters; + inters.resize( strVector.size() ); + Intersections.push_back( inters ); + + for(unsigned int i = 0 ; i < strVector.size() ; i++ ){ + + Intersections.at(counter).at( i ).resize( strVector.at( i ).size() ); + + LegacyWorldCoordinate2D index_coord; + index_coord.x = 0; + index_coord.y = 0; + + LegacyWorldCoordinate3D contour_point_world; + contour_point_world.x = 0; + contour_point_world.y = 0; + contour_point_world.z = 0; + + for(unsigned int j = 0 ; j < strVector.at( i ).size() ; j++ ){ + + contour_point_world = strVector.at( i ).at( j ); + index_coord = GetDoubleIndex2D( contour_point_world.x , contour_point_world.y , counter ); + Intersections.at(counter).at( i ).at( j ).contour_point_voxel_coord.x = index_coord.x; + Intersections.at(counter).at( i ).at( j ).contour_point_voxel_coord.y = index_coord.y; + Intersections.at(counter).at( i ).at( j ).contour_point_voxel_coord.z = GetZIndex(contour_point_world.z); + + + } + + } + + } + + if( Intersections.size() != GInf.size() ) assert(0); + + /* + for( int counter = 0 ; counter < GInf.size() ; counter++ ){ + + std::cout<< " next resolution level " << counter <getLegacyStructureVector(); + LegacyPolygonSequenceType& strVector_ref = strVector; + + LegacyWorldCoordinate3D firstPoint; + firstPoint.x = 0; + firstPoint.y = 0; + firstPoint.z = 0; + LegacyWorldCoordinate3D secondPoint; + secondPoint.x = 0; + secondPoint.y = 0; + secondPoint.z = 0; + + + // iteration z direction + for(unsigned int struct_index = 0 ; struct_index < strVector.size() ; struct_index++ ){ + + + // iteration polygon + for( int point_index = 0 ; point_index < ( strVector.at(struct_index).size() - 1 ); point_index++ ){ + + + firstPoint = strVector.at(struct_index).at( point_index ); + secondPoint = strVector.at(struct_index).at( point_index + 1 ); + + + std::vector& raw_intersections_ref = Intersections.at(resolution_level).at( struct_index ).at( point_index ).raw_intersections; + std::vector& column_intersections_ref = Intersections.at(resolution_level).at( struct_index ).at( point_index ).column_intersections; + + std::vector& intersections_raw_and_column_ref = Intersections.at(resolution_level).at( struct_index ).at( point_index ).intersections_raw_and_column; + + + GoIntersectRaw( firstPoint , secondPoint , raw_intersections_ref, resolution_level ); + GoIntersectColumn( firstPoint , secondPoint , column_intersections_ref, resolution_level ); + + UnifyRawAndColumn( firstPoint , raw_intersections_ref , column_intersections_ref , intersections_raw_and_column_ref, resolution_level ); + + + } + + + // the connection between the last and the first Point of the Polygon needs to be checked as well + + if( strVector.at(struct_index).size() > 0 ){ + + firstPoint = strVector.at( struct_index ).at( ( strVector.at(struct_index).size() - 1 ) ); + secondPoint = strVector.at( struct_index ).at( 0 ); + + std::vector& raw_intersections_ref = Intersections.at(resolution_level).at( struct_index ).at( ( strVector.at(struct_index).size() - 1 ) ).raw_intersections; + std::vector& column_intersections_ref = Intersections.at(resolution_level).at( struct_index ).at( ( strVector.at(struct_index).size() - 1 ) ).column_intersections; + + std::vector& intersections_raw_and_column_ref = Intersections.at(resolution_level).at( struct_index ).at( ( strVector.at(struct_index).size() - 1 ) ).intersections_raw_and_column; + + GoIntersectRaw( firstPoint , secondPoint , raw_intersections_ref, resolution_level ); + GoIntersectColumn( firstPoint , secondPoint , column_intersections_ref, resolution_level ); + + UnifyRawAndColumn( firstPoint, raw_intersections_ref , column_intersections_ref, intersections_raw_and_column_ref, resolution_level ); + + } + + + } + + /*for( int struct_index = 0 ; struct_index < strVector.size() ; struct_index++ ){ + + // iteration polygon + + for( int point_index = 0 ; point_index < ( strVector.at(struct_index).size() - 1 ); point_index++ ){ + + + std::vector& raw_intersections_ref = Intersections.at(0).at(0).at( struct_index ).at( point_index ).raw_intersections; + + std::vector& column_intersections_ref = Intersections.at(0).at( struct_index ).at( point_index ).column_intersections; + + + for(int i=0;i& raw_intersections_ref , std::vector& column_intersections_ref , std::vector& intersections_raw_and_column_ref, int resolution_level ){ + + if( ( column_intersections_ref.size() > 0 ) && ( raw_intersections_ref.size() == 0 ) ){ + intersections_raw_and_column_ref = column_intersections_ref; + } + + if( ( raw_intersections_ref.size() > 0 ) && ( column_intersections_ref.size() == 0 ) ){ + intersections_raw_and_column_ref = raw_intersections_ref; + } + + + if( ( intersections_raw_and_column_ref.size() == 0 ) ){ // falls das nicht der Fall ist ist der Verlauf der Konturpunkte horizontal oder vertikal. Dann gibt es sowiso nur horizontale oder vertikale Schnittpunkte und die Vereinigung ist in GoIntersect... bereits geschehen. + + bool done = false; + + int internal_counter = 0; + int counter = 0 ; + + LegacyWorldCoordinate2D rawPoint; + rawPoint.x = 0; + rawPoint.y = 0; + + LegacyWorldCoordinate2D columnPoint; + columnPoint.x = 0; + columnPoint.y = 0; + + + LegacyWorldCoordinate2D firstPoint = GetDoubleIndex2D( the_firstPoint.x , the_firstPoint.y, resolution_level ); + + + while( ! done ){ + + + if( counter < raw_intersections_ref.size() )rawPoint = raw_intersections_ref.at( counter ); + else if( raw_intersections_ref.size() > 0 ) rawPoint = raw_intersections_ref.at( raw_intersections_ref.size() - 1 ); + if( internal_counter < column_intersections_ref.size() )columnPoint = column_intersections_ref.at( internal_counter ); + else if( column_intersections_ref.size() > 0 ) columnPoint = column_intersections_ref.at( column_intersections_ref.size() - 1 ); + + double distance_raw = ( firstPoint.y - rawPoint.y ) * ( firstPoint.y - rawPoint.y ) + ( firstPoint.x - rawPoint.x ) * ( firstPoint.x - rawPoint.x ); + double distance_column = ( firstPoint.x - columnPoint.x ) * ( firstPoint.x - columnPoint.x ) + ( firstPoint.y - columnPoint.y ) * ( firstPoint.y - columnPoint.y ); + + + + + if( distance_raw == distance_column ){ + if( internal_counter >= column_intersections_ref.size() ){ + counter++; + } + else{ + intersections_raw_and_column_ref.push_back( columnPoint ); + internal_counter++; + counter++; + } + } + else if( ( ( distance_raw > distance_column ) && ( internal_counter < column_intersections_ref.size() ) ) || ( counter >= ( raw_intersections_ref.size() ) ) ){ + intersections_raw_and_column_ref.push_back( columnPoint ); + internal_counter++; + } + else if( ( distance_raw < distance_column ) || ( internal_counter >= column_intersections_ref.size() ) ){ + if( ( counter < ( raw_intersections_ref.size() ) ) ){ + intersections_raw_and_column_ref.push_back( rawPoint ); + counter++; + } + } + + if( ( internal_counter >= column_intersections_ref.size() ) && ( counter >= raw_intersections_ref.size() ) )done = true; + + + } + + + /* for( int i = 0 ; i < column_intersections_ref.size() ; i++ ){ + + LegacyWorldCoordinate2D Point = column_intersections_ref.at( i ); + std::cout<< " column_intersections_ref.at( i ) Point.x !!!!!!!!!!!!!!!!!!!!!!!!!!!!: " << Point.x <& raw_intersections_ref, int resolution_level ){ + + + if( ( secondPoint.y - firstPoint.y ) != 0 ){ + if( ( secondPoint.x - firstPoint.x ) != 0 ){ + + + /* + Siehe GoIntersectColumn (ist eigentlich das selbe) + Auf der einen Gerade liegt das Strutkur Segment und die andere Gerade ist die Spalte + Beziehungsweise die Zeile. + Zwei Geleichungen und zwei Unbekannte .... + */ + + + /* + + NEBENRECHNUNG : + + linear connection between both points : + + x = firstPoint.x + ( secondPoint.x - firstPoint.x ) * lambda + y = firstPoint.y + ( secondPoint.y - firstPoint.y ) * lambda + + with lambda between zero and one representing the regarded structure segment + + equation raw : + + x = imagePositionPatient.x + x_offset_raw * (raw_index-0.5) + imageOrientationRow.x * pixelSpacingRow * (the_index.x-0.5) + y = imagePositionPatient.y + y_offset_raw * (raw_index-0.5) + imageOrientationRow.y * pixelSpacingRow * (the_index.x-0.5) + + x_offset_raw gibt die rauemliche Verschiebung beim aendern des zeilen index an + raw_index repraesentiert den index der betrachteten Zeile also die y-Position innerhalb der Spalte + + x_offset_raw = imageOrientationColumn.x*pixelSpacingColumn; + y_offset_raw = imageOrientationColumn.y*pixelSpacingColumn; + + unknown : lambda and the_index.x + the_index.x and raw_index represent the voxel + + Solution : + + First Equation: imagePositionPatient.x + x_offset_raw * (raw_index-0.5) + imageOrientationRow.x * pixelSpacingRow * (the_index.x-0.5) = firstPoint.x + ( secondPoint.x - firstPoint.x ) * lambda + Second Equation: imagePositionPatient.y + y_offset_raw * (raw_index-0.5) + imageOrientationRow.y * pixelSpacingRow * (the_index.x-0.5) = firstPoint.y + ( secondPoint.y - firstPoint.y ) * lambda + + + Lambda from second equation: + ( imagePositionPatient.y + y_offset_raw * (raw_index-0.5) + imageOrientationRow.y * pixelSpacingRow * (the_index.x-0.5) - firstPoint.y ) / ( secondPoint.y - firstPoint.y ) = lambda + + + First Equation: + ( imagePositionPatient.x + x_offset_raw * (raw_index-0.5) + imageOrientationRow.x * pixelSpacingRow * (the_index.x-0.5) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) = lambda + + + First into Second Equation : + imagePositionPatient.y + y_offset_raw * (raw_index-0.5) - firstPoint.y - ( imagePositionPatient.x - firstPoint.x + x_offset_raw * (raw_index-0.5) ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) = - imageOrientationRow.y * pixelSpacingRow * (the_index.x-0.5) + ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x )* ( imageOrientationRow.x * pixelSpacingRow * (the_index.x-0.5) ) + + + First into Second Equation : + imagePositionPatient.y + y_offset_raw * (raw_index-0.5) - firstPoint.y - ( imagePositionPatient.x - firstPoint.x + x_offset_raw * (raw_index-0.5) ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) = (the_index.x-0.5) * ( - imageOrientationRow.y * pixelSpacingRow + ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x )* ( imageOrientationRow.x * pixelSpacingRow ) ) + First into SecondEquation : + (the_index.x-0.5) = imagePositionPatient.y + y_offset_raw * (raw_index-0.5) - firstPoint.y - ( imagePositionPatient.x - firstPoint.x + x_offset_raw * (raw_index-0.5) ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) / ( ( - imageOrientationRow.y * pixelSpacingRow + ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x )* ( imageOrientationRow.x * pixelSpacingRow ) ) ) + + Der andere index ist raw_index und daher bereits bekannt. + Vorsicht, bevor ein Schnittpunkt als solcher akzeptiert werden kann muss untersucht werden + ob lambda zwischen null und eins liegt + da ansonsten Schnittpunkte der Segmentgeraden betrachtet werden, + die ausserhalb des eigentlichen Struktursegmentes liegen. + + */ + + + double x_offset_raw = GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn(); + double y_offset_raw = GInf.at(resolution_level).getImageOrientationColumn()(1) * GInf.at(resolution_level).getPixelSpacingColumn(); + + LegacyWorldCoordinate2D index_first_point; + index_first_point.x = 0; + index_first_point.y = 0; + LegacyWorldCoordinate2D index_second_point; + index_second_point.x = 0; + index_second_point.y = 0; + + // indicees der beiden uebergebenen Punkte bestimmen + index_first_point = GetDoubleIndex2D( firstPoint.x , firstPoint.y, resolution_level ); + index_second_point = GetDoubleIndex2D( secondPoint.x , secondPoint.y, resolution_level ); + + double index_diff = ( index_second_point.y - index_first_point.y ); + + + + int begin_ind = 0; + int end_ind = 0; + bool do_swap = 0; + + if( index_diff > 0 ){ + + begin_ind = static_cast( floor( index_first_point.y ) ); + end_ind = static_cast( floor( index_second_point.y ) + 1 ); + + } + else if( index_diff < 0 ){ + + do_swap = 1; + begin_ind = static_cast( floor( index_second_point.y ) ); + end_ind = static_cast( floor( index_first_point.y ) + 1 ); + + } + else{ + + begin_ind = 0; + end_ind = 0; + + } + + if( begin_ind < 0 ) begin_ind = 0; + if( end_ind > ( GInf.at(resolution_level).getNumRows() - 1 ) ) end_ind = GInf.at(resolution_level).getNumRows() - 1; + + LegacyWorldCoordinate2D coord2D; + coord2D.x = 0; + coord2D.y = 0; + LegacyWorldCoordinate3D the_index; + the_index.x = 0; + the_index.y = 0; + the_index.z = 0; + + double multi_res_offset = GetWorldCoordinateOffsetThisResolutionLevel( resolution_level ); + + //std::cout <<"go intersection raw: "<= 0 ) && ( lambda <= 1 ) ){ + raw_intersections_ref.push_back( coord2D ); + //std::cout << coord2D.toString()<(coord2D.x),static_cast(coord2D.y),static_cast(this->GetZIndex(firstPoint.z) )}; + DoseVoxel voxel=DoseVoxel(index,GInf.at(resolution_level)); + std::vector interPs=voxel.calcIntersectionPoints(firstPoint, secondPoint); + this->voxel_intersectionPoints.insert(VoxelIntersectionPointsMap::value_type(index, interPs));!!!Lanlan */ + + /*std::cout << "intersections: "< vecW; + LegacyWorldCoordinate3D p1={firstPoint.x, (firstPoint.y+(end_ind-raw_index)*GInf.at(resolution_level).getPixelSpacingRow()), firstPoint.z}; + LegacyWorldCoordinate3D p2={firstPoint.x, (firstPoint.y+(end_ind-raw_index+1)*GInf.at(resolution_level).getPixelSpacingRow()), firstPoint.z}; + vecW.push_back(p1); + vecW.push_back(p2); + LegacyDoseVoxelIndex3D dosevoxel={static_cast(coord2D.x),static_cast(coord2D.y),static_cast( this->GetZIndex(firstPoint.z) )}; + this->voxel_intersectionPoints.insert(VoxelIntersectionPointsMap::value_type(dosevoxel, vecW));*/ + } + + + } + if( do_swap ){ + + std::vector raw_intersections_to_be_swaped; + + for( int this_one = ( raw_intersections_ref.size() - 1 ) ; this_one >= 0 ; this_one-- ){ + + raw_intersections_to_be_swaped.push_back( raw_intersections_ref.at( this_one ) ); + + } + + raw_intersections_ref.swap( raw_intersections_to_be_swaped ); + + + } + + } + else{ + GoGetRawIntersectionsAlongThisColumn( firstPoint , secondPoint , raw_intersections_ref , resolution_level ); + } + + } + } + + + + + void Mask::GoGetRawIntersectionsAlongThisColumn( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& raw_intersections_ref, int resolution_level ){ + + LegacyWorldCoordinate2D index_first_point; + index_first_point.x = 0; + index_first_point.y = 0; + LegacyWorldCoordinate2D index_second_point; + index_second_point.x = 0; + index_second_point.y = 0; + + if( firstPoint.x != secondPoint.x ) assert(0); + + // indicees der beiden uebergebenen Punkte bestimmen + index_first_point = GetDoubleIndex2D( firstPoint.x , firstPoint.y, resolution_level ); + index_second_point = GetDoubleIndex2D( secondPoint.x , secondPoint.y, resolution_level ); + + double index_diff = ( index_second_point.y - index_first_point.y ); + + int begin_ind = 0; + int end_ind = 0; + bool do_swap = 0; + + if( index_diff > 0 ){ + + begin_ind = static_cast( floor( index_first_point.y ) ); + if( floor( index_first_point.y ) < index_first_point.y ) begin_ind += 1; + end_ind = static_cast( floor( index_second_point.y ) ); + + } + else if( index_diff < 0 ){ + + do_swap = 1; + begin_ind = static_cast( floor( index_second_point.y ) ); + if( floor( index_second_point.y ) < index_second_point.y ) begin_ind += 1; + end_ind = static_cast( floor( index_first_point.y ) ); + + } + else{ + + begin_ind = 0; + end_ind = 0; + + } + + if( begin_ind < 0 ) begin_ind = 0; + if( end_ind > ( GInf.at(resolution_level).getNumRows() - 1 ) ) end_ind = GInf.at(resolution_level).getNumRows() - 1; + + LegacyWorldCoordinate2D coord2D; + coord2D.x = 0; + coord2D.y = 0; + + //std::cout <<"go intersection raw along column: "< vecW; + //LegacyWorldCoordinate3D p1={firstPoint.x, (firstPoint.y+(raw_index-begin_ind)*GInf.at(resolution_level).getPixelSpacingRow()), firstPoint.z}; + //LegacyWorldCoordinate3D p2={firstPoint.x, (firstPoint.y+(raw_index-begin_ind+1)*GInf.at(resolution_level).getPixelSpacingRow()), firstPoint.z}; + //vecW.push_back(p1); + //vecW.push_back(p2); + //LegacyDoseVoxelIndex3D dosevoxel={static_cast(coord2D.x),static_cast(coord2D.y),static_cast(this->GetZIndex(firstPoint.z))}; + //this->voxel_intersectionPoints.insert(VoxelIntersectionPointsMap::value_type(dosevoxel, vecW)); + + //core::LegacyDoseVoxelIndex3D index={static_cast(coord2D.x),static_cast(coord2D.y),0}; + /*DoseVoxel voxel=DoseVoxel(dosevoxel,GInf.at(resolution_level)); + std::vector interPs=voxel.calcIntersectionPoints(firstPoint, secondPoint); + this->voxel_intersectionPoints.insert(VoxelIntersectionPointsMap::value_type(dosevoxel, interPs));*/ + + /*std::cout << "intersections: "< raw_intersections_to_be_swaped; + + for( int this_one = ( raw_intersections_ref.size() - 1 ) ; this_one >= 0 ; this_one-- ){ + + raw_intersections_to_be_swaped.push_back( raw_intersections_ref.at( this_one ) ); + + } + + raw_intersections_ref.swap( raw_intersections_to_be_swaped ); + + + } + + + } + + + + + void Mask::GoIntersectColumn( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& column_intersections_ref, int resolution_level ){ + + if( ( secondPoint.x - firstPoint.x ) != 0 ){ + + if( ( secondPoint.y - firstPoint.y ) != 0 ){ + + /* + Siehe GoIntersectRaw (ist eigentlich das selbe) + Auf der einen Gerade liegt das Strutkur Segment und die andere Gerade ist die Spalte + Beziehungsweise die Zeile. + Zwei Geleichungen und zwei Unbekannte .... + */ + + /* + + NEBENRECHNUNG : + + linear connection between the two points : + + x = firstPoint.x + ( secondPoint.x - firstPoint.x ) * lambda + y = firstPoint.y + ( secondPoint.y - firstPoint.y ) * lambda + + with lambda between zero and one representing the regarded structure segment + + equation column : + + x_offset_column gibt die rauemliche Verschiebung beim aendern des spalten index an + column_index repraesentiert den index der betrachteten Spalte also die x Position innerhalb der Zeile + + x = imagePositionPatient.x + x_offset_column * column_index + imageOrientationColumn.x * pixelSpacingColumn * the_index.y + y = imagePositionPatient.y + y_offset_column * column_index + imageOrientationColumn.y * pixelSpacingColumn * the_index.y + + x_offset_column = imageOrientationRow.x * pixelSpacingRow; + y_offset_column = imageOrientationRow.y * pixelSpacingRow; + + + Erste Geleuchung : x = firstPoint.x + ( secondPoint.x - firstPoint.x ) * lambda = imagePositionPatient.x + x_offset_column * column_index + imageOrientationColumn.x * pixelSpacingColumn * the_index.y + Zweite Geleuchung : y = firstPoint.y + ( secondPoint.y - firstPoint.y ) * lambda = imagePositionPatient.y + y_offset_column * column_index + imageOrientationColumn.y * pixelSpacingColumn * the_index.y + + Erste Gleuchung aufloesen nach lambda : + Erste Geleuchung : lambda = ( imagePositionPatient.x + x_offset_column * column_index + imageOrientationColumn.x * pixelSpacingColumn * the_index.y - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) + Lambda in zweite Gleichung einsetzen : + firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( imagePositionPatient.x + x_offset_column * column_index - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - imagePositionPatient.y + y_offset_column * column_index = imageOrientationColumn.y * pixelSpacingColumn * the_index.y - ( imageOrientationColumn.x * pixelSpacingColumn ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) * ( the_index.y ) = the_index.y * ( imageOrientationColumn.y * pixelSpacingColumn - ( imageOrientationColumn.x * pixelSpacingColumn ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) + Lambda in zweite Gleichung einsetzen : + the_index.y = ( firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( imagePositionPatient.x + x_offset_column * column_index - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - imagePositionPatient.y + y_offset_column * column_index ) / ( imageOrientationColumn.y * pixelSpacingColumn - ( imageOrientationColumn.x * pixelSpacingColumn ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) + + */ + + /* + + Nach Koordinatentransformation nochmal ueberlegt: + + + Erste Geleuchung : x = firstPoint.x + ( secondPoint.x - firstPoint.x ) * lambda = imagePositionPatient.x + x_offset_column * ( column_index - 0.5 ) + imageOrientationColumn.x * pixelSpacingColumn * ( the_index.y - 0.5 ) + Zweite Geleuchung : y = firstPoint.y + ( secondPoint.y - firstPoint.y ) * lambda = imagePositionPatient.y + y_offset_column * ( column_index - 0.5 ) + imageOrientationColumn.y * pixelSpacingColumn * ( the_index.y - 0.5 ) + + Erste Gleuchung aufloesen nach lambda : + Erste Geleuchung : lambda = ( imagePositionPatient.x + x_offset_column * ( column_index - 0.5 ) + imageOrientationColumn.x * pixelSpacingColumn * ( the_index.y - 0.5 ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) + Lambda in zweite Gleichung einsetzen : + firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( imagePositionPatient.x + x_offset_column * ( column_index - 0.5 ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - imagePositionPatient.y + y_offset_column * ( column_index - 0.5 ) = imageOrientationColumn.y * pixelSpacingColumn * ( the_index.y - 0.5 ) - ( imageOrientationColumn.x * pixelSpacingColumn ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) * ( the_index.y - 0.5 ) = ( the_index.y - 0.5 ) * ( imageOrientationColumn.y * pixelSpacingColumn - ( imageOrientationColumn.x * pixelSpacingColumn ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) + Lambda in zweite Gleichung einsetzen : + ( the_index.y - 0.5 ) = ( firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( imagePositionPatient.x + x_offset_column * ( column_index - 0.5 ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - imagePositionPatient.y + y_offset_column * ( column_index - 0.5 ) ) / ( imageOrientationColumn.y * pixelSpacingColumn - ( imageOrientationColumn.x * pixelSpacingColumn ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) + + + + */ + + + double x_offset_column = GInf.at(resolution_level).getImageOrientationRow()(0) * GInf.at(resolution_level).getPixelSpacingRow(); + double y_offset_column = GInf.at(resolution_level).getImageOrientationRow()(1) * GInf.at(resolution_level).getPixelSpacingRow(); + + LegacyWorldCoordinate2D index_first_point; + index_first_point.x = 0; + index_first_point.y = 0; + LegacyWorldCoordinate2D index_second_point; + index_second_point.x = 0; + index_second_point.y = 0; + + // indicees der beiden uebergebenen Punkte bestimmen + index_first_point = GetDoubleIndex2D( firstPoint.x , firstPoint.y, resolution_level ); + index_second_point = GetDoubleIndex2D( secondPoint.x , secondPoint.y, resolution_level ); + + double index_diff = ( index_second_point.x - index_first_point.x ); + + int begin_ind = 0; + int end_ind = 0; + bool do_swap = 0; + + if( index_diff > 0 ){ + + begin_ind = static_cast( floor( index_first_point.x ) ); + end_ind = static_cast( floor( index_second_point.x ) + 1 ); + + } + else if( index_diff < 0 ){ + + do_swap = 1; + begin_ind = static_cast( floor( index_second_point.x ) ); + end_ind = static_cast( floor( index_first_point.x ) + 1 ); + + } + else{ + + begin_ind = 0; + end_ind = 0; + } + + if( begin_ind < 0 ) begin_ind = 0; + if( end_ind > ( GInf.at(resolution_level).getNumColumns() - 1 ) ) end_ind = GInf.at(resolution_level).getNumColumns() - 1; + + LegacyWorldCoordinate2D coord2D; + coord2D.x = 0; + coord2D.y = 0; + LegacyWorldCoordinate3D the_index; + the_index.x = 0; + the_index.y = 0; + the_index.z = 0; + + + double multi_res_offset = GetWorldCoordinateOffsetThisResolutionLevel( resolution_level ); + + for( int column_index = begin_ind ; column_index <= end_ind ; column_index++ ){ + + //ganz alt + //the_index.y = ( firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( GInf.at(resolution_level).getImagePositionPatient()(0) + x_offset_column * column_index - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - GInf.at(resolution_level).getImagePositionPatient()(1) + y_offset_column * column_index ) / ( GInf.at(resolution_level).getImageOrientationColumn()(1) * GInf.at(resolution_level).getPixelSpacingColumn() - ( GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn() ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) ; + //alt + //the_index.y = 0.5 + ( firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( GInf.at(resolution_level).getImagePositionPatient()(0) + x_offset_column * ( column_index - 0.5 ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - GInf.at(resolution_level).getImagePositionPatient()(1) + y_offset_column * ( column_index - 0.5 ) ) / ( GInf.at(resolution_level).getImageOrientationColumn()(1) * GInf.at(resolution_level).getPixelSpacingColumn() - ( GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn() ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) ; + + //neu multi res + the_index.y = multi_res_offset + ( firstPoint.y + ( secondPoint.y - firstPoint.y ) * ( GInf.at(resolution_level).getImagePositionPatient()(0) + x_offset_column * ( column_index - multi_res_offset ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ) - GInf.at(resolution_level).getImagePositionPatient()(1) + y_offset_column * ( column_index - multi_res_offset ) ) / ( GInf.at(resolution_level).getImageOrientationColumn()(1) * GInf.at(resolution_level).getPixelSpacingColumn() - ( GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn() ) * ( secondPoint.y - firstPoint.y ) / ( secondPoint.x - firstPoint.x ) ) ; + + + coord2D.y = the_index.y; + coord2D.x = column_index; + + //alt + //double lambda = ( GInf.at(resolution_level).getImagePositionPatient()(0) + x_offset_column * ( column_index - 0.5 ) + GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn() * ( the_index.y - 0.5 ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ); + + //neu multi resolution + double lambda = ( GInf.at(resolution_level).getImagePositionPatient()(0) + x_offset_column * ( column_index - multi_res_offset ) + GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn() * ( the_index.y - multi_res_offset ) - firstPoint.x ) / ( secondPoint.x - firstPoint.x ); + + if( ( lambda >= 0 ) && ( lambda <= 1 ) ){ + + column_intersections_ref.push_back( coord2D ); + + } + + + + } + if( do_swap ){ + + std::vector column_intersections_to_be_swaped; + + for( int this_one = ( column_intersections_ref.size() - 1 ) ; this_one >= 0 ; this_one-- ){ + + column_intersections_to_be_swaped.push_back( column_intersections_ref.at( this_one ) ); + + } + + column_intersections_ref.swap( column_intersections_to_be_swaped ); + + + } + + + } + else{ + GoGetColumnIntersectionsAlongThisRaw( firstPoint , secondPoint , column_intersections_ref, resolution_level ); + } + + + } + + + } + + + bool Mask::TestGoIntersectColumnByComparisonWithExpectedResult( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& column_intersections_ref, std::vector& expected_intersections_ref ){ + + column_intersections_ref.clear(); + + int resolution_level = 0; + GoIntersectColumn( firstPoint , secondPoint , column_intersections_ref, resolution_level ); + + return IdenticalIntersectionsForTest( column_intersections_ref , expected_intersections_ref ); + + } + + + bool Mask::TestGoIntersectRawByComparisonWithExpectedResult( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& raw_intersections_ref, std::vector& expected_intersections_ref ){ + + raw_intersections_ref.clear(); + + int resolution_level = 0; + GoIntersectRaw( firstPoint , secondPoint , raw_intersections_ref, resolution_level ); + + return IdenticalIntersectionsForTest( raw_intersections_ref , expected_intersections_ref ); + + } + + + + + bool Mask::IdenticalIntersectionsForTest( std::vector& calculated_intersections_ref , std::vector& expected_intersections_ref ){ + + if( calculated_intersections_ref.size() != expected_intersections_ref.size() ){ + std::cout<< " Size Problem ! " < 0.001 ) || ( diffb > 0.001 ) ) { + + std::cout<< " Unexpectd coordinate ! " <& column_intersections_ref, int resolution_level ){ + + LegacyWorldCoordinate2D index_first_point; + index_first_point.x = 0; + index_first_point.y = 0; + LegacyWorldCoordinate2D index_second_point; + index_second_point.x = 0; + index_second_point.y = 0; + + if( firstPoint.y != secondPoint.y ) assert(0); + + // indicees der beiden uebergebenen Punkte bestimmen + index_first_point = GetDoubleIndex2D( firstPoint.x , firstPoint.y, resolution_level ); + index_second_point = GetDoubleIndex2D( secondPoint.x , secondPoint.y, resolution_level ); + + double index_diff = ( index_second_point.x - index_first_point.x ); + + int begin_ind = 0; + int end_ind = 0; + bool do_swap = 0; + + if( index_diff > 0 ){ + + begin_ind = static_cast( floor( index_first_point.x ) ); + if( floor( index_first_point.x ) < index_first_point.x ) begin_ind += 1; + end_ind = static_cast( floor( index_second_point.x ) ); + } + else if( index_diff < 0 ){ + + do_swap = 1; + begin_ind = static_cast( floor( index_second_point.x ) ); + if( floor( index_second_point.x ) < index_second_point.x ) begin_ind += 1; + end_ind = static_cast( floor( index_first_point.x ) ); + + } + else{ + + begin_ind = 0; + end_ind = 0; + + } + + if( begin_ind < 0 ) begin_ind = 0; + if( end_ind > ( GInf.at(resolution_level).getNumRows() - 1 ) ) end_ind = GInf.at(resolution_level).getNumRows() - 1; + + LegacyWorldCoordinate2D coord2D; + coord2D.x = 0; + coord2D.y = 0; + + for( int column_index = begin_ind ; column_index <= end_ind ; column_index++ ){ + + coord2D.x = column_index; + coord2D.y = index_first_point.y; + column_intersections_ref.push_back( coord2D ); + + } + if( do_swap ){ + + std::vector column_intersections_to_be_swaped; + + for( int this_one = ( column_intersections_ref.size() - 1 ) ; this_one >= 0 ; this_one-- ){ + + column_intersections_to_be_swaped.push_back( column_intersections_ref.at( this_one ) ); + + } + + column_intersections_ref.swap( column_intersections_to_be_swaped ); + + } + + + } + + + + void Mask::ShowIntersections(){ + + LegacyWorldCoordinate2D coord2D; + coord2D.x = 0; + coord2D.y = 0; + + for( unsigned int resolution_level = 0 ; resolution_level < Intersections.size() ; resolution_level++ ){ + + std::cout<< " resolution_level : " << resolution_level < wich_slice; + + for(unsigned int struct_index = 0 ; struct_index < Intersections.at(resolution_level).size() ; struct_index++ ){ + + correspoinding_structure_vector str_v; + wich_slice.clear(); + + // die entsprechenden slices des dose cubes finden, die zu dieser Struktur gehoeren und die Voxel des MaskField belegen + for(unsigned int index_z = 0 ; index_z < GInf.at(resolution_level).getNumSlices() ; index_z++ ){ + + str_v = SliceStructureCorrespondency.at( index_z ); + for(unsigned int count = 0 ; count < str_v.size() ; count++ ){ + if( str_v.at( count ) == struct_index )wich_slice.push_back( index_z ); + + } + + } + + // iteration polygon + for(unsigned int point_index = 0 ; point_index < ( Intersections.at(resolution_level).at( struct_index ).size() ); point_index++ ){ + + + for(unsigned int intersect_index = 0 ; intersect_index < Intersections.at(resolution_level).at(struct_index).at( point_index ).intersections_raw_and_column.size() ; intersect_index++ ){ + + coord2D = Intersections.at(resolution_level).at(struct_index).at( point_index ).intersections_raw_and_column.at( intersect_index ); + + + if( ( coord2D.x >= 0 ) && ( coord2D.y >= 0 ) && ( coord2D.x < MaskField.at( resolution_level )->GetDimX() ) && ( coord2D.y < MaskField.at( resolution_level )->GetDimY() ) ){ + + unsigned int index_x = static_cast( floor( coord2D.x ) ); + unsigned int index_y = static_cast( floor( coord2D.y ) ); + + for(unsigned int this_counter = 0 ; this_counter < wich_slice.size() ; this_counter++ ){ + + unsigned int index_z = wich_slice.at( this_counter ); + + MaskField.at( resolution_level )->PutData( index_x , index_y , index_z , brightness_border ); + + if( ( floor( coord2D.x ) == coord2D.x ) && ( index_x > 0 ) ){ + + MaskField.at( resolution_level )->PutData( ( index_x - 1 ) , index_y , index_z , brightness_border ); + + } + if( ( floor( coord2D.y ) == coord2D.y ) && ( index_y > 0 ) ){ + + MaskField.at( resolution_level )->PutData( index_x , ( index_y - 1 ) , index_z , brightness_border ); + + } + if( ( floor( coord2D.x ) == coord2D.x ) && ( floor( coord2D.y ) == coord2D.y ) && ( index_y > 0 ) && ( index_x > 0 ) ){ + + MaskField.at( resolution_level )->PutData( ( index_x - 1 ) , ( index_y - 1 ) , index_z , brightness_border ); + + } + + + } + + + } + + } + + for(unsigned int this_counter = 0 ; this_counter < wich_slice.size() ; this_counter++ ){ + + unsigned int index_z = wich_slice.at( this_counter ); + + // Das Siemens Planunssystem schreibt aus ungeklaertem Gruend Daten raus, deren Voxelkoordinaten ausserhalb des Bildes zu liegen kommen. Eigentlich macht das keinen Sinn. Wir wissen, dass unsere Toolbox Welt-Voxel-Koordinaten Umrechnung DICOM conform ist. + int intx = floor( Intersections.at(resolution_level).at(struct_index).at( point_index ).contour_point_voxel_coord.x ); + int inty = floor( Intersections.at(resolution_level).at(struct_index).at( point_index ).contour_point_voxel_coord.y ); + + + if( ( intx >= 0 ) && ( inty >= 0 ) && ( intx < MaskField.at( resolution_level )->GetDimX() ) && ( inty < MaskField.at( resolution_level )->GetDimY() ) ){ + + Uint16 uintx = static_cast(intx); + Uint16 uinty = static_cast(inty); + + MaskField.at( resolution_level )->PutData( uintx , uinty , index_z , brightness_border ); + + } + + } + + } // point index + + } + + + } + + + + + + // like MarkVoxelsTouchedByStructure + void Mask::GetIntersectionInformationThisSlice( unsigned int index_z ){ + + // In allen Slices die dem aktuellen Polygon zugeordnet sind muessen die zu den + // Schnittpunkten der Contour gehoerigen Voxel gesetzt werden. + // (siehe SliceStructureCorrespondency bzw. correspoinding_structure_vector unter correspoinding_structure ) + // Es muessen alle eintraege von SliceStructureCorrespondency durchsucht werden um festzustellen, + // ob es sich um ein slice des dose cubes handelt, das diesem Strukturelement zugeordnet ist. + // Dabei muss beachtet werden, dass eine Structurelement fuer mehrere verschiedene Slices + // das zugehoerige sein kann. + + LegacyWorldCoordinate2D coord2D; + coord2D.x = 0; + coord2D.y = 0; + + correspoinding_structure_vector str_v; + str_v = SliceStructureCorrespondency.at( index_z ); + + int resolution_level = 0; + + for(unsigned int count = 0 ; count < str_v.size() ; count++ ){ + + unsigned int struct_index = str_v.at( count ); + + // iteration polygon + for(unsigned int point_index = 0 ; point_index < ( Intersections.at(resolution_level).at( struct_index ).size() ); point_index++ ){ + + + for(unsigned int intersect_index = 0 ; intersect_index < Intersections.at(resolution_level).at(struct_index).at( point_index ).intersections_raw_and_column.size() ; intersect_index++ ){ + + coord2D = Intersections.at(resolution_level).at(struct_index).at( point_index ).intersections_raw_and_column.at( intersect_index ); + + + if( ( coord2D.x >= 0 ) && ( coord2D.y >= 0 ) && ( coord2D.x < MaskField.at( resolution_level )->GetDimX() ) && ( coord2D.y < MaskField.at( resolution_level )->GetDimY() ) ){ + + + unsigned int index_x = static_cast( floor( coord2D.x ) ); + unsigned int index_y = static_cast( floor( coord2D.y ) ); + + + //MaskField.at( resolution_level )->PutData( index_x , index_y , index_z , brightness_border ); + + if( FieldOfIntersections ){ + + if( floor( coord2D.y ) == coord2D.y ){ + unsigned int voxel_side = 0; + AddToFieldOfIntersections( index_x , index_y , 0 , struct_index , point_index , intersect_index , coord2D, voxel_side ); + } + else if( floor( coord2D.x ) == coord2D.x ){ + unsigned int voxel_side = 3; + AddToFieldOfIntersections( index_x , index_y , 0 , struct_index , point_index , intersect_index , coord2D, voxel_side ); + } + + } + + if( ( floor( coord2D.x ) == coord2D.x ) && ( index_x > 0 ) ){ + + //MaskField.at( resolution_level )->PutData( ( index_x - 1 ) , index_y , index_z , brightness_border ); + + if( FieldOfIntersections ){ // Ecke wird gegebenenfalls mit angehaengt + unsigned int voxel_side = 1; + + + AddToFieldOfIntersections( ( index_x - 1 ) , index_y , 0 , struct_index , point_index , intersect_index , coord2D, voxel_side ); + } + + } + if( ( floor( coord2D.y ) == coord2D.y ) && ( index_y > 0 ) ){ + + //MaskField.at( resolution_level )->PutData( index_x , ( index_y - 1 ) , index_z , brightness_border ); + + if( FieldOfIntersections ){ + + if( floor( coord2D.x ) == coord2D.x ){ // Ecke wird gegebenenfalls nicht mit angehaengt, deshalb Fallunterscheidunn noetig, damit sie angehaengt wird ( Ecke wird nur an die Kante angehaengt an deren Anfang sie steht) + unsigned int voxel_side = 3; + AddToFieldOfIntersections( index_x , ( index_y - 1 ) , 0 , struct_index , point_index , intersect_index , coord2D, voxel_side ); + } + else{ + unsigned int voxel_side = 2; + AddToFieldOfIntersections( index_x , ( index_y - 1 ) , 0 , struct_index , point_index , intersect_index , coord2D, voxel_side ); + } + + } + + } + if( ( floor( coord2D.x ) == coord2D.x ) && ( floor( coord2D.y ) == coord2D.y ) && ( index_y > 0 ) && ( index_x > 0 ) ){ + + + //MaskField.at( resolution_level )->PutData( ( index_x - 1 ) , ( index_y - 1 ) , index_z , brightness_border ); + + if( FieldOfIntersections ){ + unsigned int voxel_side = 2; + + AddToFieldOfIntersections( ( index_x - 1 ) , ( index_y - 1 ) , 0 , struct_index , point_index , intersect_index , coord2D, voxel_side ); + } + + } + + + } + + } + + + + } + + } + + } + + + + + void Mask::AddToFieldOfIntersections( unsigned int index_x , unsigned int index_y , unsigned int index_z , unsigned int struct_index , unsigned int point_index , unsigned int intersect_index , LegacyWorldCoordinate2D coord2D, unsigned int voxel_side ){ + + int which_one = GetRespectiveIntersections( index_x , index_y , 0 , struct_index ); + + IterativePolygonInTermsOfIntersections::WhichIntersection which_intersection; + which_intersection.point_index = point_index; + which_intersection.intersection_index = intersect_index; + which_intersection.column_raw_or_unified = 2; + + bool go = IsItToBeRegarded( struct_index , point_index , intersect_index, index_x , index_y ); + + if( go ){ + + + if( which_one != (-1) ){ + + SetThisIntersectionToCornerOrEdge( index_x , index_y , which_intersection , coord2D , voxel_side, FieldOfIntersections->GetData( index_x , index_y, index_z )->at(which_one) ); + + } + else{ + + ExtendFieldOfIntersections( index_x , index_y , 0 , struct_index ); + + int which_one = GetRespectiveIntersections( index_x , index_y , 0 , struct_index ); + + if( which_one == (-1) )assert( 0 ); + + + + SetThisIntersectionToCornerOrEdge( index_x , index_y , which_intersection , coord2D , voxel_side, FieldOfIntersections->GetData( index_x , index_y, index_z )->at(which_one) ); + + + + } + + } + + + } + + + + bool Mask::IsItToBeRegarded( unsigned int struct_index , unsigned int point_index , unsigned int intersect_index , int index_x , int index_y ){ + // Muss betrachtet werden, falls der Konturpunkt sich in x oder durch y vom Schnittpunkt der zusaetzlich durch den intersect_index gegeben ist unterscheidet. + // Falls Schnittpunkt mit der Kante, der zusaetzlich durch intersect_index gegeben ist und der Konturpunkt nicht in x oder y unterscheiden, + // wird der vorherige Konturpunkt betrachtet. Und zwar der erste, der sich von dem intialen tatsaechlich unterscheidet. + // Wenn dieser Punkt in einer Richtung z.B. x auf der selben Kante liegt, sich aber in y Richtung unterscheidet, oder andersherum, + // dann muss der Schnittpunkt betrachtet werden, obwohl er mit einem Konturpunkt zusammenfaellt, weil der vorhergehende Konturpunkt auf der gleichen Kante liegt (eventuell in anderem Voxel, aber auf der selben Kante) + + + // Es ist nicht noetig die Selbe Stelle der Kante zweimal zu betrachten. Diese Stelle hier wird vom vorhergehenden Konturpunkt ausgehend als Schnittpunkt erkannt werden und wird deshalb hier nicht beruecksichtigt + if( ( Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.y != Intersections.at(0).at(struct_index).at( point_index ).intersections_raw_and_column.at( intersect_index ).y ) || + ( Intersections.at(0).at(struct_index).at( point_index ).intersections_raw_and_column.at( intersect_index ).x != Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.x ) )return true; + else{ + + + unsigned int struct_index_other = struct_index; + unsigned int point_index_other = point_index; + unsigned int intersect_index_other = intersect_index; + + if( GetFirstDifferent( struct_index_other , point_index_other , Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord ) ){ + + + + if( ( floor(Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.y / 1.0 ) != Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.y ) && ( floor(Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.x / 1.0 ) == Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.x ) ){ + + if( Intersections.at(0).at(struct_index_other).at( point_index_other ).contour_point_voxel_coord.x == Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.x ){ + return true; + } + + } + else if( ( floor( Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.y / 1 ) == Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.y ) && ( floor( Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.x / 1 ) != Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.x ) ){ + + if( Intersections.at(0).at(struct_index_other).at( point_index_other ).contour_point_voxel_coord.y == Intersections.at(0).at(struct_index).at( point_index ).contour_point_voxel_coord.y ){ + return true; + } + + } + + + } + else assert( 0 ); + + + } + + return false; + + + } + + + bool Mask::GetFirstDifferent( unsigned int& struct_index , unsigned int& point_index , LegacyWorldCoordinate3D compare_point ){ + + if( Intersections.at(0).at(struct_index).size() <= 1 ){ + std::cerr<< " Doesn't make sense to voxelize contour with less than two points ! " < 0 ) point_index--; + else point_index = ( Intersections.at(0).at(struct_index).size() - 1 ); + counter++; + if( counter > ( Intersections.at(0).at(struct_index).size() * 2 ) ) break; + + } + + return false; + + } + + + void Mask::ExtendFieldOfIntersections( unsigned int index_x , unsigned int index_y , unsigned int index_z , unsigned int struct_index ){ + + LegacyPolygonSequenceType strVector = _structure->getLegacyStructureVector(); + + LegacyPolygonType& the_polygon_ref = strVector.at(struct_index); + + PolygonInTermsOfIntersections& Polygon_Intersections_Input_Ref = Intersections.at(0).at( struct_index ); + + LegacyDoseVoxelIndex3D aDoseIndex; + aDoseIndex.x = index_x; + aDoseIndex.y = index_y; + aDoseIndex.z = index_z; + + //PolygonInfo polInf = PolygonInfo( the_polygon_ref , struct_index , brightness_outside , brightness_inside , MaskField.at( resolution_level ) , Polygon_Intersections_Input_Ref , aDoseIndex , GInf.at(resolution_level) ); + + VoxelIntersectionsVectorPointer pol_inf_pointer = new std::vector(); + FieldOfIntersections->PutData( index_x, index_y, index_z , pol_inf_pointer ); + + IntersectionsThisVoxel intersection_info; + intersection_info.SetPolygonNumber( struct_index ); + + FieldOfIntersections->GetData( index_x , index_y, index_z )->push_back( intersection_info ); + + } + + + + int Mask::GetRespectiveIntersections( unsigned int index_x , unsigned int index_y , unsigned int index_z , unsigned int struct_index ) const{ + + + if( FieldOfIntersections->GetData( index_x , index_y, index_z )!= NULL ){ + + for( int i = 0 ; i < FieldOfIntersections->GetData( index_x , index_y, index_z )->size() ; i++ ){ + if( FieldOfIntersections->GetData( index_x , index_y, index_z )->at(i).GetPolygonNumber() == struct_index )return i; + } + + return (-1); + + } + else return (-1); + + } + + + LegacyWorldCoordinate2D Mask::GetDoubleIndex2D( double x , double y, int resolution_level ){ + + LegacyWorldCoordinate2D result; + + // zunaechst + // x_world = imageOrientationRow.x*pixelSpacingRow*the_index.x+imageOrientationColumn.x*pixelSpacingColumn*the_index.y+imagePosition.x; + // y_world = imageOrientationRow.y*pixelSpacingRow*the_index.x+imageOrientationColumn.y*pixelSpacingColumn*the_index.y+imagePosition.y; + // mit x_world y_world ( Welt Koordinaten alles andere dcmtkrt Ausdruecke ) + + // daraus folgt + // Gleichung a : ( ( x_world - imagePosition.x ) - ( imageOrientationColumn.x*pixelSpacingColumn*the_index.y ) ) / ( imageOrientationRow.x*pixelSpacingRow ) = the_index.x ; + // Gleichung b : y_world = imageOrientationRow.y*pixelSpacingRow* ( ( ( x_world - imagePosition.x ) - ( imageOrientationColumn.x*pixelSpacingColumn*the_index.y ) ) / ( imageOrientationRow.x*pixelSpacingRow ) ) +imageOrientationColumn.y*pixelSpacingColumn*the_index.y+imagePosition.y; + + // Gleichung b weiter aufgeloest nach the_index.y + // y_world - imagePosition.y - imageOrientationRow.y*pixelSpacingRow*( x_world - imagePosition.x ) / ( imageOrientationRow.x*pixelSpacingRow ) = - imageOrientationRow.y*pixelSpacingRow*( imageOrientationColumn.x*pixelSpacingColumn*the_index.y )/ ( imageOrientationRow.x*pixelSpacingRow ) +imageOrientationColumn.y*pixelSpacingColumn*the_index.y + // Gelichung b weiter umgeformt : + // y_world - imagePosition.y - imageOrientationRow.y*pixelSpacingRow*( x_world - imagePosition.x ) / ( imageOrientationRow.x*pixelSpacingRow ) = the_index.y * ( imageOrientationColumn.y*pixelSpacingColumn - imageOrientationRow.y*pixelSpacingRow*( imageOrientationColumn.x*pixelSpacingColumn )/ ( imageOrientationRow.x*pixelSpacingRow ) ) + // es ergibt sich + + // aus Gleichung b : the_index.y = a / b + // wobei a = y_world - imagePosition.y - imageOrientationRow.y*pixelSpacingRow*( x_world - imagePosition.x ) / ( imageOrientationRow.x*pixelSpacingRow ) + // und b = ( imageOrientationColumn.y*pixelSpacingColumn - imageOrientationRow.y*pixelSpacingRow*( imageOrientationColumn.x*pixelSpacingColumn )/ ( imageOrientationRow.x*pixelSpacingRow ) ) + + // Folglich erbibt sich aus Gleichung a : + // the_index.x = ( ( x_world - imagePosition.x ) - ( imageOrientationColumn.x*pixelSpacingColumn* ( a / b ) ) ) / ( imageOrientationRow.x*pixelSpacingRow ) + // Uebersichtlicher : + // the_index.x = ( c - ( d * ( a / b ) ) ) / e + // c = ( x_world - imagePosition.x ) + // d = imageOrientationColumn.x*pixelSpacingColumn + // e = ( imageOrientationRow.x*pixelSpacingRow ) + + // in anderen Worten : + + double a = y - GInf.at(resolution_level).getImagePositionPatient()(1) - GInf.at(resolution_level).getImageOrientationRow()(1) * GInf.at(resolution_level).getPixelSpacingRow() * ( x - GInf.at(resolution_level).getImagePositionPatient()(0) ) / ( GInf.at(resolution_level).getImageOrientationRow()(0) * GInf.at(resolution_level).getPixelSpacingRow() ) ; + double b = GInf.at(resolution_level).getImageOrientationColumn()(1) * GInf.at(resolution_level).getPixelSpacingColumn() - GInf.at(resolution_level).getImageOrientationRow()(1) * GInf.at(resolution_level).getPixelSpacingRow() * ( GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn() ) / ( GInf.at(resolution_level).getImageOrientationRow()(0) * GInf.at(resolution_level).getPixelSpacingRow() ) ; + double c = ( x - GInf.at(resolution_level).getImagePositionPatient()(0) ) ; + double d = GInf.at(resolution_level).getImageOrientationColumn()(0) * GInf.at(resolution_level).getPixelSpacingColumn(); + double e = ( GInf.at(resolution_level).getImageOrientationRow()(0) * GInf.at(resolution_level).getPixelSpacingRow() ); + + //alt + // folglich ergeben sich die voxel Koordinaten zu + //result.x = ( ( c - ( d * ( a / b ) ) ) / e ) ; + //result.y = ( a / b ) ; + + // neu + // folglich ergeben sich die voxel Koordinaten zu + + // Hier muss das resolution level beruecksichtigt werden. Aenderung vornehmen. Auch testen ! + + // result.x = ( ( c - ( d * ( a / b ) ) ) / e ) + 0.5; + // result.y = ( a / b ) + 0.5; + + result.x = ( ( c - ( d * ( a / b ) ) ) / e ) + GetWorldCoordinateOffsetThisResolutionLevel( resolution_level ); + result.y = ( a / b ) + GetWorldCoordinateOffsetThisResolutionLevel( resolution_level ); + + return result; + + } + + + LegacyWorldCoordinate2D Mask::GetDoubleIndex2D( double x , double y ){ + + return GetDoubleIndex2D( x , y, 0 ); + + } + + + double Mask::GetWorldCoordinateOffsetThisResolutionLevel( int resolution_level ){ + + double world_coord_offset = 1; + + for( int i = 1 ; i <= resolution_level ; i++ )world_coord_offset *= 2; + + world_coord_offset = ( 0.5 / world_coord_offset ); + + return world_coord_offset; + + } + + + double Mask::GetBlockLengthThisResolutionLevel( int resolution_level ){ + + double length = 1; + + for( int i = 1 ; i <= resolution_level ; i++ )length *= 2; + + return length; + + } + + + LegacyWorldCoordinate1D Mask::GetZIndex( LegacyWorldCoordinate1D z ){ + // alt + // return ( ( z - GInf.at(0).getImagePositionPatient()(2) ) / GInf.at(0).getSliceThickness() ); + //neu + LegacyWorldCoordinate1D result = ( ( z - GInf.at(0).getImagePositionPatient()(2) ) / GInf.at(0).getSliceThickness() ) + 0.5; + return result; + } + + bool Mask::GetSomeBorderVoxelXYZForTest( LegacyDoseVoxelIndex3D& aDoseIndex ){ + + correspoinding_structure_vector str_v; + + bool got_it = false; + + while( ( got_it == false ) && ( aDoseIndex.z < ( SliceStructureCorrespondency.size() ) ) ){ + + str_v = SliceStructureCorrespondency.at( aDoseIndex.z ); + + if( ( str_v.size() > 0 ) && ( GetSomeBorderVoxelXYForTest( aDoseIndex ) ) ){ + + got_it = true; + } + else aDoseIndex.z++; + + } + + return got_it; + + } + + + bool Mask::GetSomeBorderVoxelXYForTest( LegacyDoseVoxelIndex3D& aDoseIndex ){ + + bool voxel_found = false; + + unsigned int z = aDoseIndex.z; + int resolution_level = 0; + + for( unsigned int x = 0 ; x < MaskField.at( resolution_level )->GetDimX() ; x++ ){ + for( unsigned int y = 0 ; y < MaskField.at( resolution_level )->GetDimY() ; y++ ){ + + + + if( MaskField.at( resolution_level )->GetData( x, y, z ) == brightness_border ){ + voxel_found = true; + aDoseIndex.x = x; + aDoseIndex.y = y; + x = MaskField.at( resolution_level )->GetDimX(); + y = MaskField.at( resolution_level )->GetDimY(); + } + } + } + + return voxel_found ; + + } + + + bool Mask::DoubleCheckIsOnBorderForTest( LegacyDoseVoxelIndex3D& aDoseIndex ){ + int resolution_level = 0; + if( MaskField.at( resolution_level )->GetData( aDoseIndex.x, aDoseIndex.y, aDoseIndex.z ) == brightness_border ) return true; + else return false; + } + + + bool Mask::CheckFractionThisVoxelCorrectForTest( LegacyDoseVoxelIndex3D another_dose_index , float the_correct_result ){ + if( GetFractionInside( another_dose_index ) == the_correct_result ) return true; + else return false; + } + + + float Mask::GetFractionThisVoxelForTest( LegacyDoseVoxelIndex3D another_dose_index ){ + return GetFractionInside( another_dose_index ); + } + + + PolygonInfo* Mask::JustCreateAPolygonInfoForTest( LegacyDoseVoxelIndex3D aDoseIndex ){ + + PolygonInfo* polInfTest = NULL; + + if( do_detailed_subvox ){ + + correspoinding_structure_vector str_v; + bool got_it = false; + + while( ( got_it == false ) && ( aDoseIndex.z < ( SliceStructureCorrespondency.size() - 1 ) ) ){ + str_v = SliceStructureCorrespondency.at( aDoseIndex.z ); + if( str_v.size() > 0 ) got_it = true; + else aDoseIndex.z++; + } + + // Nur der erste Polygonzug des entsprechenden slices wird betrachtet, daher keine Schleife ueber alle Strukturen dieses Slices noetig. + // Die Teststruktur die hier verwendetw wird besteht in diesem Slice nur aus einem Polygonzug. + // for( unsigned int count = 0 ; count < str_v.size() ; count++ ){ + + if( got_it ){ + + int struct_index = str_v.at( 0 ); + + LegacyPolygonSequenceType pol_seq; + + PolygonInTermsOfIntersections& Polygon_Intersections_Input_Ref = Intersections.at(0).at( struct_index ); + + LegacyPolygonSequenceType strVector = _structure->getLegacyStructureVector(); + LegacyPolygonType& the_polygon_ref = strVector.at(struct_index); + + if( FieldOfIntersections ){ + FieldOfIntersections->SetZero(); + GetIntersectionInformationThisSlice( aDoseIndex.z ); + } + int which_one = GetRespectiveIntersections( aDoseIndex.x , aDoseIndex.y , 0 , struct_index ); + + if( which_one != (-1) ){ + + IntersectionsThisVoxel intersections_this_voxel = FieldOfIntersections->GetData( aDoseIndex.x , aDoseIndex.y , 0 )->at( which_one ); + polInfTest = new PolygonInfo( the_polygon_ref, struct_index , brightness_outside , brightness_inside ,MaskField.at( 0 ) , Polygon_Intersections_Input_Ref , aDoseIndex , GInf.at( 0 ) , intersections_this_voxel ); + + } + + } + else return NULL; + + } + + return polInfTest; + + } + + + + + bool Mask::GetIntersectionsOfSliceForTest( unsigned int position , PolygonInTermsOfIntersections& Polygon_Intersections_Ref ){ + + if( position < Intersections.at(0).size() ){ + + Polygon_Intersections_Ref = Intersections.at(0).at( position ); + return true; + + } + else return false; + + } + + + bool Mask::ReturnFirstWithIntersectionsForTest( unsigned int& position ){ + + position = 0; + + while( position < Intersections.at(0).size() ){ + + if( Intersections.at(0).at( position ).size() > 0 )return true; + else position++; + + } + + position = 0; + return false; + + } + + + float Mask::GetFractionInside( const LegacyDoseVoxelIndex3D& aDoseIndex ) { + + double fraction_total = 0; + + correspoinding_structure_vector str_v; + str_v = SliceStructureCorrespondency.at( aDoseIndex.z ); + + // Hier werden alle Polygone untersucht, die in diesem Slice liegen. + for( unsigned int count = 0 ; count < str_v.size() ; count++ ){ + + int struct_index = str_v.at( count ); + + int which_one = GetRespectiveIntersections( aDoseIndex.x , aDoseIndex.y , 0 , struct_index ); + + //assert( which_one != (-1) ); // nein nicht assert, denn nicht jede Struktur die zu diesem Slice gehoert schneidet zwangslauefig dieses Voxel + + if( which_one != (-1) ){ + + LegacyPolygonSequenceType pol_seq; + + assert( struct_index < Intersections.at(0).size() ); + + const PolygonInTermsOfIntersections& Polygon_Intersections_Input_Ref = Intersections.at(0).at( struct_index ); + + LegacyPolygonSequenceType strVector = _structure->getLegacyStructureVector(); + + LegacyPolygonType& the_polygon_ref = strVector.at(struct_index); + + const IntersectionsThisVoxel& intersections_this_voxel = FieldOfIntersections->GetData( aDoseIndex.x , aDoseIndex.y , 0 )->at(which_one) ; + + + PolygonInfo polInf = PolygonInfo( the_polygon_ref , struct_index , brightness_outside , brightness_inside , MaskField.at( 0 ) , Polygon_Intersections_Input_Ref , aDoseIndex , GInf.at(0) , intersections_this_voxel ); + + if( polInf.CreatePolygonSequence() ){ + + pol_seq = polInf.GetResultingSequence(); + + double fraction_this_polygon = 0; + + int numberOfContours = pol_seq.size(); + + for(int i=0; i= 0) ) std::cout<< " fraction_this_polygon : " << fraction_this_polygon <= 0 ); // that's essential since ResultingSequence is represented in Voxel coordinates + //assert( fraction_this_polygon <= 1 ); // that's essential since ResultingSequence is represented in Voxel coordinates + + //double voxel_volume = GInf.at(resolution_level).getPixelSpacingRow() * GInf.at(resolution_level).getPixelSpacingColumn() * GInf.at(resolution_level).getSliceThickness(); + + //if( voxel_volume == 0 )assert(0); // this must never happen. Since depends on dataset : exception to be implemented ! + + //fraction_this_polygon /= voxel_volume; + + } + + + fraction_total += fraction_this_polygon; + + } + + } + + + } // Iteration ueber Polygone + + return fraction_total; + + } + + + }//namespace + }//namespace +}//namespace + + + diff --git a/code/masks/rttbMask.h b/code/masks/rttbMask.h new file mode 100644 index 0000000..e37ddd4 --- /dev/null +++ b/code/masks/rttbMask.h @@ -0,0 +1,757 @@ +// ----------------------------------------------------------------------- +// 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 __RT_MASK_H +#define __RT_MASK_H + +#include "rttbBaseType_LEGACY.h" +#include "rttbBaseType.h" +#include "rttbStructure_LEGACY.h" +#include "DoseVoxel_LEGACY.h" +#include "DoseIteratorInterface_LEGACY.h" +#include "rttbField_LEGACY.h" +#include "rttbMaskType_LEGACY.h" +#include "rttbContour_LEGACY.h" +#include "rttbPolygonInfo_LEGACY.h" +#include "rttbIterativePolygonInTermsOfIntersections_LEGACY.h" +#include +#include + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + /*! @class Mask + * @brief This class represents the Mask calculated using a dose iterator and a StructureLegacy/DoseVoxelVector. + The mask is first calculated with voxel accurracy. Those voxels that are touched by the structure are then further + regarded using PolygonInfo and IterativePolygonInTermsOfIntersections to find out the fraction that is + inside the structure. + The voxelization is done on several resultution levels, but the calculation on subvoxel accurracy, + that means the calculation of the fraction of the voxel that is inside the structure, is done solely on the finest resolution level. + And solely for those voxels that are touched by the structure - typically most voxels are not. + This multi resolution approach is much faster as compared to doing it on just one level, + because a voxel that is not touched by the structure and determinded to be inside the structure on a + croase resolution level does on all finer resolution level solely correspond to voxels that are inside, too. The same + goes for a voxel that was determined to be completely outside: All the voxels that are contained in this voxel + on each finer resolution level are colpletely outside, too. So the creation of the mask without subvoxel accurracy is + done on the croasest resolution level completely and for each voxel with clear affiliation inside/outside all the + respective voxels are set for all the finer resolution levels right away as they are determined on a croase level. + It is cumputational effort to create several masks + and it is effort to set the inside/outside affiliation of a voxel on all levels instead of just one. However, it saves + effort, because on each finer resolution level, only view voxels + need to be considered: Those that are close to the contour and were thus touched by the contour on the previous level + that was to croase to tell the inside/outside affiliation. Since there is a factor four in the number of voxels between two levels, + the multi resouluton calculation is fast, because view voxels need to be regarded. + Field.h has an efficient way of finding those voxels with unclear affiliation and of setting affiliation throughout resoltuion levels in blocks. + After reaching the finest resolution those voxels that are still in the border region + and thus just touched by the structure are then further examined with subvoxel accurracy, based on + PolygonInfo and IterativePolygonInTermsOfIntersections. + + + */ + class Mask{ + + public: + + + /*! @brief Constructor with a DoseIterator and a StructureLegacy + * The mask is calculated with sub voxel fraction info. + @pre The Structure must not intersect itself. It may touch itself. + @param aDoseIter is a pointer to a DoseIteratorInterface. It is supposed to contain the information about + the dose value associated with each voxel. + @param aStructure is a Pointer to the StructureLegacy which holds the contour the mask shall be based on. + * @exception RTTBNullPointerException thrown if aDoseIter or aStructure is NULL + */ + Mask( DoseIteratorInterface* aDoseIter , StructureLegacy* aStructure); + + /*! @brief Constructor with a DoseIterator and a vector of DoseVoxel inside a structure (with voxel proportion information) + @param aDoseIter is a pointer to a DoseIteratorInterface. It is supposed to contain the information about + the dose value associated with each voxel. + @param aDoseVoxelVector is a reference to a vector that holds values of the type DoseVoxel. Using this parameter it is possible + to accept a given "voxelization" created by Mevis for example. + * @exception RTTBNullPointerException thrown if aDoseIter is NULL + */ +// Mask( DoseIteratorInterface* aDoseIter , const std::vector& aDoseVoxelVector ); + + /*! @brief Constructor with a DoseIterator and a StructureLegacy + * The mask is calculated with sub voxel fraction info. + @pre The Structure must not intersect itself. It may touch itself. + @param aGeoInfo is a pointer to a GeometricInfo of the dose. + @param aStructure is a Pointer to the StructureLegacy which holds the contour the mask shall be based on. + * @exception RTTBNullPointerException thrown if aDoseGeoInfo or aStructure is NULL + */ + Mask( core::GeometricInfo* aDoseGeoInfo , StructureLegacy* aStructure); + + + /*! @brief Destructor + */ + ~Mask(); + + /*! @brief Old Code!!! It is unnecessary now. The user should check the geometric information of the new dose iterator, if changed, + generate new mask using Mask( core::GeometricInfo* aDoseGeoInfo , StructureLegacy* aStructure)! + + *Old brief: Reset dose. If the geometric information has not changed, the _doseVoxelInStructure does not need to be recalculated, in case + other dose values are introduced for the same structure and geometry. Only doseData needs to be recalculated. + @param aDoseIter is a pointer to a DoseIteratorInterface. It is supposed to contain the information about + the dose value associated with each voxel. + * @exception RTTBNullPointerException Thrown if aDoseIter or aStructure is NULL + + */ + void resetDose(DoseIteratorInterface* aDoseIterator); + + + + /*! @brief Get the dose voxels which lie inside the structure + * @return Vector of DoseVoxel which are inside the structure (with voxel proportion information) + */ + const std::vector& getDoseVoxelInStructure() const; + + /*! @brief Set the vector of DoseVoxel which are inside the structure (with voxel proportion information) + * @param aDoseVoxelVector Vector of RTDoseVoxels inside the structure to be set. + * @return + */ + void setDoseVoxelInStructure(std::vector aDoseVoxelVector); + + /*! @brief Get the dose iterator + * @return DoseIteratorInterface& returns the dose Iterator. + */ + const DoseIteratorInterface& getDoseIterator() const; + + /*! @brief Get the structure + * @return StructureLegacy& Returns the structure. + */ + const StructureLegacy& getRTStructure() const; + + /*! @brief Get absolute dose data of the voxels which are inside the structure. + @return Returns the dose values. + */ + //const std::vector getDoseData() const; + + + /*new...................*/ + + /*! @brief Calculates 2D voxel coordinates from 2D world coordinates. + @param x voxel coordinate x + @param y voxel coordinate y + @return Returns 2D world coordinates. + */ + LegacyWorldCoordinate2D GetDoubleIndex2D( double x , double y ); + /*! @brief Calculates 2D voxel coordinates from 2D world coordinates taking the current resolution level under consideration. + @param x voxel coordinate x + @param y voxel coordinate y + @param resolution_level current resolution + @return Returns 2D voxel coordinates. + */ + LegacyWorldCoordinate2D GetDoubleIndex2D( double x , double y, int resolution_level ); + + /*! @brief The offset between world and voxel coordinate system depends on the current resolution. This function calculates this offset. + @param resolution_level current resolution + @return Returns the offset between world and voxel coordinate system. + */ + double GetWorldCoordinateOffsetThisResolutionLevel( int resolution_level ); + + // Functions and other stuff needed for unit tests: + /*! @brief Needed for unit tests. Provides the Information about the Intersections of a specific polygon, + that is part of the structure, with the edges of the voxels. + @param position Index des polygon of interest. The structure consists of an entity of plygons. + @param Polygon_Intersections_Ref Refernce to an object of the Type PolygonInTermsOfIntersection. After running the function, this Reference holds the Information about + the intersections of the polygon of interest (specified by position) with the voxel edges. + @return This function returns false and does not do its job, in case "position" holds an unreasonalble value, e.g. refers to a + polygon that does not exist. + */ + bool GetIntersectionsOfSliceForTest( unsigned int position , PolygonInTermsOfIntersections& Polygon_Intersections_Ref ); + /*! @brief Needed for unit tests. Returns true in case there are any intersections of the structure with the voxel edges. + A reference to an integer is set to the index of the first polygon that intersects with voxel edges + in case there are intersections. Otherways the reference is set to zero and the function returns false. + @param position Some reference to some integer which is modified by this function. + In case there are any intersection this reference is set to the index of the first intersecting polygon. + Otherways it is set to zero. + @return Returns false in case there are no intersections and true in case there are intersections of the contour with the voxel edges. + */ + bool ReturnFirstWithIntersectionsForTest( unsigned int& position ); + /*! @brief Needed for unit tests. This function is made to test the function GoIntersectColumn which is private and therefore not + accessible for the testing unit. This function called TestGoIntersectColumnByComparisonWithExpectedResult + is provided with two 3D Points and with a vector called expected_intersections_ref that holds the information about + those intersections that are to be expected. These values are set by the one who wrote the unit test. Now these expected + values are compared with those that are calculated by the function GoIntersectColumn and in case there is any deviation + between acutal an expected result this function here returns false. Otherwise it returns true. + + @param firstPoint The first one of two points that specify a line which is to be checked for intersections with voxel edges. + @param secondPoint The second one of two points that specify a line which is to be checked for intersections with voxel edges. + @param column_intersections_ref After running this function this reference to a vector of objects of the type LegacyWorldCoordinate2D + holds the information about the calculated intersections. + @param expected_intersections_ref This is a reference to a vector of objects of the type LegacyWorldCoordinate2D which holds the + information about the expected intersections. These intersections are set by the programmer + who writes the unit test and who knows the correct intersections. + @return This funciton returns true in case the function GoIntersectColumn turned out to work properly. Otherwise it returns false. + */ + bool TestGoIntersectColumnByComparisonWithExpectedResult( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& column_intersections_ref, std::vector& expected_intersections_ref ); + /*! @brief Needed for unit tests. See TestGoIntersectColumnByComparisonWithExpectedResult for details since this function here is very similar. + The only difference is that this funciton is made to help with unit testing with respect to the funcion GoIntersectRaw + instead of GoIntersectColumn - despite that its just the same. + */ + bool TestGoIntersectRawByComparisonWithExpectedResult( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& raw_intersections_ref, std::vector& expected_intersections_ref ); + /*! @brief Needed for unit tests. Checks whether two vectors of points that are passed to this function are identical. + @param calculated_intersections_ref First vector of points to be compared with the second one. + @param expected_intersections_ref Second vector of points to be compared with the first one. + @return Returns true in case the vectors are identical. + */ + bool IdenticalIntersectionsForTest( std::vector& calculated_intersections_ref , std::vector& expected_intersections_ref ); + /*! @brief Needed for unit tests. An object of the type PolygonInfo can only be created in an environment that has all the + incredients that are necessary. Since in Mask all the incredients are available and since there is no other environment, where this is the case, + since PolygonInfos are sofar not created anywhere else, this function was created, just in order to create and return a + PolygonInfo object for + test purposes. So this function creates an object of the type PolygonInfo and retunrns a pointer to it. + The PolygonInfo is created either for a voxel with cordinates aDoseIndex, given as a parameter + or alternatively for a voxel with greater + z-position in case there is no contour in the slice of the given location. In case there is no contour in any + slice with greater z-position either, the PolygonInfo can not be created. In that case a nullpointer is returned. + @param aDoseIndex Position for which the PolygonInfo object is to be created. + @return Returns an object of the type PolygonInfo in case a polygon of the structure can be found. Otherways it returns a NULL + pointer. + */ + PolygonInfo* JustCreateAPolygonInfoForTest( LegacyDoseVoxelIndex3D aDoseIndex ); + /*! @brief Needed for unit tests. Checks for a specific voxel, whether the correct fraction of this voxel was recognized to be + enclosed by the structure. + @param another_dose_index parameter of type LegacyDoseVoxelIndex3D which specifies the voxel coordinate of interest. + @param the_correct_result is a float that specifies the expected result. + @return Returns true in case Mask did well and the calculated fraction meets the expectation. + */ + bool CheckFractionThisVoxelCorrectForTest( LegacyDoseVoxelIndex3D another_dose_index , float the_correct_result ); + /*! @brief Needed for unit tests in order to access the fraction of a specific voxel that was determined to be enclosed by the structure. + @param aDoseIndex is a reference to a LegacyDoseVoxelIndex3D which specifies the voxel position of interest. + @return Returns a float that holds the informaton about the portion of the voxel which is enclosed by the structure. + */ + float GetFractionThisVoxelForTest( LegacyDoseVoxelIndex3D another_dose_index ); + /*! @brief Needed for unit tests. Returns true in case the voxel specified by aDoseIndex is touched by the structure. + @param aDoseIndex is a reference to a LegacyDoseVoxelIndex3D which specifies the voxel position of interest. + @return Returns true in case the voxel specified by aDoseIndex is touched by the structure. + */ + bool DoubleCheckIsOnBorderForTest( LegacyDoseVoxelIndex3D& aDoseIndex ); + /*! @brief Needed for unit tests. The parameter aDoseIndex is set to a voxel that is touched by the structure. + @param aDoseIndex is a reference to a LegacyDoseVoxelIndex3D which is modified by this function. If things go well, + it is set to a voxelposition touched by the structure. In that case the function returns true. + @return Returns false in case no voxel is touched by the structure. Otherways returns true. + */ + bool GetSomeBorderVoxelXYZForTest( LegacyDoseVoxelIndex3D& aDoseIndex ); + // End functions and other stuff needed for unit tests + + + + protected: + + typedef std::vector* VoxelIntersectionsVectorPointer; + + // @brief withSubVoxelFraction If true, the mask will be calculated with sub voxel fraction info; otherwise, without + // sub voxel fraction info, that means getDoseVoxelInStructure() returns the DoseVoxel with getProportionInStr()= 1. + // + //bool _withSubVoxelFraction; + + /// the dose iterator + DoseIteratorInterface* _doseIter; + /// the structure + StructureLegacy* _structure; + + /// vector of the RTDoseVoxels inside the structure + std::vector _doseVoxelInStructure; + + /// vector of the dose values corresponding to _doseVoxelInStructure, it has the same size as _doseVoxelInStructure + std::vector _doseData; + + /// geometric information about the dose distribution + std::vector< core::GeometricInfo > GInf; + + //VoxelIntersectionPointsMap voxel_intersectionPoints; + + /// Once they are determined to be inside voxels get this brightness. + field_content_type brightness_inside; + /// Once they are determined to be touched by the structure voxels get this brightness. + field_content_type brightness_border; + /// Once they are determined to be outside voxels get this brightness. + field_content_type brightness_outside; + /// Voxels that are yet not determinded to be inside the structure, outside the structure or touched by the structure are marked with this brightness. + field_content_type brightness_unknown; + + /// Calculates the mask. + void Calc(); + /// Just for test. Doublechecks and does assert(0) in case something has gone wrong. + void TestVoxelMaskUppward(); + + /*! @brief This function is made for assertation. The voxelization is done on several resultution levels as explained above. + There are situations throughout the algorithm where it is known, that a specific affiliation of one voxel on one resolution + level does determine its affiliation on other resolution levels. The job of this function is to check that and to + assert, it terminates the program deliberately if something has happened, that definitely must never happen. + @param resolution_level_in Integer that species the current resolution level. + @param index_internal Parameter of the type LegacyUnsignedIndex3D which specifies the position in the voxel coordinate system + on the current resolution level that is to be checked throughout all finer levels. + @param brightness Integer that specifies the brightness that is expected throughout all the finer resolution levels. + */ + void CheckUppwardBlockSameIntensity( int resolution_level_in , LegacyUnsignedIndex3D index_internal , int brightness ); + /*! @brief This function sets a brightness of a voxel throughout resolution levels, since the Voxelisierung is done on + several resultution levels as explained above. + @param resolution_level_in Integer that species the current resolution level. + @param index_internal Parameter of the type LegacyUnsignedIndex3D which specifies the position in the voxel coordinate system + on the current resolution level that is to be set at first and then throughout all finer levels + adapted to the finer voxel coordinate sysetem. + @param brightness Integer that specifies the brightness that is to be set on the current and throughout all the finer resolution levels. + */ + void SetUppwardBlockThisIntensity( int resolution_level_in , LegacyUnsignedIndex3D index_internal , int brightness ); + /*! @brief This function is made for assertation. The voxelization is done on several resultution levels as explained above. + There are situations where a specific affiliation of one voxel on one resolution + level does determine affiliation of at least one voxel on any finer resolution + level to the same intensity, within a specific block of voxels that corresponds to the voxel in position index_internal + on the croase level. + The job of this function is to check whether + there is any such voxel on every finer resolution level, within the respective block. + So this function terminates the program deliberately if something has happened, that definitely must never happen. + @param resolution_level_in Integer that species the current resolution level. + @param index_internal Parameter of the type LegacyUnsignedIndex3D which specifies the position in the voxel coordinate system + on the current resolution level that is to be checked throughout all finer levels. + @param brightness Integer that specifies the brightness that is expected throughout all the finer resolution levels. + */ + void CheckUppwardOneOutOfThisBlockHasSameIntensity( int resolution_level_in , LegacyUnsignedIndex3D index_internal , int brightness ); + /*! @brief Calculates for some resolution level the number of voxels of the finest resolution level, that fit into this + voxel one dimensionally. So this is a number of fine voxels that fit into this croase voxel along the length of this croase voxel. + @param resolution_level Integer that specifies the resolution level of the croase voxel. Whereas zero is the finest level, one the next + croase level with a total number of voxels of just one quater of the next finer level .... and so on .... + @return Returns a double that holds the information about the block length. Actually it should be an integer, but a double works, too ... + */ + double GetBlockLengthThisResolutionLevel( int resolution_level ); + /*! @brief Checks the structure, so checks all polygons of the structure, on intersections with themselves. + @return Returns true, if the structure does not contain self intersecting polygons and thus is fine. + */ + bool ContourFine(); + /*! @brief Checks two polygons for intersection with one another. Checks one polygon for intersection with itself, in case + struct_index_a and struct_index_b are the same. + @param struct_index_a Integer that specifies the index of the first polygon that is to be checked for intersections. + @param struct_index_b Integer that specifies the index of the second polygon that is to be checked for intersections. + @return Returns true if the polygons do intersect (or if the polygon does intersect in case of a check for self intersection). + */ + bool DoesIntersect( int struct_index_a , int struct_index_b ) const; + /*! @brief Checks two lines for intersection. + @param firstPoint Paramter of type LegacyWorldCoordinate3D that specifies the beginning of the first line. + @param secondPoint Paramter of type LegacyWorldCoordinate3D that specifies the end of the first line. + @param thirdPoint Paramter of type LegacyWorldCoordinate3D that specifies the beginning of the second line. + @param fourthPoint Paramter of type LegacyWorldCoordinate3D that specifies the end of the second line. + @return Returns true in case there is an intersection. + */ + bool HasIntersectionsThisPair( const LegacyWorldCoordinate3D& firstPoint , const LegacyWorldCoordinate3D& secondPoint, const LegacyWorldCoordinate3D& thirdPoint , const LegacyWorldCoordinate3D& fourthPoint ) const; + + /*! Holds the masks for different resolution levels. + To be initialized in Constructor by an initialization function. + */ + std::vector< rttb::masks::legacy::FieldOfScalar* > MaskField; + + /*! Two dimensional field. + Content of one element holds the information about the intersections of the structure with + the voxel edges of the corresponding voxel. The object, that holds this intersecton information + for the voxel in position x,y within the slice, is located in the position x,y within FieldOfIntersections. + FieldOfIntersections is created for just one slice - the slice which Mask is currently working on. + */ + rttb::masks::legacy::FieldOfPointer* FieldOfIntersections; + + bool do_detailed_subvox; /// If do_detailed_subvox is true, the Mask is calculated with sub-voxel accurracy. + + /*! To be initialized in Constructor by an initialization function. + Holds the information about the structure and its Intersections with the voxel edges. + */ + std::vector Intersections; ///Holds the information about the structure and its Intersections with the voxel edges. + + /*! @brief Fills _doseVoxelInStructure with DoseVoxel objects. Each of these DoseVoxel objects carries the information about the + portion of the voxel that is enclosed by the structure. The DoseVoxel objects are created based on the Mask + with the finest resolution. Based on this Mask field it is clear that the voxel is completely inside the structure or + completely outside in most of the cases. + SetDoseVoxelInStructureVector calls GetFractionInside() and thus determines the fraction + of the voxel that is enclosed by the structure in case the voxel is touched by the structure and thus + not yet determined to be completely inside or outside. + */ + void SetDoseVoxelInStructureVector(); + + + /*! @brief Old stuff. + */ + void SeperateRegions(); + + /*! @brief Old stuff. + */ + void SeperateRegionsQuick(); + + /*! @brief Old Stuff. + */ + void ConnectArea( field_content_type brightness_from , field_content_type brightness_to , LegacyUnsignedIndex3D start_index, LegacyDoseVoxelIndex2D index_begin, LegacyDoseVoxelIndex2D index_end ); // To be called by SetDoseVoxelInStructureVector + + /*! @brief To be initialized by an init function that is called by Constructor. Holds the information about how the polygons correspond to the slices. + */ + std::vector SliceStructureCorrespondency; + + + /*! @brief To be initialized by an init fuction which is called by Constructor. Contains the information about which area of the image is actually to be regarded. A large part of the image may be far off from any polygon. + */ + vector_of_area_vectors MarkInterestingArea; + + + /*! @brief Is called by the constructor and calls other initialization fucntions. + */ + void Init(); + + + /*! @brief Searches backward in Intersections until a point is reached that differs from compare_point. + point_index is set to the index of this point. + @param struct_index Reference to an integer that specifies the polygon of interest. + @param point_index Reference to an integer that is modified by this function. + First it specifies the index of the point where the function stearts searching. + It is decremented during the search and in the end it points to the first point within the search direction + that turned out to be different from compare_point. + @param compare_point Parameter of the type LegacyWorldCoordinate3D. Specifies a point. This function searches for a point + which differs from compare_point. + @return Returns true in case such a point was found and false in case it was not. + */ + bool GetFirstDifferent( unsigned int& struct_index , unsigned int& point_index , LegacyWorldCoordinate3D compare_point ); + + + + /*! @brief This function determines whether the intersection with struct_index, point_index and intersect_index + is to be set to the FieldOfIntersections. It is to be set in case the intersection with the voxeledge is either + not a point of the polygon, or in case it is ( that means the point of the polygon is on the edge of a voxel ) + and at the same time the previous point of the polygon has one identical coordinate in a way so that it is located on + the same edge, too (eventually alongside another voxel, so the same edge here means "same column seperating voxels"). + @param struct_index Index of the polygon. + @param point_index Index of the point within the polygon (last one between the intersection that is currently being processed). + @param intersect_index Index of the intersection that is currently being processed. + @param index_x X-position of the voxel. The element of FieldOfIntersectons that belongs to this voxel is calculated right now. + @param index_y Y-position of the voxel. The element of FieldOfIntersectons that belongs to this voxel is calculated right now. + @return Retruns true in case the intersection with struct_index, point_index and intersect_index is to be set to the + FieldOfIntersections. + */ + bool IsItToBeRegarded( unsigned int struct_index , unsigned int point_index , unsigned int intersect_index , int index_x , int index_y ); + + + + /*! @brief This function calls SetThisIntersection with the respective parameters and therefore takes part in positioning the intersection characterized by the + parameter intersection within the FieldOfIntersections. + @param index_x X-position of the voxel. + @param index_y Y-position of the voxel. + @param intersection Specifies the intersection that is currently regarded (similar to an index, specifies location where the intersection can be found within the object Intersections). + @param coord2D Voxel coordinate of the intersection. + @param edge_number Denotes the edge which the intersection is on. + @param voxel_intersections The element within FieldOfIntersections that corresponds to this voxel and polygon. + */ + void SetThisIntersectionToCornerOrEdge(unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D, unsigned int edge_number, IntersectionsThisVoxel& voxel_intersections ); + + + /*! @brief This function positions an intersection within voxel_intersections. Practically voxel_intersections is an element within FieldOfIntersections (generally it can be any reference to an IntersectionsThisVoxel ). + The voxel edge the intersection is placed to is given by edge_number. + @param corner_of_interest The corner at the beginning of the respective edge. Beginning means the point of the edge that is first passed while surrounding the voxel clockwise. + @param corner_next The corner at the end of the respective edge. That means the point along the edge that is passed last while surrounding the voxel clockwise. + @param corner_fixed Its the coordinate, which is the same all along the edge. + @param intersection Denotes the index of the regarded intersection in Intersections. + @param coord_of_interest The coordinate which is interesting, because it is responsible for the position of the intersection along the edge, while the other coordinate is just the same for the intersection as well as for all the points that belong to the edge. + @param coord_fixed This coordinate should equal to corner_fixed. The function asserts for that. Its the coordinate that all the points on the edge and the intersection point have in common. + @param voxel_intersections Practically its an element of FieldOfIntersections (although generally it can be any element of IntersectionsThisVoxel). + @param edge_number Number of the edge that is regarded. The edges are counted colckwise starting from upper left. + */ + void SetThisIntersection( unsigned int corner_of_interest, unsigned int corner_next , unsigned int corner_fixed , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , double coord_of_interest , double coord_fixed, IntersectionsThisVoxel& voxel_intersections , unsigned int edge_number ); + + + /*! @brief This function checks whether value_to_compare is in between small_value_to_compare_with and big_value_to_compare_with. Practically value_to_compare is a coordinate of an intersection point. Practically big_value_to_compare_with is the end point of an edge. + In case value_to_compare is in between, the index of the intersection (that characterizes its location within Intersections (Intersections is a global variabele, a vector of StructureInTermsOfIntersections) ) and its coordinates are appended to the corresponding edge within voxel_intersections. + @param value_to_compare This value is to be compared with other values and inserted into voxel_intersections at the right place. + @param small_value_to_compare_with First value to compare with. + @param big_value_to_compare_with Second value to compare with. + @param edge_number Number of the edge. Edges are counted clockwise, starting from upper left. + @param intersection Index of the intersection within Intersections. + @param voxel_intersections Object of type IntersectionsThisVoxel that holds the information about all the intersections of the structure with this currently regarded voxel as well as the information where to find these intersection within Intersections + @return Retruns true in case insertation was successful. + */ + bool CompareIfBetweenAppend( double value_to_compare , double small_value_to_compare_with , double big_value_to_compare_with, unsigned int edge_number, IterativePolygonInTermsOfIntersections::WhichIntersection intersection , IntersectionsThisVoxel& voxel_intersections ); + + + /*! @brief This function checks whether the intersection point is already known. + In case it is the index of the intersection within Intersections is appended to the already existing vector of points of the respective edge that all represent this specific intersection point (in case the structure goes through this point several times touching itself, otherways ist just one) + (Intersections is a global variable, a vector of StructureInTermsOfIntersections which contains the information about all the intersections with all the voxel edges on all resolutions). + In case it does not exist yet a new vector of indices as well as coordinates is created and inserted in the respective position. + In case the same intersection point will be found again later on it will be appended then. + @param value_to_compare Coordinate of the point to be inserted. + @param edge_number Number of the edge. The edges are counted clockwise, starting from upper left. + @param intersection Index of the intersectoin within Intersections. + @param voxel_intersections An object representing the information about the intersections of the structure with this voxel, including the information about coordinates of the intersection points as well as indicees that represent the information about where to find them within Intersections. + @return Returns true in case of a successful insertation. + */ + bool CheckIfIndenticalOrBetweenRespectiveVectorElementsAndDoInsert( double value_to_compare , unsigned int edge_number, IterativePolygonInTermsOfIntersections::WhichIntersection intersection , IntersectionsThisVoxel& voxel_intersections ); + + + /*! @brief See CompareIfBetweenAppend - the only difference is, that this function inserts the intersection at the beginning of the edge and that practically small_value_to_compare_with is the first point within the respective edge while big_value_to_compare_with is either the first intersection that has aleready been found, or the last point within the edge. + @return Returns true in case the intersection was inserted into voxel_intersections. + */ + bool CompareIfBetweenInsertAtBeginning( double value_to_compare , double small_value_to_compare_with , double big_value_to_compare_with, unsigned int edge_number, IterativePolygonInTermsOfIntersections::WhichIntersection intersection , IntersectionsThisVoxel& voxel_intersections ); + + + /*! @brief Old stuff. + */ + void SetThisIntersectionXIncreasing( unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections , unsigned int edge_number ); + /*! @brief Old stuff. + */ + void SetThisIntersectionYIncreasing(unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections ); + /*! @brief Old stuff. + */ + void SetThisIntersectionXDecreasing(unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections ); + /*! @brief Old stuff. + */ + void SetThisIntersectionYDecreasing(unsigned int index_x , unsigned int index_y , IterativePolygonInTermsOfIntersections::WhichIntersection intersection , LegacyWorldCoordinate2D coord2D , IntersectionsThisVoxel& voxel_intersections ); + + + /*! @brief This function determines the area that contains voxels that need to be regarded. + For example voxels within x-coordinate lower than the contour pont with lowest x-coordinate do not need to be regarded, since they can + not be enclosed by the contor. Of course the same goes for other coordintates and ... + */ + void InitInterestingArea(); + /*! @brief Initializes MaskField which contains the masks fields for different resolution levels and the corresponding geometric infromation stored in GInf. + Different sizes are initialized, representing the different resolutions. Calls InitIntersections. + */ + void InitMultiResolution(); + /*! @brief Initializes Intersections which is a vector of StructureInTermsOfIntersections and holds the information about the intersections between voxel grid and structure for different resolution levels. + This function just initializes, but yet does not calculate the intersections, so here Intersectons is created, but remains empty. + */ + void InitIntersections(); + + /*! @brief This function is called to calculate the intersections between structure and voxel edges and thus to fill + Intersections with intersecton values, which is a vector of StructureInTermsOfIntersections and holds the information about the intersections between voxel grid and structure for different resolution levels. + @param resolution_level Specifies for which voxel grid the intersections are to be calculated. In case it is zero it is done for the + finest resolution and the values are filled into the first element of Intersections. + */ + void GetIntersections( int resolution_level ); + + /*! @brief This function fills intersections_raw_and_column_ref with intersection values. It operates on a segment of a polygon consisting of a line that connects a first and a second point. + The intersections of this polygon segment with the voxel edges on the resolution_level are already determined and stored in + raw_intersections_ref, which contains the intersections with raw-eges as well as + column_intersections_ref which holds the intersections with the voxel edges regarding columns. + In intersections_raw_and_column_ref they are sorted with respect to their distance + from firstPoint and in the end intersections_raw_and_column_ref contains all the intersections - colum and raw intersections. + @param firstPoint Starting point of the segment of the polygon. + @param raw_intersections_ref Contains the raw intersections sorted with resepect to their distance from firstPoint. + @param column_intersections_ref Contains the column intersections sorted with resepect to their distance from firstPoint. + @param intersections_raw_and_column_ref After running this function intersections_raw_and_column_ref contains all intersections sorted with resepect to their distance from firstPoint. + @param resolution_level The resolution level. Zero is finest. + */ + void UnifyRawAndColumn( LegacyWorldCoordinate3D firstPoint, std::vector& raw_intersections_ref , std::vector& column_intersections_ref , std::vector& intersections_raw_and_column_ref, int resolution_level ); + + + /*! @brief This function calculates the intersections between a line and the horizontal voxel edges (voxel edges in raw direction). + The line starts with firstPoint and ends with secondPoint. + @param firstPoint Point to start the line. + @param secondPoint point to end the line. + @param raw_intersections_ref The intersections are placed here. + @param resolution_level The current resolution. + */ + void GoIntersectRaw( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& raw_intersections_ref , int resolution_level ); + + + /*! @brief This function calculates the intersections between a line and the vertical voxel edges (voxel edges in column direction). + The line starts with firstPoint and ends with secondPoint + @param firstPoint Point to start the line. + @param secondPoint point to end the line. + @param column_intersections_ref The intersections are placed here. + @param resolution_level The current resolution. + */ + void GoIntersectColumn( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& column_intersections_ref, int resolution_level ); + + + /*! @brief This function calculates the intersections between a line and the horizontal voxel edges (voxel edges in raw direction). + The line starts with firstPoint and ends with secondPoint. + This function is called in case the x-position of firstPoint and secondPoint are identical. + @param firstPoint Point to start the line. + @param secondPoint point to end the line. + @param raw_intersections_ref The intersections are placed here. + @param resolution_level The current resolution. + */ + void GoGetRawIntersectionsAlongThisColumn( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& raw_intersections_ref, int resolution_level ); + + + /*! @brief This function calculates the intersections between a line and the vertical voxel edges (voxel edges in column direction). + The line starts with firstPoint and ends with secondPoint. This function is called only in case the y-position of first and of + second point are identical. + @param firstPoint Point to start the line. + @param secondPoint point to end the line. + @param column_intersections_ref The intersections are placed here. + @param resolution_level The current resolution. + */ + void GoGetColumnIntersectionsAlongThisRaw( LegacyWorldCoordinate3D firstPoint , LegacyWorldCoordinate3D secondPoint , std::vector& column_intersections_ref, int resolution_level ); + + + /*! @brief This function prints out all the intersections with cout. Normally its not called, but sometimes its needed for debugging. + */ + void ShowIntersections(); + + + /*! @brief Based on the global variable Intersections that holds the information about the intersections betweent the voxel edges and + the polygons this function determines all voxels that are touched by the structure on resolution_level. So this operation + is carried out for the mask field that belongs to resolution_level. For resolution_level equal to zero its the finest + resolution and so the resolution of the mask field is the resolution of the dose distribution. + */ + void MarkVoxelsTouchedByStructure( int resolution_level ); + + + /*! @brief Sets FieldOfIntersections for the current slice. This is done based on Intersections which stores the information about the + polygons and their intersections. + @param index_z Integer that represents the index of the slice which is to be regarded. + */ + void GetIntersectionInformationThisSlice( unsigned int index_z ); + + + /*! @brief The intersection with voxel coordinates coord2D is inserted into + the FieldOfIntersectons in position of voxel (index_x , index_y , index_z) and for the polygon with index struct_index + and for the side of the voxel denoted by voxel_side. + @param index_x X-position of the voxel. + @param index_y Y-position of the voxel. + @param index_z Z-position of the voxel. + @param struct_index Index of the polygon that is intersecting here. + @param point_index Index of the last polygon point before the intersection. + @param intersect_index Index of the intersection. By the way also represents the information about how many intersections with voxel edges are in between this intersection with voxel edge and the last polygon point. + @param coord2D Intersection in voxel coordinates. + @param voxel_side Side of the voxel which is intersected here. + */ + void AddToFieldOfIntersections( unsigned int index_x , unsigned int index_y , unsigned int index_z , unsigned int struct_index , unsigned int point_index , unsigned int intersect_index , LegacyWorldCoordinate2D coord2D, unsigned int voxel_side ); + + + /*! @brief A new element of the type VoxelIntersectionsVectorPointer is inserted into the FieldOfIntersections and a first element + of type IntersectionsThisVoxel is inserted. + @param index_x X-position of the voxel of interest, which needs specification of its intersections. + @param index_y Y-position of the voxel of interest, which needs specification of its intersections. + @param index_z Z-position of the voxel of interest, which needs specification of its intersections. + @param struct_index Specifies the specific polygon and its respective PolygonInTermsOfIntersections which intersects here. + */ + void ExtendFieldOfIntersections( unsigned int index_x , unsigned int index_y , unsigned int index_z , unsigned int struct_index ); + + + /*! @brief In case FieldOfIntersections does already have an element of type VoxelIntersectionsVectorPointer in the position that + corresponds to the voxel in index_x , index_y , index_z, + the index of the IntersectionsThisVoxel element that corresponds to the polygon with index struct_index is returned. + @param index_x X-position of the voxel of interest. + @param index_y Y-position of the voxel of interest. + @param index_z Z-position of the voxel of interest. + @param struct_index Index of the polygon of interest. + @return Returns the index of the IntersectionsThisVoxel element that corresponds to the polygon with index struct_index. + */ + int GetRespectiveIntersections( unsigned int index_x , unsigned int index_y , unsigned int index_z , unsigned int struct_index ) const; + + + /*! @brief This function calculates and returns the fraction of the voxel with index aDoseIndex that is enclosed by the strucure. + This fraction is between zero and one. + @param aDoseIndex Index of the voxel of interest. + @return Returns a number between zero and one that specifies the fraction of the voxel that is enclosed by the structure. + */ + float GetFractionInside( const LegacyDoseVoxelIndex3D& aDoseIndex ) ; + + + /*! @brief Gets a number of voxels that are known to have oldcolor via indexList. Takes a voxel from this list. Lets call it THEVOXEL. + Checks the neighbours of THEVOXEL. Each neighbour that does have oldcolor is appended to the list for further + investigation. Afterwards THEVOXEL is set to newcolor and taken from the list. Now the next voxel of the list is regarded ... + ... so it is now THEVOXEL ... and so on ... until the list is empty. + @param indexList List of indicees that are known to have oldcolor. + @param oldcolor The old color. + @param newcolor The new color. + @param resolution_level The resolution level which is currently regarded. + */ + void FloodFill4( UnsignedIndexList indexList ,int oldcolor,int newcolor,int resolution_level); + + + /*! @brief Old stuff. + */ + void setInsideVoxelPreviousVersion(int resolution_level); + + + /*! @brief Goes through MaskField over all resolution levels and sets all the mask voxels to the brightness that characterizes + those voxel that are not known to be outside, inside or border. Those voxels that do already have the brightness that + characterizes them as border are not changed here. + */ + void SetContentUnknown(); + + + /*! @brief This function calls FloodFill4 for those voxels that are known to be inside or outside although yet not denoted so in the + mask field. FloodFill4 will first investigate the neighbourhood of these voxels and then set them to the correct value. + @param resolution_level The resolution that is currently regarded. + @param indexListInside Voxels known to be inside, but yet not denoted so in the mask field. + @param indexListOutside Voxels known to be outside, but yet not denoted so in the mask field. + */ + void setInsideVoxel( int resolution_level, std::vector< UnsignedIndexList > indexListInside, std::vector< UnsignedIndexList > indexListOutside ); + + + /*! @brief This function is just for debugging. It prints out the number of voxels with specific brightness. + @param resolution_level The resolution_level of interest. + */ + void CountThisColor( int resolution_level ); + + + /*! @brief First this function determines the voxels that are yet not denoted inside or outside in the mask field, + but are directly in touch with voxels which are already known to be inside/outside and thus their affiliation is clear. + This is done for the resolution resolution_level. GetBorderRegion of Field.h is called to do this job, + if the resolution is not croasest. In case the resolution is croasest, this step is omitted. + In case of the croasest resolution this step is not necessary and thus omitted since + on the croasest resolution no voxel is yet known to be inside or outside. + Afterwards setInsideVoxel is called by + this function. + @param resolution_level The resolution level which is supposed to be regarded here. + */ + void setInsideVoxelMultiRes( int resolution_level ); + + + /*! @brief Transforms world coordinate in voxel coordinate. + @param z The world coordinate to be tranformed. + @return Returns the resulting voxel coordinate. + */ + LegacyWorldCoordinate1D GetZIndex( LegacyWorldCoordinate1D z ); + + + /*! @brief Provides _doseData with the absolute dose data of the voxels which are inside the structure. + */ + void calcDoseData(); + + + // private stuff that is just for test: + /*! @brief Needed for unit tests. The parameter aDoseIndex is set to a voxel that is touched by + the structure and which is located in the same slice that is given by the z-position of aDoseIndex, if possible. + @param aDoseIndex is a reference to a LegacyDoseVoxelIndex3D which is modified by this function in x and y, but not in z. + If things go well, aDoseIndex is set to a voxelposition within the slice in position z that is touched + by the structure. In that case the function returns true. + @return Returns false in case no voxel is touched by the structure within this slice. Otherways returns true. + */ + bool GetSomeBorderVoxelXYForTest( LegacyDoseVoxelIndex3D& aDoseIndex ); + // end private stuff just for test + + + private: + void clearMaskField(); + + + }; +} + + } +} + + + + + + + +#endif \ No newline at end of file diff --git a/code/masks/rttbMaskType_LEGACY.h b/code/masks/rttbMaskType_LEGACY.h new file mode 100644 index 0000000..20291af --- /dev/null +++ b/code/masks/rttbMaskType_LEGACY.h @@ -0,0 +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 __MASK_TYPE_H +#define __MASK_TYPE_H + +#include "rttbBaseType_LEGACY.h" +#include + + +namespace rttb{ + namespace masks{ + namespace legacy + { + + /*! @brief PointLevelVector + This structure represents one point of the structure and the intersections of the following + contour segment with the voxel edges. Contour segment, that is a line connecting this contour point and the next one. + contour_point_voxel_coord represents the contour point in voxel coordinates. + intersections_raw_and_column represents all the intersections with the voxel eges sorted in a way, + so that the one that is closest to the contour point represented by this PointLevelVector is the first one. + The one that is furthest from the contour point represented by this PointLevelVector is the last one + that appears in intersections_raw_and_column. + raw_intersections is just like intersections_raw_and_column, but represents the intersections with the voxel edges + that are parallel to the voxel raws. + column_intersections is like raw_intersections but represents the intersections with the voxel edges that + are parallel to the voxel columns. + */ + + struct PointLevelVector{ + + PointLevelVector(){ + to_be_Regarded = 1; + } + + ~PointLevelVector(){ + raw_intersections.clear(); + column_intersections.clear(); + intersections_raw_and_column.clear(); + } + + std::vector raw_intersections; + std::vector column_intersections; + std::vector intersections_raw_and_column; + LegacyWorldCoordinate3D contour_point_voxel_coord; + + bool to_be_Regarded; + }; + + /*! @typedef correspoinding_structure_vector + */ + typedef std::vector correspoinding_structure_vector; + + /*! @typedef PolygonLevelVector + */ + typedef std::vector PolygonLevelVector; + + /*! @typedef SliceLevelVector + */ + typedef std::vector SliceLevelVector; + + /*! @typedef field_content_type + */ + typedef int field_content_type; + + /*! @typedef VoxelIntersectionPointsMap + */ + typedef std::map< LegacyDoseVoxelIndex3D,std::vector > VoxelIntersectionPointsMap; + + /*! @brief Characterizes the relationship between the voxel edge (in this position that this CharacterizeIntersection object belongs to) + and the contour that is in touch with the edge right here. + real_intersection for example means that here the structure really intersects the voxel edge. + inside_to_edge means that the structure, coming from inside the voxel touches the voxel edge here and then runs along the voxel edge, touching rather than intersecting. + edge_to_edge means that the structure goes along the edge and has a contour point here. So it comes and goes along the edge and has a point here. + outside_to_edge means that here the contour comes from outside the voxel and continues to run along the edge. So here in this position represented by this CharacterizeIntersection the contour does not actually run into the voxel. + inside_inside_touches means that the contour comes from inside the voxel, does have a point here in this position right on the edge and continues its way going back into the voxel. So the contour just touches the voxel edge and does not really intersect here. + corner : this is just a corner. + unknown nobody knows what that is. + */ + enum CharacterizeIntersection{ unknown = -1 , real_intersection = 0 , edge_to_edge = 1 , inside_to_edge = 2 , edge_to_inside = 3 , outside_to_edge = 4 , edge_to_outside = 5 , inside_inside_touches = 6, outside_outside_touches = 7, corner = 8 }; + + typedef LegacyPolygonType::iterator LegacyPolygonTypeIterator; + + typedef std::vector PolygonInTermsOfIntersections; + + typedef std::vector StructureInTermsOfIntersections; + + typedef std::list UnsignedIndexList; + + } + } +} + + + + + + + +#endif \ No newline at end of file diff --git a/code/masks/rttbOTBMaskAccessor.cpp b/code/masks/rttbOTBMaskAccessor.cpp new file mode 100644 index 0000000..383aa1b --- /dev/null +++ b/code/masks/rttbOTBMaskAccessor.cpp @@ -0,0 +1,184 @@ +// ----------------------------------------------------------------------- +// 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 "rttbOTBMaskAccessor.h" +#include "rttbMappingOutsideOfImageException.h" + +#include + +#include +#include +#include + +namespace rttb +{ + + namespace masks + { + + OTBMaskAccessor::OTBMaskAccessor(StructTypePointer aStructurePointer, + const core::GeometricInfo& aGeometricInfo) + : _spStructure(aStructurePointer), _legacyStructure(*aStructurePointer) + { + _spRelevantVoxelVector = MaskVoxelListPointer(); + _geoInfo = aGeometricInfo; + + //generate new structure set uid + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _maskUID = "OTBMask_" + ss.str(); + } + + + OTBMaskAccessor::~OTBMaskAccessor() + { + }; + + void OTBMaskAccessor::updateMask() + { + MaskVoxelList newRelevantVoxelVector; + + if (_spRelevantVoxelVector) + { + return; // already calculated + } + + + legacy::Mask legacyMask(&_geoInfo , &_legacyStructure); + + //translate result to expected values + const std::vector voxelsInStruct = legacyMask.getDoseVoxelInStructure(); + + for (std::vector::const_iterator it = voxelsInStruct.begin(); + it != voxelsInStruct.end(); ++it) + { + rttb::VoxelGridID aVoxelGridID; + rttb::VoxelGridIndex3D newIndex; + + legacy::LegacyDoseVoxelIndex3D oldIndex = it->getVoxelIndex3D(); + newIndex(0) = oldIndex.x; + newIndex(1) = oldIndex.y; + newIndex(2) = oldIndex.z; + + // new architecture + if (!_geoInfo.convert(newIndex, aVoxelGridID)) + { + throw core::MappingOutsideOfImageException("mapping outside of image occurred!"); + } + + core::MaskVoxel newMaskVoxel(aVoxelGridID); + newMaskVoxel.setRelevantVolumeFraction(it->getProportionInStr()); + + newRelevantVoxelVector.push_back(newMaskVoxel); + } + + _spRelevantVoxelVector = boost::make_shared(newRelevantVoxelVector); + return; + } + + OTBMaskAccessor::MaskVoxelListPointer OTBMaskAccessor::getRelevantVoxelVector() + { + // if not already generated start voxelization here + updateMask(); + return _spRelevantVoxelVector; + } + + OTBMaskAccessor::MaskVoxelListPointer OTBMaskAccessor::getRelevantVoxelVector(float lowerThreshold) + { + MaskVoxelListPointer filteredVoxelVectorPointer(new MaskVoxelList); + updateMask(); + // filter relevant voxels + OTBMaskAccessor::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 OTBMaskAccessor::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) + { + OTBMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); + + while (it != _spRelevantVoxelVector->end()) + { + if ((*it).getVoxelGridID() == aID) + { + voxel = (*it); + return true; + } + + ++it; + } + + //aID is not in mask + voxel.setRelevantVolumeFraction(0); + } + // returns false if mask was not calculated without triggering calculation (otherwise not const!) + else + { + return false; + } + + return false; + + } + + bool OTBMaskAccessor::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; + } + } + + } +} \ No newline at end of file diff --git a/code/masks/rttbOTBMaskAccessor.h b/code/masks/rttbOTBMaskAccessor.h new file mode 100644 index 0000000..f7c8c82 --- /dev/null +++ b/code/masks/rttbOTBMaskAccessor.h @@ -0,0 +1,121 @@ +// ----------------------------------------------------------------------- +// 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_ACCESSOR_OTB_NEW_H +#define __MASK_ACCESSOR_OTB_NEW_H + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbMaskVoxel.h" +#include "rttbMaskType_LEGACY.h" +#include "rttbBaseType_LEGACY.h" +#include "rttbStructure_LEGACY.h" +#include "rttbMask.h" +#include "rttbContour_LEGACY.h" +#include "rttbPolygonInfo_LEGACY.h" +#include "DoseIteratorInterface_LEGACY.h" +#include "DoseIterator_LEGACY.h" +#include "rttbMaskAccessorInterface.h" +#include "rttbGenericDoseIterator.h" +#include "rttbStructure.h" + +#include + + +namespace rttb +{ + + namespace masks + { + /*! @class OTBMaskAccessor + * @brief Implementation of original toolbox voxelization by M. Hub. + */ + class OTBMaskAccessor: 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; + + legacy::StructureLegacy _legacyStructure; + + IDType _maskUID; + + + public: + + ~OTBMaskAccessor(); + + // import of structure sets (loading from data) is done elsewhere. Structures are only voxelized here. + // here the original RTToolbox voxelization shall be implemented + OTBMaskAccessor(StructTypePointer aStructurePointer, const core::GeometricInfo& aGeometricInfo); + + /*! @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. + * 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; + + bool getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const; + + /* @ brief is true if dose 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; + }; + + /*! @brief give access to GeometricInfo*/ + inline const core::GeometricInfo& getGeometricInfo() const + { + return _geoInfo; + }; + + IDType getMaskUID() const + { + return _maskUID; + }; + + }; + } +} + +#endif diff --git a/code/masks/rttbPolygonInfo_LEGACY.cpp b/code/masks/rttbPolygonInfo_LEGACY.cpp new file mode 100644 index 0000000..2bcd98a --- /dev/null +++ b/code/masks/rttbPolygonInfo_LEGACY.cpp @@ -0,0 +1,3966 @@ +// ----------------------------------------------------------------------- +// 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 "rttbMask.h" +#include "rttbPolygonInfo_LEGACY.h" +#include "rttbNullPointerException.h" + +namespace rttb{ + namespace masks{ + namespace legacy{ + + PolygonInfo::PolygonInfo( const LegacyPolygonType& the_polygon_in, int polygon_number_in, field_content_type brightness_outside_in , field_content_type brightness_inside_in , FieldOfScalar* MaskFieldReceived_in , const PolygonInTermsOfIntersections& Polygon_Intersections_In , const LegacyDoseVoxelIndex3D& aDoseIndex , core::GeometricInfo& GInfIn , const IntersectionsThisVoxel& intersections_this_voxel_in ): the_polygon( the_polygon_in ) , voxel_intersections( intersections_this_voxel_in) { + + if( MaskFieldReceived_in == NULL ){ + assert( 0 ); + } + else{ + MaskFieldReceived = MaskFieldReceived_in; + } + brightness_outside = brightness_outside_in; + + brightness_inside = brightness_inside_in; + + SetDoseIndex( aDoseIndex ); + + pol_inf_vec.clear(); + + PolygonInfoVectorOfEdges& pol_inf_vec_ref = pol_inf_vec; + + GInformation = GInfIn; + + SetCurrentPosition( aDoseIndex ); + + it_poly.SetIntersectionsAndResetIntersectionIndex( Polygon_Intersections_In ); + + voxel_intersections.SetPolygonNumber( polygon_number_in ); + } + + void PolygonInfo::CalcSnippetVectors(){ + VoxelIntersectionIndex next_voxel_intersection; + + pol_inf_vec.resize(0); + + for( int voxel_edge = 0 ; voxel_edge < 4 ; voxel_edge++ ){ + + next_voxel_intersection.reset(); + + current_edge = voxel_edge; + + snippet_vector snip_vec; + + snippet_vector& snip_vec_ref = snip_vec; + + SetCurrentPositionToEdge( voxel_edge ); + + std::vector snip_vec_local; + snippet_vector& snip_vec_ref_local = snip_vec_local; + + bool finished = false; + + while( finished == false ){ + finished = AppendNextIntersectionAndCorrespoindingPolygonSnippets( snip_vec_ref_local, snip_vec_ref , voxel_edge, next_voxel_intersection ); + } + + pol_inf_vec.push_back( snip_vec ); + } + + AppendEdges( pol_inf_vec ); + + it_poly.ResetIntersectionIndex(); + } + + void PolygonInfo::ShowSnippet( PolygonSnippet a_snippet ){ + std::cout<< " a_snippet.i_have_been_processed : " << a_snippet.i_have_been_processed <GetData( 0, 0, 0 ) < 0 ) + { + gone_well = false; + std::cout<< " Mask field values must not be below zero ! " <( pol_snip_a.point_of_interest_start.x ) != static_cast( pol_snip_b.point_of_interest_start.x ) ) + { + return false; + } + if( static_cast( pol_snip_a.point_of_interest_start.y ) != static_cast( pol_snip_b.point_of_interest_start.y ) ) + { + return false; + } + if( static_cast( pol_snip_a.point_of_interest_end.y ) != static_cast( pol_snip_b.point_of_interest_end.y ) ) + { + return false; + } + if( static_cast( pol_snip_a.why_here.x ) != static_cast( pol_snip_b.why_here.x ) ) + { + return false; + } + if( static_cast( pol_snip_a.why_here.y ) != static_cast( pol_snip_b.why_here.y ) ) + { + return false; + } + if( pol_snip_a.snippet_intermediate_content.size() != pol_snip_b.snippet_intermediate_content.size() ) + { + return false; + } + + for( GridIndexType counter = 0 ; counter < pol_snip_a.snippet_intermediate_content.size() ; counter++ ){ + if( static_cast( pol_snip_a.snippet_intermediate_content.at(counter).x ) != static_cast( pol_snip_b.snippet_intermediate_content.at(counter).x ) ) + { + return false; + } + if( static_cast( pol_snip_a.snippet_intermediate_content.at(counter).y ) != static_cast( pol_snip_b.snippet_intermediate_content.at(counter).y ) ) + { + return false; + } + if( static_cast( pol_snip_a.snippet_intermediate_content.at(counter).z ) != static_cast( pol_snip_b.snippet_intermediate_content.at(counter).z ) ) + { + return false; + } + } + return true; + } + + + bool PolygonInfo::CompareCalculatedAndExpectedResultingSequenceForTest( LegacyPolygonSequenceType& expected_resulting_sequence_ref ){ + if( expected_resulting_sequence_ref.size() > resulting_sequence.size() ) + { + std::cout<< " Size ! " <(the_dose_index.x); + current_position.y = static_cast(the_dose_index.y); + break; + case 1 : + current_position.x =static_cast(the_dose_index.x + 1); + current_position.y = static_cast(the_dose_index.y); + break; + case 2 : + current_position.x =static_cast(the_dose_index.x + 1); + current_position.y = static_cast(the_dose_index.y + 1); + break; + case 3 : + current_position.x =static_cast(the_dose_index.x); + current_position.y = static_cast(the_dose_index.y + 1); + break; + default : + assert( 0 ); + } + } + + void PolygonInfo::AppendEdges( PolygonInfoVectorOfEdges& pol_inf_vec_ref ){ + for( unsigned int voxel_edge = 0 ; voxel_edge < pol_inf_vec_ref.size() ; voxel_edge++ ){ + + assert( voxel_edge <= 3 ); + + PolygonSnippet pol_snip_front; + pol_snip_front.is_edge = 1; + pol_snip_front.characterize = corner; + + switch( voxel_edge ){ + case 0 : + pol_snip_front.point_of_interest_start.x =static_cast(the_dose_index.x); + pol_snip_front.point_of_interest_start.y = static_cast(the_dose_index.y); + pol_snip_front.point_of_interest_end.x =static_cast(the_dose_index.x); + pol_snip_front.point_of_interest_end.y = static_cast(the_dose_index.y); + pol_snip_front.why_here.x =static_cast(the_dose_index.x); + pol_snip_front.why_here.y = static_cast(the_dose_index.y); + break; + case 1 : + pol_snip_front.point_of_interest_start.x =static_cast(the_dose_index.x + 1); + pol_snip_front.point_of_interest_start.y = static_cast(the_dose_index.y); + pol_snip_front.point_of_interest_end.x =static_cast(the_dose_index.x + 1); + pol_snip_front.point_of_interest_end.y = static_cast(the_dose_index.y); + pol_snip_front.why_here.x =static_cast(the_dose_index.x + 1); + pol_snip_front.why_here.y = static_cast(the_dose_index.y); + break; + case 2 : + pol_snip_front.point_of_interest_start.x =static_cast(the_dose_index.x + 1); + pol_snip_front.point_of_interest_start.y = static_cast(the_dose_index.y + 1); + pol_snip_front.point_of_interest_end.x =static_cast(the_dose_index.x + 1); + pol_snip_front.point_of_interest_end.y = static_cast(the_dose_index.y + 1); + pol_snip_front.why_here.x =static_cast(the_dose_index.x + 1); + pol_snip_front.why_here.y = static_cast(the_dose_index.y + 1); + break; + case 3 : + pol_snip_front.point_of_interest_start.x =static_cast(the_dose_index.x); + pol_snip_front.point_of_interest_start.y = static_cast(the_dose_index.y + 1); + pol_snip_front.point_of_interest_end.x =static_cast(the_dose_index.x); + pol_snip_front.point_of_interest_end.y = static_cast(the_dose_index.y + 1); + pol_snip_front.why_here.x =static_cast(the_dose_index.x); + pol_snip_front.why_here.y = static_cast(the_dose_index.y + 1); + break; + default : + assert( 0 ); + } + + snippet_vector_iterator iter; + iter = pol_inf_vec_ref.at( voxel_edge ).begin(); + pol_inf_vec_ref.at( voxel_edge ).insert( iter , pol_snip_front ); + } + CheckEdgeIntersections(); + } + + void PolygonInfo::CheckEdgeIntersections(){ + SnippetIndex edge_index; + + edge_index.edge = 0; + edge_index.snip = 0; + current_edge = 0; + + CheckEdegeTouchCharacteristicsAndAppendIntermediateContent( edge_index ); + + edge_index.edge = 1; + edge_index.snip = 0; + current_edge = 1; + + CheckEdegeTouchCharacteristicsAndAppendIntermediateContent( edge_index ); + + edge_index.edge = 2; + edge_index.snip = 0; + current_edge = 2; + + CheckEdegeTouchCharacteristicsAndAppendIntermediateContent( edge_index ); + + edge_index.edge = 3; + edge_index.snip = 0; + current_edge = 3; + CheckEdegeTouchCharacteristicsAndAppendIntermediateContent( edge_index ); + } + + + bool PolygonInfo::GetCornerFromVoxelIntersections( unsigned int the_index, SnippetIndex edge_index , IterativePolygonInTermsOfIntersections::WhichIntersection& intersect_index ){ + if( voxel_intersections.corner_intersections_intersection_coord.at( edge_index.edge ).size() > the_index ) + { + intersect_index = voxel_intersections.corner_intersections_intersection_coord.at(edge_index.edge).at( the_index ); + return true; + } + else{ + return false; + } + } + + int PolygonInfo::GetNumberOfSnppetsThisEdge( SnippetIndex edge_index ){ + return voxel_intersections.corner_intersections_intersection_coord.at( edge_index.edge ).size(); + } + + + bool PolygonInfo::SetToNextIntersectonAlongThisEdgeAndReturnDistanceBasedOnVoxelIntersections( LegacyWorldCoordinate2D& closest_local_current_position_ref , int current_edge, VoxelIntersectionIndex next_voxel_intersection ){ + if( voxel_intersections.edge_intersections_intersection_coord.at( current_edge ).size() > next_voxel_intersection.next_intersection ) + { + if( voxel_intersections.edge_intersections_intersection_coord.at( current_edge ).at( next_voxel_intersection.next_intersection ).size() > next_voxel_intersection.intersection_douplication ) + { + IterativePolygonInTermsOfIntersections::WhichIntersection an_intersection_index = voxel_intersections.edge_intersections_intersection_coord.at(current_edge).at(next_voxel_intersection.next_intersection).at( next_voxel_intersection.intersection_douplication ); + + it_poly.SetCurrentIntersectionIndex( an_intersection_index ); + + it_poly.ThisIntersection( closest_local_current_position_ref ); + + if( !it_poly.CheckCurrentIntersectionIndexIdentical( an_intersection_index ) ) + { + assert(0); + } + return true; + } + else{ + return false; + } + } + else{ + return false; + } + } + + void PolygonInfo::CheckEdegeTouchCharacteristicsAndAppendIntermediateContent( SnippetIndex edge_index ){ + + LegacyWorldCoordinate2D edge_coord = pol_inf_vec.at( edge_index.edge ).at( edge_index.snip ).why_here; + + IterativePolygonInTermsOfIntersections::WhichIntersection the_intersection_index_from_voxel_intersections; + the_intersection_index_from_voxel_intersections.point_index = 0; + the_intersection_index_from_voxel_intersections.intersection_index = 0; + IterativePolygonInTermsOfIntersections::WhichIntersection& the_intersection_index_from_voxel_intersections_ref = the_intersection_index_from_voxel_intersections; + + int number_of_snippets_this_edge = GetNumberOfSnppetsThisEdge( edge_index ); + + std::vector snip_vec_local; + + + if( number_of_snippets_this_edge > 0 ) + { + snip_vec_local = CreateSnippetVector( number_of_snippets_this_edge , pol_inf_vec.at( edge_index.edge ).at( edge_index.snip ) ); + } + int erased = 0; + + for( int which_one = 0 ; which_one < number_of_snippets_this_edge ; which_one++ ){ + bool there_is_an_intersection = GetCornerFromVoxelIntersections( which_one, edge_index , the_intersection_index_from_voxel_intersections_ref ); + + if( there_is_an_intersection ) + { + it_poly.SetCurrentIntersectionIndex( the_intersection_index_from_voxel_intersections_ref ); + + LegacyWorldCoordinate3D contour_point_one = it_poly.GetRestpectivePologonPointInVoxelCoordinates( the_intersection_index_from_voxel_intersections_ref ); + + int back = 1; + + while( ( contour_point_one.x == edge_coord.x ) && ( contour_point_one.y == edge_coord.y ) ){ + contour_point_one = it_poly.GetRestpectivePologonPointInVoxelCoordinatesPrevious( the_intersection_index_from_voxel_intersections_ref , back ); + + back++; + } + + int ahead = 1; + + LegacyWorldCoordinate3D contour_point_two = it_poly.GetRestpectivePologonPointInVoxelCoordinatesFurther( the_intersection_index_from_voxel_intersections_ref , ahead ); + + while( ( contour_point_two.x == edge_coord.x ) && ( contour_point_two.y == edge_coord.y ) ){ + ahead++; + + contour_point_two = it_poly.GetRestpectivePologonPointInVoxelCoordinatesFurther( the_intersection_index_from_voxel_intersections_ref , ahead ); + } + + bool there_is_a_suitable_polygon_forward = false; + bool there_is_a_suitable_polygon_backward = false; + + PolygonSnippet forward_snippet; + PolygonSnippet& forward_snippet_ref = forward_snippet; + PolygonSnippet backward_snippet; + PolygonSnippet& backward_snippet_ref = backward_snippet; + + PolygonSnippet& edge_snippet_ref = snip_vec_local.at( ( which_one - erased ) ); + + float angle_charact_a = 0; + float angle_charact_b = 0; + + if( GoForwardAndCreatePolygonSnippet( forward_snippet_ref ) ) + { + there_is_a_suitable_polygon_forward = true; + } + + if( GoBackwardAndCreatePolygonSnippet( backward_snippet_ref ) ) + { + there_is_a_suitable_polygon_backward = true; + } + + if( there_is_a_suitable_polygon_forward ) + { + angle_charact_a = forward_snippet_ref.angle_charact; + } + if( there_is_a_suitable_polygon_backward ) + { + angle_charact_b = backward_snippet_ref.angle_charact; + } + if( ( ! there_is_a_suitable_polygon_forward ) && ( ! there_is_a_suitable_polygon_backward ) ) + { + edge_snippet_ref.characterize = corner; + } + if( ( there_is_a_suitable_polygon_forward ) && ( there_is_a_suitable_polygon_backward ) ) + { + WorkWithForwardAndBackwardEdgeSnippet( which_one, angle_charact_a , angle_charact_b , forward_snippet_ref , backward_snippet_ref , edge_snippet_ref , snip_vec_local , erased ); + } + if( ( there_is_a_suitable_polygon_forward ) && ( ! there_is_a_suitable_polygon_backward ) ) + { + WorkWithForwardEdgeSnippet( edge_coord, contour_point_one, which_one, angle_charact_a , angle_charact_b , forward_snippet_ref , backward_snippet_ref , edge_snippet_ref , snip_vec_local , erased ); + } + if( ( ! there_is_a_suitable_polygon_forward ) && ( there_is_a_suitable_polygon_backward ) ) + { + WorkWithBackwardEdgeSnippet( edge_coord, contour_point_two , which_one, angle_charact_a , angle_charact_b , forward_snippet_ref , backward_snippet_ref , edge_snippet_ref , snip_vec_local , erased ); + } + } + } + + if( snip_vec_local.size() > 1 ) + { + snip_vec_local = SortClockwise( snip_vec_local ); + } + InsertToSnippetVectorFront( snip_vec_local, edge_index ); + } + + void PolygonInfo::WorkWithForwardAndBackwardEdgeSnippet( int which_one, float& angle_charact_a , float& angle_charact_b , PolygonSnippet& forward_snippet_ref , PolygonSnippet& backward_snippet_ref , PolygonSnippet& edge_snippet_ref , std::vector& snip_vec_local , int& erased ){ + if( angle_charact_a < angle_charact_b ) + { + Switch( forward_snippet_ref , backward_snippet_ref ); + float keep_angle_charact_a = angle_charact_a; + float keep_angle_charact_b = angle_charact_b; + angle_charact_a = keep_angle_charact_b; + angle_charact_b = keep_angle_charact_a; + } + + if( ( angle_charact_a > 0 ) && ( angle_charact_a < 1.0 ) ) + { + if( ( angle_charact_b > 0 ) && ( angle_charact_b < 1.0 ) ) + { + forward_snippet_ref.characterize = inside_inside_touches; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + + backward_snippet_ref.characterize = inside_inside_touches; + backward_snippet_ref.why_here.x =backward_snippet_ref.point_of_interest_start.x; + backward_snippet_ref.why_here.y = backward_snippet_ref.point_of_interest_start.y; + backward_snippet_ref.is_edge = true; + + snippet_vector_iterator iter; + + iter = snip_vec_local.begin() + ( which_one - erased ) ; + snip_vec_local.insert( iter , backward_snippet_ref ); + } + else if( angle_charact_b == 0 ) + { + forward_snippet_ref.characterize = edge_to_inside; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + } + else if( angle_charact_b == 1.0 ) + { + assert(0); + } + else if( angle_charact_b < 0 ) + { + forward_snippet_ref.characterize = real_intersection; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + } + } + else if( angle_charact_a < 0 ) + { + assert( angle_charact_b < 0 ); + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_a == 1.0 ) + { + if( ( angle_charact_b > 0 ) && ( angle_charact_b < 1.0 ) ) + { + backward_snippet_ref.is_edge = true; + backward_snippet_ref.characterize = inside_to_edge; + backward_snippet_ref.why_here.x =backward_snippet_ref.point_of_interest_start.x; + backward_snippet_ref.why_here.y = backward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , backward_snippet_ref ); + } + else if( angle_charact_b == 1.0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_b == 0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_b < 0 ) + { + forward_snippet_ref.characterize = outside_to_edge; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + } + } + } + + void PolygonInfo::WorkWithForwardEdgeSnippet( LegacyWorldCoordinate2D edge_coord, LegacyWorldCoordinate3D contour_point_one, int which_one, float& angle_charact_a , float& angle_charact_b , PolygonSnippet& forward_snippet_ref , PolygonSnippet& backward_snippet_ref , PolygonSnippet& edge_snippet_ref , std::vector& snip_vec_local , int& erased ){ + float angle_charact = 0; + float& angle_charact_ref = angle_charact; + LegacyWorldCoordinate2D contour_point_one_2d; + contour_point_one_2d.x =contour_point_one.x; + contour_point_one_2d.y = contour_point_one.y; + + GetAngle( contour_point_one_2d , edge_coord , angle_charact_ref ); + + if( ( angle_charact_a > 0 ) && ( angle_charact_a < 1.0 ) ) + { + if( ( angle_charact_ref != 0 ) && ( angle_charact_ref != 1.0 ) ) + { + forward_snippet_ref.characterize = real_intersection; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + } + else if( angle_charact_ref == 0 ) + { + forward_snippet_ref.characterize = edge_to_inside; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + } + else if( angle_charact_ref == 1.0 ) + { + forward_snippet_ref.characterize = inside_to_edge ; + forward_snippet_ref.is_edge = true; + forward_snippet_ref.why_here.x =forward_snippet_ref.point_of_interest_start.x; + forward_snippet_ref.why_here.y = forward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , forward_snippet_ref ); + } + } + else if( angle_charact_a == 0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_a == 1.0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_a < 0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + } + + void PolygonInfo::WorkWithBackwardEdgeSnippet( LegacyWorldCoordinate2D edge_coord, LegacyWorldCoordinate3D contour_point_two , int which_one, float& angle_charact_a , float& angle_charact_b , PolygonSnippet& forward_snippet_ref , PolygonSnippet& backward_snippet_ref , PolygonSnippet& edge_snippet_ref , std::vector& snip_vec_local , int& erased ){ + float angle_charact = 0; + float& angle_charact_ref = angle_charact; + LegacyWorldCoordinate2D contour_point_two_2d; + contour_point_two_2d.x = contour_point_two.x; + contour_point_two_2d.y = contour_point_two.y; + + GetAngle( contour_point_two_2d , edge_coord , angle_charact_ref ); + + if( ( angle_charact_b > 0 ) && ( angle_charact_b < 1.0 ) ) + { + if( ( angle_charact_ref != 0 ) && ( angle_charact_ref != 1.0 ) ) + { + backward_snippet_ref.characterize = real_intersection; + backward_snippet_ref.is_edge = true; + backward_snippet_ref.why_here.x = backward_snippet_ref.point_of_interest_start.x; + backward_snippet_ref.why_here.y = backward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , backward_snippet_ref ); + } + if( ( angle_charact_ref == 1.0 ) ) + { + backward_snippet_ref.characterize = inside_to_edge; + backward_snippet_ref.is_edge = true; + backward_snippet_ref.why_here.x = backward_snippet_ref.point_of_interest_start.x; + backward_snippet_ref.why_here.y = backward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , backward_snippet_ref ); + } + if( ( angle_charact_ref == 0 ) ) + { + backward_snippet_ref.characterize = edge_to_inside; + backward_snippet_ref.is_edge = true; + backward_snippet_ref.why_here.x = backward_snippet_ref.point_of_interest_start.x; + backward_snippet_ref.why_here.y = backward_snippet_ref.point_of_interest_start.y; + + CopySnippet( edge_snippet_ref , backward_snippet_ref ); + } + + } + else if( angle_charact_b == 0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_b == 1.0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + else if( angle_charact_b < 0 ) + { + int position = ( which_one - erased ); + + RemoveFromLocalSnippetVector( snip_vec_local , position ); + + erased++; + } + } + + PolygonInfo::snippet_vector PolygonInfo::CreateSnippetVector( int number_of_snippets_this_edge , PolygonSnippet template_snip ){ + snippet_vector vector_local; + + PolygonSnippet pol_snip; + + for( int i = 0 ; i < number_of_snippets_this_edge ; i++ ){ + CopySnippet( pol_snip, template_snip ); + + vector_local.push_back( pol_snip ); + } + return vector_local; + } + + PolygonInfo::snippet_vector PolygonInfo::SortClockwise( snippet_vector snip_vec_local ){ + snippet_vector snip_vec_local_internal; + std::vector do_check; +do_check.clear(); + + for( GridIndexType i = 0 ; i < snip_vec_local.size() ; i++ ){ + + LegacyWorldCoordinate2D position_one = snip_vec_local.at( i ).point_of_interest_end; + + double distance_one = GetDistanceAlongEdge( the_dose_index , position_one ); + + LegacyWorldCoordinate2D position_for_reference = snip_vec_local.at( i ).why_here; + + double distance_for_reference = GetDistanceAlongEdge( the_dose_index , position_for_reference ); + + double rang_one = GetClockwiseDist( distance_for_reference , distance_one ); + + bool got_it = false; + + for( GridIndexType j = 0 ; j < snip_vec_local_internal.size() ; j++ ){ + + if( do_check.at(j) == true ) + { + LegacyWorldCoordinate2D position_two = snip_vec_local_internal.at( j ).point_of_interest_end; + + double distance_two = GetDistanceAlongEdge( the_dose_index , position_two ); + + double rang_two = GetClockwiseDist( distance_for_reference , distance_two ); + + if( rang_one < rang_two ) + { + snippet_vector_iterator iter; + iter = snip_vec_local_internal.begin(); + iter += j; + snip_vec_local_internal.insert( iter , snip_vec_local.at( i ) ); + got_it = true; + + bool check = true; + do_check.push_back( check ); + + if( ( snip_vec_local.at( i ).characterize == 6 ) && ( ( i + 1 ) < snip_vec_local.size() ) ) + { + if( snip_vec_local.at( i + 1 ).characterize == 6 ) + { + snippet_vector_iterator iter; + iter = snip_vec_local_internal.begin(); + iter += j; + snip_vec_local_internal.insert( iter , snip_vec_local.at( i + 1 ) ); + i++; + got_it = true; + + bool check = false; + do_check.push_back( check ); + } + } + j = snip_vec_local.size(); + } + } + } + if( got_it == false ) + { + snip_vec_local_internal.push_back( snip_vec_local.at( i ) ); + + bool check = true; + do_check.push_back( check ); + + if( ( snip_vec_local.at( i ).characterize == 6 ) && ( ( i + 1 ) < snip_vec_local.size() ) ) + { + if( snip_vec_local.at( i + 1 ).characterize == 6 ) + { + snip_vec_local_internal.push_back( snip_vec_local.at( i + 1 ) ); + i++; + got_it = true; + + bool check = false; + do_check.push_back( check ); + } + } + } + + } + return snip_vec_local_internal; + } + + void PolygonInfo::InsertToSnippetVectorFront( snippet_vector snip_vec_local , SnippetIndex edge_index ){ + snippet_vector_iterator iter; + + for( GridIndexType i = 0 ; i < snip_vec_local.size() ; i++ ){ + PolygonSnippet pol_snip_front = snip_vec_local.at( i ); + + iter = pol_inf_vec.at( edge_index.edge ).begin() + ( i + 1 ); + + pol_inf_vec.at( edge_index.edge ).insert( iter , pol_snip_front ); + } + + if( snip_vec_local.size() > 0 ) + { + iter = pol_inf_vec.at( edge_index.edge ).begin(); + pol_inf_vec.at( edge_index.edge ).erase( iter ); + } + } + + void PolygonInfo::RemoveFromLocalSnippetVector( snippet_vector& snip_vec_local , GridIndexType which_one ){ + snippet_vector_iterator iter; + + if( snip_vec_local.size() > which_one ) + { + iter = snip_vec_local.begin() + which_one; + snip_vec_local.erase( iter ); + } + } + + void PolygonInfo::GetSectorsAndSetCharacteristicsDoubleCheck( SnippetIndex edge_index, LegacyWorldCoordinate2D edge_coord , LegacyWorldCoordinate3D& contour_point_one , LegacyWorldCoordinate3D& contour_point_two ){ + PolygonSnippet& a_snippet_ref = pol_inf_vec.at( edge_index.edge ).at( edge_index.snip ); + + WhichSector sector_first_point = not_known; + WhichSector sector_second_point = not_known; + + WhichSector& sector_first_point_ref = sector_first_point; + WhichSector& sector_second_point_ref = sector_second_point; + + GetSectors( edge_coord , contour_point_one , contour_point_two , sector_first_point_ref , sector_second_point_ref ); + + if( edge_index.edge == 0 ) + { + CharacterizeIntersection charact_double_check = GetCharacteristicFirstCorner( sector_first_point_ref , sector_second_point_ref ); + if( ( a_snippet_ref.characterize != charact_double_check ) && ( a_snippet_ref.characterize != corner ) ) + { + std::cout<< " Have double checked the characteristic of corner zero. It turned out not to be determined correctly ! " < edge_coord.x ) && ( contour_point_one.y < edge_coord.y ) ) + { + sector_first_point_ref = section_twelve_to_three_o_clock; + return; + } + if( ( contour_point_one.x > edge_coord.x ) && ( contour_point_one.y == edge_coord.y ) ) + { + sector_first_point_ref = on_edge_three_o_clock; + return; + } + if( ( contour_point_one.x > edge_coord.x ) && ( contour_point_one.y > edge_coord.y ) ) + { + sector_first_point_ref = section_three_to_six_o_clock; + return; + } + if( ( contour_point_one.x == edge_coord.x ) && ( contour_point_one.y > edge_coord.y ) ) + { + sector_first_point_ref = on_edge_six_o_clock; + return; + } + if( ( contour_point_one.x < edge_coord.x ) && ( contour_point_one.y > edge_coord.y ) ) + { + sector_first_point_ref = section_six_to_nine_o_clock; + return; + } + if( ( contour_point_one.x < edge_coord.x ) && ( contour_point_one.y == edge_coord.y ) ) + { + sector_first_point_ref = on_edge_nine_o_clock; + return; + } + if( ( contour_point_one.x < edge_coord.x ) && ( contour_point_one.y < edge_coord.y ) ) + { + sector_first_point_ref = section_nine_to_twelve_o_clock; + return; + } + } + + void PolygonInfo::GetSectorSecondPoint( LegacyWorldCoordinate2D edge_coord , LegacyWorldCoordinate3D& contour_point_two, WhichSector& sector_second_point_ref ){ + if( ( contour_point_two.x == edge_coord.x ) && ( contour_point_two.y < edge_coord.y ) ) + { + sector_second_point_ref = on_edge_twelve_o_clock; + return; + } + if( ( contour_point_two.x > edge_coord.x ) && ( contour_point_two.y < edge_coord.y ) ) + { + sector_second_point_ref = section_twelve_to_three_o_clock; + return; + } + if( ( contour_point_two.x > edge_coord.x ) && ( contour_point_two.y == edge_coord.y ) ) + { + sector_second_point_ref = on_edge_three_o_clock; + return; + } + if( ( contour_point_two.x > edge_coord.x ) && ( contour_point_two.y > edge_coord.y ) ) + { + sector_second_point_ref = section_three_to_six_o_clock; + return; + } + if( ( contour_point_two.x == edge_coord.x ) && ( contour_point_two.y > edge_coord.y ) ) + { + sector_second_point_ref = on_edge_six_o_clock; + return; + } + if( ( contour_point_two.x < edge_coord.x ) && ( contour_point_two.y > edge_coord.y ) ) + { + sector_second_point_ref = section_six_to_nine_o_clock; + return; + } + if( ( contour_point_two.x < edge_coord.x ) && ( contour_point_two.y == edge_coord.y ) ) + { + sector_second_point_ref = on_edge_nine_o_clock; + return; + } + if( ( contour_point_two.x < edge_coord.x ) && ( contour_point_two.y < edge_coord.y ) ) + { + sector_second_point_ref = section_nine_to_twelve_o_clock; + return; + } + } + + CharacterizeIntersection PolygonInfo::GetCharacteristicFirstCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ){ + CharacterizeIntersection charact = unknown; + CharacterizeIntersection& charact_ref = charact; + + std::vector in_combination_vector; + std::vector& in_combination_vector_ref = in_combination_vector; + GetInCombinationFirstCorner( in_combination_vector_ref ); + + WhichSector inside; + WhichSector& inside_ref = inside; + GetInsideFirstCorner( inside_ref ); + + WhichSector preceding_edge; + WhichSector& preceding_edge_ref = preceding_edge; + GetPrecedingEdgeFirstCorner( preceding_edge_ref ); + + WhichSector following_edge; + WhichSector& following_edge_ref = following_edge; + GetFollowingEdgeFirstCorner( following_edge_ref ); + + DoCharacterize( sector_first_point_ref , sector_second_point_ref , charact_ref , in_combination_vector , inside_ref , preceding_edge_ref , following_edge_ref ); + + if( charact == unknown ) + { + assert( 0 ); + } + return charact; + } + + void PolygonInfo::DoCharacterize( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , CharacterizeIntersection& charact_ref , std::vector in_combination_vector , WhichSector& inside_ref , WhichSector& preceding_edge_ref , WhichSector& following_edge_ref ){ + if( BothOnEdge( sector_first_point_ref , sector_second_point_ref , preceding_edge_ref , following_edge_ref ) ) + { + charact_ref = edge_to_edge;} + else if( FromEdgeIn( sector_first_point_ref , sector_second_point_ref , preceding_edge_ref , inside_ref ) ) + { + charact_ref = edge_to_inside; + } + else if( FromEdgeOut( sector_first_point_ref , sector_second_point_ref , preceding_edge_ref , in_combination_vector ) ) + { + charact_ref = edge_to_outside; + } + else if( OutToEdge( sector_first_point_ref , sector_second_point_ref , following_edge_ref , in_combination_vector ) ) + { + charact_ref = outside_to_edge; + } + else if( InToEdge( sector_first_point_ref , sector_second_point_ref , following_edge_ref , inside_ref ) ) + { + charact_ref = inside_to_edge; + } + else if( BothIn( sector_first_point_ref , sector_second_point_ref , inside_ref ) ) + { + charact_ref = inside_inside_touches; + } + else if( BothOut( sector_first_point_ref , sector_second_point_ref , in_combination_vector ) ) + { + charact_ref = outside_outside_touches; + } + else if( OneInOneOut( sector_first_point_ref , sector_second_point_ref , inside_ref , in_combination_vector ) ) charact_ref = real_intersection; + else assert( 0 ); + } + + + void PolygonInfo::GetInCombinationFirstCorner( std::vector& in_combination_vector_ref ){ + + WhichSector sec_three_to_six = section_three_to_six_o_clock; + in_combination_vector_ref.push_back( sec_three_to_six ); + + WhichSector sec_on_edge_six = on_edge_six_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_six ); + + WhichSector sec_on_edge_three = on_edge_three_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_three ); + + } + + + void PolygonInfo::GetInsideFirstCorner( WhichSector& inside_ref ){ + inside_ref = section_three_to_six_o_clock; + } + + + void PolygonInfo::GetPrecedingEdgeFirstCorner( WhichSector& preceding_edge_ref ){ + preceding_edge_ref = on_edge_six_o_clock; + } + + + void PolygonInfo::GetFollowingEdgeFirstCorner( WhichSector& following_edge_ref ){ + following_edge_ref = on_edge_three_o_clock; + } + + + + bool PolygonInfo::BothOnEdge( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& preceding_edge_ref , WhichSector& following_edge_ref ){ + + std::vector first_sector_vector; + WhichSector sec_following_edge = following_edge_ref; + first_sector_vector.push_back( sec_following_edge ); + + std::vector second_sector_vector; + WhichSector sec_preceding = preceding_edge_ref; + second_sector_vector.push_back( sec_preceding ); + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_first_point_ref , first_sector_vector , sector_second_point_ref , second_sector_vector ); + bool it_is_b = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_second_point_ref , first_sector_vector , sector_first_point_ref , second_sector_vector ); + + if( it_is_a || it_is_b ) return true; + else return false; + + } + + + bool PolygonInfo::FromEdgeIn( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& preceding_edge_ref , WhichSector& inside_ref ){ + + std::vector first_sector_vector; + WhichSector sec_preceding = preceding_edge_ref; + first_sector_vector.push_back( sec_preceding ); + + std::vector second_sector_vector; + WhichSector sec_inside = inside_ref; + second_sector_vector.push_back( sec_inside ); + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_first_point_ref , first_sector_vector , sector_second_point_ref , second_sector_vector ); + bool it_is_b = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_second_point_ref , first_sector_vector , sector_first_point_ref , second_sector_vector ); + + if( it_is_a || it_is_b ) return true; + else return false; + + } + + + + bool PolygonInfo::FromEdgeOut( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& preceding_edge_ref , std::vector second_sector_vector ){ + + std::vector first_sector_vector; + WhichSector sec_preceding = preceding_edge_ref; + first_sector_vector.push_back( sec_preceding ); + + /*std::vector second_sector_vector; + WhichSector sec_three_to_six = section_three_to_six_o_clock; + second_sector_vector.push_back( sec_three_to_six ); + + WhichSector sec_on_edge_six = on_edge_six_o_clock; + second_sector_vector.push_back( sec_on_edge_six ); + + WhichSector sec_on_edge_three = on_edge_three_o_clock; + second_sector_vector.push_back( sec_on_edge_three );*/ + + + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( sector_first_point_ref , first_sector_vector , sector_second_point_ref , second_sector_vector ); + bool it_is_b = FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( sector_second_point_ref , first_sector_vector , sector_first_point_ref , second_sector_vector ); + + if( it_is_a || it_is_b ) return true; + else return false; + + } + + + + + bool PolygonInfo::OutToEdge( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& following_edge_ref , std::vector second_sector_vector ){ + + std::vector first_sector_vector; + WhichSector sec_following = following_edge_ref; + first_sector_vector.push_back( sec_following ); + + /* std::vector second_sector_vector; + WhichSector sec_three_to_six = section_three_to_six_o_clock; + second_sector_vector.push_back( sec_three_to_six ); + + WhichSector sec_on_edge_six = on_edge_six_o_clock; + second_sector_vector.push_back( sec_on_edge_six ); + + WhichSector sec_on_edge_three = on_edge_three_o_clock; + second_sector_vector.push_back( sec_on_edge_three ); */ + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( sector_first_point_ref , first_sector_vector , sector_second_point_ref , second_sector_vector ); + bool it_is_b = FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( sector_second_point_ref , first_sector_vector , sector_first_point_ref , second_sector_vector ); + + if( it_is_a || it_is_b ) return true; + else return false; + + } + + + + + bool PolygonInfo::InToEdge( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& following_edge_ref , WhichSector& inside_ref ){ + + std::vector first_sector_vector; + WhichSector sec_following_edge = following_edge_ref; + first_sector_vector.push_back( sec_following_edge ); + + std::vector second_sector_vector; + WhichSector sec_inside = inside_ref; + second_sector_vector.push_back( sec_inside ); + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_first_point_ref , first_sector_vector , sector_second_point_ref , second_sector_vector ); + bool it_is_b = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_second_point_ref , first_sector_vector , sector_first_point_ref , second_sector_vector ); + + if( it_is_a || it_is_b ) return true; + else return false; + + } + + + + + bool PolygonInfo::BothIn( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& inside_ref ){ + + std::vector sector_vector; + WhichSector sec_inside = inside_ref; + sector_vector.push_back( sec_inside ); + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( sector_first_point_ref , sector_vector , sector_second_point_ref , sector_vector ); + + if( it_is_a ) return true; + else return false; + + } + + + bool PolygonInfo::BothOut( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , std::vector sector_vector ){ + + /*std::vector sector_vector; + WhichSector sec_three_to_six = section_three_to_six_o_clock; + sector_vector.push_back( sec_three_to_six ); + + WhichSector sec_on_edge_six = on_edge_six_o_clock; + sector_vector.push_back( sec_on_edge_six ); + + WhichSector sec_on_edge_three = on_edge_three_o_clock; + sector_vector.push_back( sec_on_edge_three );*/ + + bool it_is_a = FirstSectorIsNoneOfTheseAndSecondSectorIsNoneOfThose( sector_first_point_ref , sector_vector , sector_second_point_ref , sector_vector ); + + if( it_is_a ) return true; + else return false; + + + } + + + + bool PolygonInfo::OneInOneOut( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& inside_ref , std::vector first_sector_vector ){ + + /*std::vector first_sector_vector; + WhichSector sec_three_to_six = section_three_to_six_o_clock; + first_sector_vector.push_back( sec_three_to_six ); + + WhichSector sec_on_edge_six = on_edge_six_o_clock; + first_sector_vector.push_back( sec_on_edge_six ); + + WhichSector sec_on_edge_three = on_edge_three_o_clock; + first_sector_vector.push_back( sec_on_edge_three );*/ + + std::vector second_sector_vector; + WhichSector sec_inside = inside_ref; + second_sector_vector.push_back( sec_inside ); + + bool it_is_a = FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( sector_first_point_ref , second_sector_vector , sector_second_point_ref , first_sector_vector ); + bool it_is_b = FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( sector_second_point_ref , second_sector_vector , sector_first_point_ref , first_sector_vector ); + + if( it_is_a || it_is_b ) return true; + else return false; + + } + + + + + bool PolygonInfo::FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( WhichSector& sector_first_point_ref , std::vector first_sector_vector , WhichSector& sector_second_point_ref , std::vector second_sector_vector ){ + + bool first_one_of_these = false; + bool second_one_of_those = false; + + for( unsigned int index = 0 ; index < first_sector_vector.size() ; index++ ){ + if( first_sector_vector.at( index ) == sector_first_point_ref ) first_one_of_these = true; + } + + for( unsigned int index = 0 ; index < second_sector_vector.size() ; index++ ){ + if( second_sector_vector.at( index ) == sector_second_point_ref ) second_one_of_those = true; + } + + if( first_one_of_these && second_one_of_those )return true; + else return false; + + } + + + + + bool PolygonInfo::FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( WhichSector& sector_first_point_ref , std::vector first_sector_vector , WhichSector& sector_second_point_ref , std::vector second_sector_vector ){ + + bool first_one_of_these = false; + bool second_none_of_those = true; + + for( unsigned int index = 0 ; index < first_sector_vector.size() ; index++ ){ + if( first_sector_vector.at( index ) == sector_first_point_ref ) first_one_of_these = true; + } + + for( unsigned int index = 0 ; index < second_sector_vector.size() ; index++ ){ + if( second_sector_vector.at( index ) == sector_second_point_ref ) second_none_of_those = false; + } + + if( first_one_of_these && second_none_of_those )return true; + else return false; + + } + + + + + bool PolygonInfo::FirstSectorIsNoneOfTheseAndSecondSectorIsNoneOfThose( WhichSector& sector_first_point_ref , std::vector first_sector_vector , WhichSector& sector_second_point_ref , std::vector second_sector_vector ){ + + bool first_none_of_these = true; + bool second_none_of_those = true; + + for( unsigned int index = 0 ; index < first_sector_vector.size() ; index++ ){ + if( first_sector_vector.at( index ) == sector_first_point_ref ) first_none_of_these = false; + } + + for( unsigned int index = 0 ; index < second_sector_vector.size() ; index++ ){ + if( second_sector_vector.at( index ) == sector_second_point_ref ) second_none_of_those = false; + } + + if( first_none_of_these && second_none_of_those )return true; + else return false; + + } + + + + CharacterizeIntersection PolygonInfo::GetCharacteristicSecondCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ){ + + CharacterizeIntersection charact = unknown; + CharacterizeIntersection& charact_ref = charact; + + std::vector in_combination_vector; + std::vector& in_combination_vector_ref = in_combination_vector; + GetInCombinationSecondCorner( in_combination_vector_ref ); + + WhichSector inside; + WhichSector& inside_ref = inside; + GetInsideSecondCorner( inside_ref ); + + WhichSector preceding_edge; + WhichSector& preceding_edge_ref = preceding_edge; + GetPrecedingEdgeSecondCorner( preceding_edge_ref ); + + WhichSector following_edge; + WhichSector& following_edge_ref = following_edge; + GetFollowingEdgeSecondCorner( following_edge_ref ); + + DoCharacterize( sector_first_point_ref , sector_second_point_ref , charact_ref , in_combination_vector , inside_ref , preceding_edge_ref , following_edge_ref ); + + if( charact == unknown ) assert( 0 ); + + return charact; + + } + + + + + void PolygonInfo::GetInCombinationSecondCorner( std::vector& in_combination_vector_ref ){ + + WhichSector sec_six_to_nine = section_six_to_nine_o_clock; + in_combination_vector_ref.push_back( sec_six_to_nine ); + + WhichSector sec_on_edge_nine = on_edge_nine_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_nine ); + + WhichSector sec_on_edge_six = on_edge_six_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_six ); + + } + + + + void PolygonInfo::GetInsideSecondCorner( WhichSector& inside_ref ){ + inside_ref = section_six_to_nine_o_clock; + } + + + + void PolygonInfo::GetPrecedingEdgeSecondCorner( WhichSector& preceding_edge_ref ){ + preceding_edge_ref = on_edge_nine_o_clock; + } + + + + void PolygonInfo::GetFollowingEdgeSecondCorner( WhichSector& following_edge_ref ){ + following_edge_ref = on_edge_six_o_clock; + } + + + + CharacterizeIntersection PolygonInfo::GetCharacteristicThirdCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ){ + + CharacterizeIntersection charact = unknown; + CharacterizeIntersection& charact_ref = charact; + + std::vector in_combination_vector; + std::vector& in_combination_vector_ref = in_combination_vector; + GetInCombinationThirdCorner( in_combination_vector_ref ); + + WhichSector inside; + WhichSector& inside_ref = inside; + GetInsideThirdCorner( inside_ref ); + + WhichSector preceding_edge; + WhichSector& preceding_edge_ref = preceding_edge; + GetPrecedingEdgeThirdCorner( preceding_edge_ref ); + + WhichSector following_edge; + WhichSector& following_edge_ref = following_edge; + GetFollowingEdgeThirdCorner( following_edge_ref ); + + DoCharacterize( sector_first_point_ref , sector_second_point_ref , charact_ref , in_combination_vector , inside_ref , preceding_edge_ref , following_edge_ref ); + + if( charact == unknown ) assert( 0 ); + + return charact; + + } + + + + void PolygonInfo::GetInCombinationThirdCorner( std::vector& in_combination_vector_ref ){ + + WhichSector sec_nine_to_twelve = section_nine_to_twelve_o_clock; + in_combination_vector_ref.push_back( sec_nine_to_twelve ); + + WhichSector sec_on_edge_twelve = on_edge_twelve_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_twelve ); + + WhichSector sec_on_edge_nine = on_edge_nine_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_nine ); + + } + + + void PolygonInfo::GetInsideThirdCorner( WhichSector& inside_ref ){ + inside_ref = section_nine_to_twelve_o_clock; + } + + + void PolygonInfo::GetPrecedingEdgeThirdCorner( WhichSector& preceding_edge_ref ){ + preceding_edge_ref = on_edge_twelve_o_clock; + } + + + void PolygonInfo::GetFollowingEdgeThirdCorner( WhichSector& following_edge_ref ){ + following_edge_ref = on_edge_nine_o_clock; + } + + + + CharacterizeIntersection PolygonInfo::GetCharacteristicFourthCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ){ + + CharacterizeIntersection charact = unknown; + CharacterizeIntersection& charact_ref = charact; + + std::vector in_combination_vector; + std::vector& in_combination_vector_ref = in_combination_vector; + GetInCombinationFourthCorner( in_combination_vector_ref ); + + WhichSector inside; + WhichSector& inside_ref = inside; + GetInsideFourthCorner( inside_ref ); + + WhichSector preceding_edge; + WhichSector& preceding_edge_ref = preceding_edge; + GetPrecedingEdgeFourthCorner( preceding_edge_ref ); + + WhichSector following_edge; + WhichSector& following_edge_ref = following_edge; + GetFollowingEdgeFourthCorner( following_edge_ref ); + + DoCharacterize( sector_first_point_ref , sector_second_point_ref , charact_ref , in_combination_vector , inside_ref , preceding_edge_ref , following_edge_ref ); + + if( charact == unknown ) assert( 0 ); + + return charact; + + } + + + void PolygonInfo::GetInCombinationFourthCorner( std::vector& in_combination_vector_ref ){ + + WhichSector sec_twelve_to_three = section_twelve_to_three_o_clock; + in_combination_vector_ref.push_back( sec_twelve_to_three ); + + WhichSector sec_on_edge_three = on_edge_three_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_three ); + + WhichSector sec_on_edge_twelve = on_edge_twelve_o_clock; + in_combination_vector_ref.push_back( sec_on_edge_twelve ); + + } + + + void PolygonInfo::GetInsideFourthCorner( WhichSector& inside_ref ){ + inside_ref = section_twelve_to_three_o_clock; + } + + + void PolygonInfo::GetPrecedingEdgeFourthCorner( WhichSector& preceding_edge_ref ){ + preceding_edge_ref = on_edge_three_o_clock; + } + + + void PolygonInfo::GetFollowingEdgeFourthCorner( WhichSector& following_edge_ref ){ + following_edge_ref = on_edge_twelve_o_clock; + } + + + + bool PolygonInfo::AppendNextIntersectionAndCorrespoindingPolygonSnippets( snippet_vector& snip_vec_ref_local , snippet_vector& snip_vec_ref , int current_edge, VoxelIntersectionIndex& next_voxel_intersection ){ + + bool finished = false; + bool im_done = false; + + LegacyWorldCoordinate2D closest_local_current_position; + LegacyWorldCoordinate2D& closest_local_current_position_ref = closest_local_current_position; + closest_local_current_position.x = current_position.x; + closest_local_current_position.y = current_position.y; + + while( im_done == false ){ + + closest_local_current_position.x = current_position.x; + closest_local_current_position.y = current_position.y; + + // double distance_current_position = GetDistanceAlongEdge( the_dose_index , current_position ); + + // die folgende Funktion macht den naechstliegenden Schnittpunkt ausfindig gespeichert in closest_local_current_position_ref + // the_intersection_index speichert die Information ueber seine Lage im Polygonzug + // hier wird untersucht, ob es einen naechsten Schnittpunkt gibt + + // float distance_next_intersection = SetToNextIntersectionAlongThisEdgeAndReturnDistance( closest_local_current_position_ref , distance_current_position , current_edge ); + + unsigned int the_index = 0; + SnippetIndex edge_index; + edge_index.edge = 0; + edge_index.snip = 0; + + bool got_one = 0; + + got_one = SetToNextIntersectonAlongThisEdgeAndReturnDistanceBasedOnVoxelIntersections( closest_local_current_position_ref, current_edge, next_voxel_intersection ); + + + //if( distance_next_intersection != ( 1000000 ) ){ // gibt es einen naechsten Schnittpunkt? + if( got_one ){ + + // Was der folgende Funktionsaufruf alles tut : + // Hier in AddSnippetsThisIntersection wird untersucht, ob zu dem gefundenen Schnittpunkt + // ein Polygon existiert das innerhalb des Voxels liegt, nur dann, wenn das der Fall ist, wird an snip_vec_ref angehaengt. + // Falls es einen geeigneten Schnittpunkt gibt, all die zugehoerigen + // Polygonsnippets anhaengen. + // Auch wird durch die Funktion AddSnippetsThisIntersection untersucht, ob man schon ausserhalb der betrachteten Kante ist. + + if( AddSnippetsThisIntersection( snip_vec_ref_local , closest_local_current_position ) ){ + + im_done = true; + finished = false; + // Falls Polygone gefunden im_done auf true und finished auf false setzen. + // Das bedeutet, an dieser Kante wird weitergearbeitet. Vielleicht gibt es noch mehr Schnittpunkte. + + } + + // falls kein drinnen liegendes Polygonstueck gefunden wurde, welches im Bereich der betrachteten Kante liegt + // bleibt im_done auf false und es wird weiter gesucht + // die current_position wird dann auf den zuletzt gefundenen Schnittpunkt gesetzt, zu dem es kein Polygonstueck gab + current_position.x = closest_local_current_position.x; + current_position.y = closest_local_current_position.y; + + + } + else{ + + // es gibt keinen weiteren Schnittpunkt mehr + im_done = true; + finished = true; + + } + + bool changed_loc = false; + + IncrementVoxelIntersectionIndex( current_edge, next_voxel_intersection , changed_loc ); + + if( changed_loc ){ + + if( snip_vec_ref_local.size() > 1 ) snip_vec_ref_local = SortClockwise( snip_vec_ref_local ); + + for( GridIndexType counter = 0 ; counter < snip_vec_ref_local.size() ; counter++ ){ + + snip_vec_ref.push_back( snip_vec_ref_local.at( counter ) ); + + } + + snip_vec_ref_local.resize( 0 ); + + } + + } + + return finished; + + } + + + void PolygonInfo::IncrementVoxelIntersectionIndex( int current_edge, VoxelIntersectionIndex& vox_inters_index , bool& changed_location ){ + + if( voxel_intersections.edge_intersections_intersection_coord.at( current_edge ).size() > vox_inters_index.next_intersection ){ + + if( voxel_intersections.edge_intersections_intersection_coord.at( current_edge ).at( vox_inters_index.next_intersection ).size() > ( vox_inters_index.intersection_douplication + 1 ) ){ + + vox_inters_index.intersection_douplication++; + + } + else{ + + vox_inters_index.intersection_douplication = 0; + vox_inters_index.next_intersection++; + changed_location = true; + + } + + } + + } + + + // Achtung, bei der verallgemeinerung, die Beruehrpunkte erlaubt, muss hier eine Veraenderung vor werden. + // Es muessen auch naechste Punkte mit Abstand wie der bisherige erlaubt sien. + // Anmerkung : ist insofern erledigt, dass diese Funktion hier nicht mehr verwendet wird. + float PolygonInfo::SetToNextIntersectionAlongThisEdgeAndReturnDistance( LegacyWorldCoordinate2D& closest_local_current_position_ref , float distance_current_position , int current_edge ){ + + // Hier wird im Uhrzeigersinn vorwaerts gegangen um den Schnittpunkt zu finden, der dem aktuell betrachteten Schnittpunkt + // am naechsten liegt. Im Uhrzeigersinn zurueck liegende Punkte werden dabei ignoriert. + // Natuerlich ist jeder Punkt sich selbst der Naechste. Selbst wird daher ignoriert. + // diese Funktion gibt -1000000 zurueck, falls kein Punkt gefunden werden konnte. + // Die Distanz wird im Uhrzeigersinn der Voxelkante entlang gemessen. + // Erkannt und gesucht wird nur was auf der betrachteten Voxelkante liegt. + // Es wird der Abstand zu current_position, eindimensional entlang der + // Voxelkante zurueckgegeben. + + float distance_current_position_local = 0; + double distance_next_edge = 0; + float min_local_distance = 1000000; + float min_local_distance_along_edge = 1000000; + + LegacyWorldCoordinate2D local_current_position; + local_current_position.x = current_position.x; + local_current_position.y = current_position.y; + + + + float local_distance; + local_distance = -1000000; + + it_poly.RememberPosition(); + + it_poly.ResetIntersectionIndex(); + + bool was_able_to_increment = true; + + + while( was_able_to_increment == true ){ + + // if( it_poly.CheckToBeRegarded() ){ + + + if( it_poly.ThisIntersection( local_current_position ) ){ + + if( IsTheSameEdgeButNoCorner( local_current_position , current_position , current_edge ) ){ + + + + distance_current_position_local = GetDistanceAlongEdge( the_dose_index , local_current_position ); + distance_next_edge = GetDistanceNextEdge( current_edge ); + + + if( ( static_cast( distance_current_position_local ) > static_cast( distance_current_position ) ) && ( static_cast( distance_next_edge ) > static_cast( distance_current_position_local ) ) ){ // betrachtet werden nur die Schnittpunkte, die im Uhrzeigersinn + // entlanng der Voxelkante weiter entfernt sind + // echt groesser schliesst Detektion des betracteten Punktes selbst aus + + + local_distance = GetPeriodicDist( distance_current_position_local , distance_current_position ); + + + if( local_distance < min_local_distance ){ + + min_local_distance = local_distance; + min_local_distance_along_edge = distance_current_position_local; + + closest_local_current_position_ref.x = local_current_position.x; + closest_local_current_position_ref.y = local_current_position.y; + + it_poly.RememberPosition(); + + } + + } + + } + + // } // check to be regarded + + } + + was_able_to_increment = it_poly.IncremtentIntersection(); + + } + + it_poly.JumpToRememberedPosition(); + return min_local_distance_along_edge; + + } + + + + + // Es ist noetig zu prufen, ob Ecke, wegen Rundungsfehlern. Hier muss auf genau die selbe Art festgestellt werden ob Ecke wie bei der unabhaengigen Betrachtung der Ecken. + // nein, das ist doch nicht noetig. Diese Funktion wird nicht mehr verwendet + bool PolygonInfo::IsTheSameEdgeButNoCorner( LegacyWorldCoordinate2D local_current_position , LegacyWorldCoordinate2D current_position , int current_edge ){ + + LegacyWorldCoordinate2D corner_coord; + corner_coord.x = static_cast( the_dose_index.x ); + corner_coord.y = static_cast( the_dose_index.y ); + + switch( current_edge ){ + + case 0 : + + if( ( local_current_position.y == current_position.y ) && ( local_current_position.x <= ( the_dose_index.x + 1 ) ) && ( local_current_position.x >= the_dose_index.x ) && ( the_dose_index.y == local_current_position.y ) ){ + + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + corner_coord.x += 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + return true; + + } + else return false; + break; + + case 1 : + + if( ( local_current_position.x == current_position.x ) && ( local_current_position.y <= ( the_dose_index.y + 1 ) ) && ( local_current_position.y >= the_dose_index.y ) && ( ( the_dose_index.x + 1 ) == local_current_position.x ) ){ + corner_coord.x += 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + corner_coord.y += 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + return true; + } + else return false; + break; + + case 2 : + + if( ( local_current_position.y == current_position.y ) && ( local_current_position.x <= ( the_dose_index.x + 1 ) ) && ( local_current_position.x >= the_dose_index.x ) && ( ( the_dose_index.y + 1 ) == local_current_position.y ) ){ + corner_coord.x += 1; + corner_coord.y += 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + corner_coord.x -= 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + return true; + } + else return false; + break; + + case 3 : + + if( ( local_current_position.x == current_position.x ) && ( local_current_position.y <= ( the_dose_index.y + 1 ) ) && ( local_current_position.y >= the_dose_index.y ) && ( the_dose_index.x == local_current_position.x ) ){ + corner_coord.y += 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + corner_coord.y -= 1; + if( ( static_cast( corner_coord.x ) == static_cast( local_current_position.x ) ) && ( static_cast( corner_coord.y ) == static_cast( local_current_position.y ) ) ) return false; + return true; + } + else return false; + break; + + default : + + assert( 0 ); + + } + return false; + } + + + + + + bool PolygonInfo::AddSnippetsThisIntersection( snippet_vector& snip_vec_ref , LegacyWorldCoordinate2D closest_local_current_position ){ + + bool there_is_a_suitable_polygon_forward = false; + bool there_is_a_suitable_polygon_backward = false; + + PolygonSnippet forward_snippet; + PolygonSnippet& forward_snippet_ref = forward_snippet; + PolygonSnippet backward_snippet; + PolygonSnippet& backward_snippet_ref = backward_snippet; + + + // ersten Punkt an beide Polygonschnipsel anhaengen + + // Dann beide Schnittpunktelisten untersuchen um festzustellen, welche Punkte zuletzt noch aufgenommen + // werden muss in dieses Polygonstueck. Das geschieht in der Vorwaertsrichtung sowie in der rueckwaertigen Richtung + // durch die folgende Funktion + + bool do_switch = false ; + + float angle_charact_a = 0; + float angle_charact_b = 0; + + if( GoForwardAndCreatePolygonSnippet( forward_snippet_ref ) ){ + there_is_a_suitable_polygon_forward = true; + } + + if( GoBackwardAndCreatePolygonSnippet( backward_snippet_ref ) ){ + there_is_a_suitable_polygon_backward = true; + } + + if( there_is_a_suitable_polygon_forward ){ + angle_charact_a = forward_snippet_ref.angle_charact; + } + if( there_is_a_suitable_polygon_backward ){ + angle_charact_b = backward_snippet_ref.angle_charact; + } + + + if( there_is_a_suitable_polygon_forward ){ + if( there_is_a_suitable_polygon_backward ){ + + + if( angle_charact_a < angle_charact_b ){ + Switch( forward_snippet_ref , backward_snippet_ref ); + } + + //if( ( JustEdge( forward_snippet_ref ) ) && ( JustEdge( backward_snippet_ref ) ) ){ + // nothing happens + //} + + if( ( JustEdge( forward_snippet_ref ) ) && ( ! JustEdge( backward_snippet_ref ) ) ){ + backward_snippet_ref.characterize = inside_to_edge; + backward_snippet.why_here = closest_local_current_position; + snip_vec_ref.push_back( backward_snippet ); + } + else if( ( ! JustEdge( forward_snippet_ref ) ) && ( JustEdge( backward_snippet_ref ) ) ){ + forward_snippet_ref.characterize = edge_to_inside; + forward_snippet.why_here = closest_local_current_position; + snip_vec_ref.push_back( forward_snippet ); + } + else if( ( ! JustEdge( forward_snippet_ref ) ) && ( ! JustEdge( backward_snippet_ref ) ) ){ + backward_snippet_ref.characterize = inside_inside_touches; + backward_snippet.why_here = closest_local_current_position; + snip_vec_ref.push_back( backward_snippet ); + forward_snippet_ref.characterize = inside_inside_touches; + forward_snippet.why_here = closest_local_current_position; + snip_vec_ref.push_back( forward_snippet ); + } + + } + + } + + + if( ( there_is_a_suitable_polygon_forward ) && ( ! there_is_a_suitable_polygon_backward ) ){ + + if( JustEdge( forward_snippet_ref ) ){ + + // nothing happens + + } + else{ + + forward_snippet_ref.characterize = real_intersection; + forward_snippet.why_here = closest_local_current_position; + snip_vec_ref.push_back( forward_snippet ); + + } + + } + + + if( ( ! there_is_a_suitable_polygon_forward ) && ( there_is_a_suitable_polygon_backward ) ){ + + if( JustEdge( backward_snippet_ref ) ){ + + } + else{ + + backward_snippet_ref.characterize = real_intersection; + backward_snippet_ref.why_here = closest_local_current_position; + snip_vec_ref.push_back( backward_snippet ); + + } + + } + + + + // Es ist zwar so, dass man das backward snippet, falls es nur ein snippet gibt nicht anhaengen muss, denn in diesem Fall + // ist das backward snippet bereits zum forward snippet geworden (und wird als forward snippet angehaengt). + // Falls es zwei snippets gibt wird nur das forward snippet angehaengt, und zwar das, das im Urzeigersinn richtig liegt. + // Im Zweifelsfall tauschen. + // Falls es nur ein forward snippet gibt wird es angehaengt, falls es im Urzeigersinn liegt. + + + // Dann muessen die Polygonstuecke an den snip_vec_ref dran gehaengt werden + // nur falls das Polygonstueck drin liegt anhaengen (GoForwardAndCreatePolygonSnippet GoBackwardAndCreatePolygonSnippet prueft das). + + if( ( there_is_a_suitable_polygon_forward ) || ( there_is_a_suitable_polygon_backward ) ) return true; + else return false; + + } + + + + bool PolygonInfo::JustEdge( PolygonSnippet& snippet_ref ){ + + bool just_edge = true; + + if( ( current_edge == 0 ) || ( current_edge == 2 ) ){ + for( unsigned int position = 0 ; position < snippet_ref.snippet_intermediate_content.size() ; position++ ){ + LegacyWorldCoordinate3D w_coord = snippet_ref.snippet_intermediate_content.at( position ); + if( static_cast( w_coord.y ) != static_cast( snippet_ref.point_of_interest_start.y ) ) just_edge = false; + } + if( static_cast( snippet_ref.point_of_interest_start.y ) != static_cast( snippet_ref.point_of_interest_end.y ) ) just_edge = false; + } + else if( ( current_edge == 1 ) || ( current_edge == 3 ) ){ + if( static_cast( snippet_ref.point_of_interest_start.x ) != static_cast( snippet_ref.point_of_interest_end.x ) ) just_edge = false; + for( unsigned int position = 0 ; position < snippet_ref.snippet_intermediate_content.size() ; position++ ){ + LegacyWorldCoordinate3D w_coord = snippet_ref.snippet_intermediate_content.at( position ); + if( static_cast( w_coord.x ) != static_cast( snippet_ref.point_of_interest_start.x ) ) just_edge = false; + } + } + else assert( 0 ); + + return just_edge; + + } + + + + void PolygonInfo::Switch( PolygonSnippet& snippet_one_ref , PolygonSnippet& snippet_two_ref ){ + + PolygonSnippet keep_snippet; + PolygonSnippet& keep_snippet_ref = keep_snippet; + CopySnippet( keep_snippet_ref , snippet_one_ref ); + + CopySnippet( snippet_one_ref, snippet_two_ref ); + CopySnippet( snippet_two_ref, keep_snippet_ref ); + + } + + + + + + void PolygonInfo::CopySnippet( PolygonSnippet& to_snippet, PolygonSnippet& snip_in ){ + + to_snippet.characterize = snip_in.characterize; + to_snippet.is_edge = snip_in.is_edge; + + to_snippet.why_here.x = snip_in.why_here.x; + to_snippet.why_here.y = snip_in.why_here.y; + + to_snippet.point_of_interest_start.x = snip_in.point_of_interest_start.x; + to_snippet.point_of_interest_start.y = snip_in.point_of_interest_start.y; + + to_snippet.point_of_interest_end.x = snip_in.point_of_interest_end.x; + to_snippet.point_of_interest_end.y = snip_in.point_of_interest_end.y; + + to_snippet.snippet_intermediate_content.clear(); + + for( unsigned int counter = 0 ; counter < snip_in.snippet_intermediate_content.size() ; counter++ ){ + to_snippet.snippet_intermediate_content.push_back( snip_in.snippet_intermediate_content.at( counter ) ); + } + + } + + + + + bool PolygonInfo::GoForwardAndCreatePolygonSnippet( PolygonSnippet& forward_snippet_ref ){ + + // hier muss als erstes geprueft werden, ob das Polygonsnippet ueberhaupt innerhalb des betrachteten Voxels liegt. + // Falls nein kann man alles folgende ueberspringen. + + float& angle_charact_forward = forward_snippet_ref.angle_charact; + + + + bool is_inside_valid = CheckPolygonIsInsideForward( angle_charact_forward ); + + if( ( the_dose_index.x == 2 ) && ( the_dose_index.y == 2 ) ){ + it_poly.PrintIntersectionIndex(); + } + + if( is_inside_valid ){ + + LegacyWorldCoordinate2D& point_of_interest_start_ref = forward_snippet_ref.point_of_interest_start; + LegacyWorldCoordinate2D& point_of_interest_end_ref = forward_snippet_ref.point_of_interest_end; + + std::vector& snippet_intermediate_content_ref = forward_snippet_ref.snippet_intermediate_content; + it_poly.RunForwardToNextIntersection( point_of_interest_start_ref , point_of_interest_end_ref , snippet_intermediate_content_ref , the_dose_index ); + + } + + return is_inside_valid; + + } + + + + bool PolygonInfo::GoBackwardAndCreatePolygonSnippet( PolygonSnippet& backward_snippet_ref ){ + + // hier muss als erstes geprueft werden, ob das Polygonsnippet ueberhaupt innerhalb des betrachteten Voxels liegt. + // Falls nein kann man alles folgende ueberspringen. + + float& angle_charact_backward = backward_snippet_ref.angle_charact; + bool is_inside_valid = CheckPolygonIsInsideBackward( angle_charact_backward ); + + if( is_inside_valid ){ + + LegacyWorldCoordinate2D& point_of_interest_start_ref = backward_snippet_ref.point_of_interest_start; + LegacyWorldCoordinate2D& point_of_interest_end_ref = backward_snippet_ref.point_of_interest_end; + std::vector& snippet_intermediate_content_ref = backward_snippet_ref.snippet_intermediate_content; + + it_poly.RunBackwardToNextIntersection( point_of_interest_start_ref , point_of_interest_end_ref , snippet_intermediate_content_ref ); + + } + + return is_inside_valid; + + } + + + + // To do : Sollte man it_poly.NextIntersection( the_next_point_ref ) besser als Funktions-Objekt-Dings uebergeben + // und dadurch CheckPolygonIsInsideForward() und CheckPolygonIsInsideBackward() in eine Funktion verpacken ? + // Man kann dann eigentlich auch mit anderen "Forward" und "Backward" Funktionen so verfahren und hat insgesamt weniger Funtionen. + // Das ist to do fuer spaeter. + bool PolygonInfo::CheckPolygonIsInsideForward( float& angle_charact_forward ){ + + bool its_inside = false; + + it_poly.RememberPosition(); + + LegacyWorldCoordinate2D the_first_point; + the_first_point.x =0; + the_first_point.y = 0; + LegacyWorldCoordinate2D& the_first_point_ref = the_first_point; + + LegacyWorldCoordinate2D the_next_point; + the_next_point.x =0; + the_next_point.y = 0; + LegacyWorldCoordinate2D& the_next_point_ref = the_next_point; + + if( it_poly.ThisIntersection( the_first_point_ref ) ){ + + bool got_it = it_poly.NextPointOrIntersectionPeriodically( the_first_point_ref, the_next_point_ref, the_dose_index ); + + int maxNum = it_poly.MaximumNumberOfElements(); + + int stop_counter = -1; + + while( ( stop_counter < ( maxNum + 2 ) ) ){ + + stop_counter++; + + /* + double close_first = ( the_next_point.x - the_first_point.x ) * ( the_next_point.x - the_first_point.x ); + close_first = sqrt( close_first ); + if( close_first > ( GInformation.getPixelSpacingRow() / 10000 ) ) stop_counter = maxNum; // ( GInformation.getPixelSpacingRow() / 10000 ) weil durch Rundungsfehler manchmal nicht exakt gleich + + double close_second = ( the_next_point.y - the_first_point.y ) * ( the_next_point.y - the_first_point.y ); + close_second = sqrt( close_second ); + if( close_second > ( GInformation.getPixelSpacingColumn() /10000 ) ) stop_counter = maxNum; + */ + + if( ( static_cast( the_next_point.x ) != static_cast( the_first_point.x ) ) || ( static_cast( the_next_point.x ) != static_cast( the_first_point.x ) ) ) stop_counter = ( maxNum + 2 ); + if( ( static_cast( the_next_point.y ) != static_cast( the_first_point.y ) ) || ( static_cast( the_next_point.y ) != static_cast( the_first_point.y ) ) ) stop_counter = ( maxNum + 2 ); + + if( stop_counter < ( maxNum + 2 ) ) got_it = it_poly.NextPointOrIntersectionPeriodically( the_first_point_ref, the_next_point_ref, the_dose_index ); + + } + + if( got_it == false )assert( 0 ); + + + if( ( got_it ) && ( ( static_cast( the_next_point.x ) != static_cast( the_first_point.x ) ) || ( static_cast( the_next_point.y ) != static_cast( the_first_point.y ) ) ) ){ + + LegacyWorldCoordinate2D an_intermediate_point; + an_intermediate_point.x =( ( the_first_point.x + the_next_point.x ) * 0.5) ; + an_intermediate_point.y = ( ( the_first_point.y + the_next_point.y ) * 0.5) ; + + GetAngle( the_next_point , the_first_point , angle_charact_forward ); + + if( CheckPointInVoxelRegardingDoseIndex( an_intermediate_point ) ) its_inside = true; + + } + else its_inside = false; + + + } + + it_poly.JumpToRememberedPosition(); + + return its_inside; + + } + + + + + // Je kleiner der Winkel zwischen Kantenrichtung im Uhrzeigersinn und Strukturelement, desto groesser der Rueckgabewert. Der + // Rueckgabewert ist der Cosisnus dieses Winkels und ist daher 1, falls der Winkel null ist - minus eins ist er falls der + // Winkel 180 Grad gegen die Kante laueft. Da hier nur Strukturelemente betrachtet werden, die ins Voxel rein laufen, + // gibt es Winkel gruesser als 180 Grad nicht. + + void PolygonInfo::GetAngle( LegacyWorldCoordinate2D the_next_point , LegacyWorldCoordinate2D the_first_point , float& angle_charact ){ + + double hypoten = ( the_next_point.x - the_first_point.x ) * ( the_next_point.x - the_first_point.x ) + ( the_next_point.y - the_first_point.y ) * ( the_next_point.y - the_first_point.y ); + + assert( hypoten > 0 ); // This should never happen except in case of a structure consisting of one point only. However, that's not allowed in voxelisation. + + if( current_edge == 0 ){ + hypoten = sqrt( hypoten ); + double gekat = ( the_next_point.x - the_first_point.x ); + angle_charact = static_cast( ( gekat / hypoten ) ); + } + if( current_edge == 1 ){ + hypoten = sqrt( hypoten ); + double ankat = ( the_next_point.y - the_first_point.y ); + angle_charact = static_cast( ( ankat / hypoten ) ); + } + if( current_edge == 2 ){ + hypoten = sqrt( hypoten ); + double gekat = ( the_first_point.x - the_next_point.x ); + angle_charact = static_cast( ( gekat / hypoten ) ); + } + if( current_edge == 3 ){ + hypoten = sqrt( hypoten ); + double ankat = ( the_first_point.y - the_next_point.y ); + angle_charact = static_cast( ( ankat / hypoten ) ); + } + + } + + + + + // To do : Kann man it_poly.NextIntersection( the_next_point_ref ) nicht als Funktions-Objekts-Dings uebergeben + // und dadurch CheckPolygonIsInsideForward() und CheckPolygonIsInsideBackward() in eine Funktion verpacken ? + // Man kann dann eigentlich auch mit anderen "Forward" und "Backward" Sachen so verfahren. + // Mache das spaeter. + + bool PolygonInfo::CheckPolygonIsInsideBackward( float& angle_charact_backward ){ + + bool its_inside = false; + + it_poly.RememberPosition(); + + LegacyWorldCoordinate2D the_first_point; + the_first_point.x =0; + the_first_point.y = 0; + LegacyWorldCoordinate2D& the_first_point_ref = the_first_point; + + LegacyWorldCoordinate2D the_next_point; + the_next_point.x =0; + the_next_point.y = 0; + LegacyWorldCoordinate2D& the_next_point_ref = the_next_point; + + if( it_poly.ThisIntersection( the_first_point_ref ) ){ + + bool got_it = it_poly.PreviousPointOrIntersectionPeriodically( the_first_point_ref , the_next_point_ref , the_dose_index ); + + int stop_counter = -1; + int maxNum = it_poly.MaximumNumberOfElements(); + + while( ( stop_counter < maxNum ) ){ + + stop_counter++; + + /*double close_first = ( the_next_point.x - the_first_point.x ) * ( the_next_point.x - the_first_point.x ); + close_first = sqrt( close_first ); + if( close_first > ( GInformation.getPixelSpacingRow() / 10000 ) ) stop_counter = maxNum; // ( GInformation.getPixelSpacingRow() / 10000 ) weil durch Rundungsfehler manchmal nicht exakt gleich + + double close_second = ( the_next_point.y - the_first_point.y ) * ( the_next_point.y - the_first_point.y ); + close_second = sqrt( close_second ); + if( close_second > ( GInformation.getPixelSpacingColumn() /10000 ) ) stop_counter = maxNum; */ + + + if( ( static_cast( the_next_point.x ) != static_cast( the_first_point.x ) ) || ( static_cast( the_next_point.x ) != static_cast( the_first_point.x ) ) ) stop_counter = maxNum; + + if( ( static_cast( the_next_point.y ) != static_cast( the_first_point.y ) ) || ( static_cast( the_next_point.y ) != static_cast( the_first_point.y ) ) ) stop_counter = maxNum; + + + + if( stop_counter < maxNum ) got_it = it_poly.PreviousPointOrIntersectionPeriodically( the_first_point_ref , the_next_point_ref , the_dose_index ); + + + } + + if( got_it == false )assert( 0 ); + + + if( ( got_it ) && ( ( static_cast( the_next_point.x ) != static_cast( the_first_point.x ) ) || ( static_cast( the_next_point.y ) != static_cast( the_first_point.y ) ) ) ){ + + LegacyWorldCoordinate2D an_intermediate_point; + an_intermediate_point.x =( ( the_first_point.x + the_next_point.x ) * 0.5 ); + an_intermediate_point.y = ( ( the_first_point.y + the_next_point.y ) * 0.5 ); + + GetAngle( the_next_point , the_first_point , angle_charact_backward ); + + if( CheckPointInVoxelRegardingDoseIndex( an_intermediate_point ) ) its_inside = true; + + } + else its_inside = false; + + } + else assert(0); + + it_poly.JumpToRememberedPosition(); + + return its_inside; + + } + + + + bool PolygonInfo::CheckPointInVoxelRegardingDoseIndex( LegacyWorldCoordinate2D voxel_coordinate_to_be_checked ){ + + bool its_inside = false; + + if( ( voxel_coordinate_to_be_checked.x <= ( the_dose_index.x + 1 ) ) && ( voxel_coordinate_to_be_checked.x >= the_dose_index.x ) && ( voxel_coordinate_to_be_checked.y <= ( the_dose_index.y + 1 ) ) && ( voxel_coordinate_to_be_checked.y >= the_dose_index.y ) ) its_inside = true; + + + return its_inside; + + } + + + + double PolygonInfo::GetDistanceNextEdge( int current_edge ){ + + double return_distance; + + + switch( current_edge ){ + + + case 0 : + return_distance = GInformation.getPixelSpacingRow(); + break; + + + case 1 : + return_distance = GInformation.getPixelSpacingColumn(); + return_distance += GInformation.getPixelSpacingRow(); + break; + + case 2 : + return_distance = GInformation.getPixelSpacingColumn(); + return_distance += 2*GInformation.getPixelSpacingRow(); + break; + + case 3 : + return_distance = 2*GInformation.getPixelSpacingColumn(); + return_distance += 2*GInformation.getPixelSpacingRow(); + break; + + + } + + + return return_distance; + + } + + + + float PolygonInfo::GetDistanceAlongEdge( LegacyDoseVoxelIndex3D edge_position , LegacyWorldCoordinate2D local_position ){ + + WorldCoordinate return_distance; + + if( local_position.y == edge_position.y ){ + return_distance = ( local_position.x - edge_position.x ) * GInformation.getPixelSpacingRow(); + } + else if( local_position.y == ( edge_position.y + 1 ) ){ + return_distance = GInformation.getPixelSpacingRow() - ( local_position.x - edge_position.x ) * GInformation.getPixelSpacingRow(); + return_distance += GInformation.getPixelSpacingColumn(); + return_distance += GInformation.getPixelSpacingRow(); + + } + else if( local_position.x == edge_position.x ){ + + return_distance = 2 * GInformation.getPixelSpacingRow(); + return_distance += GInformation.getPixelSpacingColumn(); + return_distance += GInformation.getPixelSpacingColumn() - ( local_position.y - edge_position.y ) * GInformation.getPixelSpacingColumn(); + + }else if( local_position.x == ( edge_position.x + 1 ) ){ + return_distance = GInformation.getPixelSpacingRow(); + return_distance += ( local_position.y - edge_position.y ) * GInformation.getPixelSpacingColumn(); + + } + else{ + // assert : Etwas ist schief gegangen. Wenn es sich wirklich um einen Schnittpunkt handelt, + // dann muesste einer der obigen Faelle eingetreten sein. + assert( 0 ); + } + + return return_distance; + + } + + + + // Returns 1D distance of two Points along edge. + // Both Points are located on the edge of the voxel and the + + float PolygonInfo::GetPeriodicDist( float distance_current_position_local , float distance_current_position ){ + + float distance_oneD_return; + + float max_distance = static_cast( 2 * GInformation.getPixelSpacingRow() + 2 * GInformation.getPixelSpacingColumn() ); + + if( distance_current_position_local >= distance_current_position ){ + + distance_oneD_return = distance_current_position_local - distance_current_position; + + float cross_edge_distance = ( max_distance - distance_current_position_local ) + distance_current_position; + + if( cross_edge_distance < distance_oneD_return ) distance_oneD_return = cross_edge_distance; + + } + else{ + + distance_oneD_return = distance_current_position - distance_current_position_local; + + float cross_edge_distance = ( max_distance - distance_current_position ) + distance_current_position_local; + + if( cross_edge_distance < distance_oneD_return ) distance_oneD_return = cross_edge_distance; + + } + + return distance_oneD_return; + + } + + + + double PolygonInfo::GetClockwiseDist( double distance_refer_to , double distance_compare ){ + + double distance_oneD_return; + + double max_distance = static_cast( 2 * GInformation.getPixelSpacingRow() + 2 * GInformation.getPixelSpacingColumn() ); + + if( distance_compare >= distance_refer_to ){ + + distance_oneD_return = distance_compare - distance_refer_to; + + } + else{ + + distance_oneD_return = ( max_distance - distance_refer_to ) + distance_compare; + + } + + return distance_oneD_return; + + } + + + + /* + + // Es gibt einen current_static_index, der besagt welches Polygon gerade erzeugt wird. + + // Und einen current_moving_index, der einmal ums Voxel laueft. + // Auserdem einen begin_index, der sagt, wo man mit der Erzeugung des umschliessenden Polygons angefangen hat + // und wider aufhoeren will. + + + current_static_index klappert jetzt die Voxelkante ab. + Fuer jeden zweiten drinnen-drausen-Wechsel (den gibt es bei echten Schnittpunkten und manchen Beruehrpunkten) + wird nun also ein innen-geschlossenes-Polygon berechnet. + Das geschieht indem fuer die entsprechende Position des current_static_index solange der current_moving_index + verschoben wird, bis man im Kreis rum ist. + Der current_moving_index wird dabei in doppleschritten verschoben: + Erst der Kante entlang zum naechsten echten Schnittpunkt oder Beruehrpunkt der zweiten Art. + Dann wird diesem Polygonsnippet zu sienem anderen + Schnittpunkt/Beruehrpunkt gefolgt. Das Polygonsnippet und die Randpunkte werden dabei an das derzeit zu erzeugende + Innenpolygon angehaengt. + + Im Idealfall weiss man von einer Voxelecke anhand der Maske, dass sie draussen liegt. + + Falls es keine Voxelecke gibt von der man eindeutig sagen kann dass sie draussen liegt und auf die man dann den begin_index setzen kann, + dann wird gesucht, ob es irgend eine Ecke gibt, die nicht Schnitt oder Beruehrpunkt ist. Per VoxelInStructure aus der Tooblox kann man sich in + diesem Fall behelfen. + + Falls alle Ecken Beruehr oder Schnittpunkte sind, wird zunaechst geprueft, ob es nur Polygonsnippets gibt, die den Kanten entlang + laufen. Falls das so ist, wird VoxelInStructure der Toolbox fuer das Zentrum des Voxels aufgerufen. + Wenn das Zentrum drin ist, dann ist das ganze Voxel von der Kontur umschlossen. + Falls nicht liegt das ganze Voxel ausserhalb. + + Falls alle Voxelecken Schnitt/Beruehrpunkte sind und falls es sehr wohl echte Schnitte gibt: + Einen Punkt finden, der echter Schnittpunkt ist. Seine Nachbarschaft per VoxelInStructure aus der Tooblox untersuchen. + + + */ + + // Anmerkung : Hier werden Aenderungen noetig sien, sobald Beruehrpunkte der Kontur mit sich selbst erlaubt sind. + // Die Aenderung wird darin bestehen, dass mehrere snippets mit dem selben why_here Wert existieren, die gleichberechtigt + // behandelt werden nach dem Winkel des sich nach innnen fortsetzenden Strukturelements geordnet sind. Diese werden dann + // behandelt wei ueblich (also keine echte Aenderung des Programmablaufs nur der Art der Polygon - Snippets ) + + // Polygon aufbauen: resulting_sequence wird hier belegt + bool PolygonInfo::CreatePolygonSequence(){ + + CalcSnippetVectors(); + + SnippetIndex& current_static_index_ref = current_static_index; + + // Es wird im idealfall in einer Ecke mit bekanntem drinnen /drausen begonnen. begin_index wird dort drauf gesetzt. + // current_static_index wird solange eins weiter gesetzt bis man wieder am Anfang ankommt. + // falls die Kontur gar nicht ins voxel rein laueft wird nothing_to_do auf true gesetzt + + bool nothing_to_do = false; + bool& nothing_to_do_ref = nothing_to_do; + + + // SetCurrentStaticIndexKnownInsideOrOutside gibt zum einen ueber its_inside die information darueber zurueck, + // ob der Punkt auf den current_static_index gesetzt wurde im aussenbereich der Kontur liegt. + // Zum anderen enthaelt nothing_to_do_ref nach Aufruf von SetCurrentStaticIndexKnownInsideOrOutside die + // Information darueber, ob das Voxel ueberhaupt angeschnitten ist. Ist nothing_to_do_ref true, so ist das Voxel + // gar nicht angeschnitten. Ist dann auch noch its_inside true, dann liegt das ganze Voxel komplett im innern der Struktur. + // Ansonsten liegt das ganze Voxel komplett ausserhalb der Struktur. + bool its_inside = SetCurrentStaticIndexKnownInsideOrOutside( current_static_index_ref , nothing_to_do_ref ); // Falls es eine Ecke gibt, die draussen liegt wird hier false zurueckgegeben. + bool just_switched_inside = false; + if( its_inside ) just_switched_inside = true; + + + /* std::cout<< " the_dose_index.x : " << the_dose_index.x <( the_world_coordinate_3D.z ) != 0 ){ + the_z_value = the_world_coordinate_3D.z; + second_index = resulting_sequence.at( first_index ).size(); + } + + } + for( unsigned int second_index = 0 ; second_index < resulting_sequence.at( first_index ).size() ; second_index++ ){ + + LegacyWorldCoordinate3D& the_world_coordinate_3D_ref = resulting_sequence.at( first_index ).at( second_index ); + the_world_coordinate_3D_ref.z = the_z_value; + + } + + + if( resulting_sequence.at( first_index ).size() > 0 ){ + if( static_cast( resulting_sequence.at( first_index ).at( resulting_sequence.at( first_index ).size() - 1 ).x ) != static_cast( resulting_sequence.at( first_index ).at(0).x ) )assert(0); // this must never happen. The countour must be of periodic nature. + if( static_cast( resulting_sequence.at( first_index ).at( resulting_sequence.at( first_index ).size() - 1 ).y ) != static_cast( resulting_sequence.at( first_index ).at(0).y ) )assert(0); // this must never happen. The countour must be of periodic nature. + if( static_cast( resulting_sequence.at( first_index ).at( resulting_sequence.at( first_index ).size() - 1 ).z ) != static_cast( resulting_sequence.at( first_index ).at(0).z ) )assert(0); // this must never happen. The countour must be of periodic nature. + } + + } + + } + + + + + int PolygonInfo::GetMaxNumberSnippets(){ + + int nr = 0; + + for( int ind = 0 ; ind != 4 ; ind++ ){ + + nr += pol_inf_vec.at( ind ).size(); + + } + + return nr; + + } + + + + // Hier werden Aenderungen noetig sein, sobald Beruehrpunkte der Kontur mit sich selbst erlaubt sind. + // Ist erledigt. Die Beruehrpunkte sind jetzt erlaubt + void PolygonInfo::GoGetPoly( bool& do_continue_ref , bool& next_is_edge_ref, int& continue_counter_ref , int& max_num_ref ){ + + + PolygonSnippet snipp; + PolygonSnippet& snipp_ref = snipp; + + if( next_is_edge_ref ){ + + bool interupt = false; + + GetCurrentMovingSnippet( snipp_ref ); + + LegacyWorldCoordinate2D why_here = snipp_ref.why_here; + + int the_previous_edge = current_moving_index.edge; + continue_counter_ref++; + IncrementSnippetIndex( current_moving_index ); + + if( do_continue_ref ) do_continue_ref = DoContinue( max_num_ref , continue_counter_ref ); + if( do_continue_ref == false )interupt = true; + while( ! interupt ){ + + if( TryGetPolygonsnippetEdge( continue_counter_ref , the_previous_edge ) ){ // holt sich das Schnipsel, das zum moving index gehoert falls es Konturinhalt hat. Falls nicht wird der moving index weiter gesetzt. + interupt = true; + next_is_edge_ref = false; + + } + + + if( do_continue_ref ) do_continue_ref = DoContinue( max_num_ref , continue_counter_ref ); + + if( do_continue_ref == false ){ + + interupt = true; + } + + + } + + GetCurrentMovingSnippet( snipp_ref ); + + DoAppendIntermediateEdgeUpToThisSnippet( snipp_ref , why_here , the_previous_edge , current_moving_index.edge ); + + } + else{ + + if( GetCurrentMovingSnippet( snipp_ref ) ){ + + LegacyWorldCoordinate2D why_here = snipp_ref.why_here; + + SetCurrentMovingProcessed(); + + GoAlongSnippetToNextIntersectionAndAppendToResultingSequence( snipp_ref , continue_counter_ref ); + + SetCurrentMovingProcessed(); + + if( do_continue_ref ){ + + next_is_edge_ref = GetNextIsEdge( current_moving_index ); + + if( ! next_is_edge_ref ){ + continue_counter_ref++; + IncrementSnippetIndex( current_moving_index ); + } + + } + + if( do_continue_ref ) do_continue_ref = DoContinue( max_num_ref , continue_counter_ref ); + + + } + else assert(0); //this should never happen + + + } + + + } + + + + bool PolygonInfo::CurrentSnippetIsProcessed(){ + return pol_inf_vec.at( current_static_index.edge ).at( current_static_index.snip ).i_have_been_processed; + } + + + void PolygonInfo::ShowPolygonInTermsOfIntersections(){ + + it_poly.ShowSelf(); + + } + + + void PolygonInfo::ShowResultingSequence(){ + + for( GridIndexType counter = 0 ; counter < resulting_sequence.size() ; counter++ ){ + + std::cout<< " And this is countour number : " << counter <= 0 ) && ( current_moving_index.edge >= 0 ) ){ + snipp_ref = pol_inf_vec.at( current_moving_index.edge ).at( current_moving_index.snip ); + + return true; + } + else return false; + + } + + + void PolygonInfo::SetCurrentMovingProcessed(){ + if( ( current_moving_index.edge < 4 ) && ( current_moving_index.snip < pol_inf_vec.at( current_moving_index.edge ).size() ) && ( current_moving_index.snip >= 0 ) && ( current_moving_index.edge >= 0 ) ){ + pol_inf_vec.at( current_moving_index.edge ).at( current_moving_index.snip ).i_have_been_processed = true; + } + } + + + int PolygonInfo::EdgeDist( int edge_old , int the_current_edge ){ + + int edge_internal = edge_old; + + int counter = 0; + + while( edge_internal != the_current_edge ){ + counter++; + IncrementEdge( edge_internal ); + } + + return counter; + + } + + + void PolygonInfo::IncrementEdge( int& edge_increment ){ + + if( edge_increment == 3 ) edge_increment = 0; + else edge_increment++; + + } + + + void PolygonInfo::DoAppendIntermediateEdgeUpToThisSnippet( PolygonSnippet& snipp_ref , LegacyWorldCoordinate2D why_here , int the_previous_edge , int the_current_edge ){ + + PolygonSnippet snipp_internal; + PolygonSnippet& snipp_internal_ref = snipp_internal; + + snipp_internal_ref.why_here = why_here; + snipp_internal_ref.point_of_interest_start = why_here; + snipp_internal_ref.point_of_interest_end = snipp_ref.why_here; + + std::vector& inter_content_ref = snipp_internal.snippet_intermediate_content; + + int edge_internal = the_previous_edge; + + while( EdgeDist( edge_internal , the_current_edge ) > 0 ){ + edge_internal++; + if( edge_internal == 4 ) edge_internal = 0; + AppendEdgeToVector( inter_content_ref , edge_internal ); + } + + if( snipp_internal_ref.snippet_intermediate_content.size() > 0 ){ + if( ( snipp_internal_ref.snippet_intermediate_content.at( 0 ).x == snipp_internal_ref.point_of_interest_end.x ) && ( snipp_internal_ref.snippet_intermediate_content.at( 0 ).y == snipp_internal_ref.point_of_interest_end.y ) ) snipp_internal_ref.snippet_intermediate_content.clear(); + } + + if( snipp_internal_ref.snippet_intermediate_content.size() > 0 ){ + std::vector::iterator iter; + while( ( snipp_internal_ref.snippet_intermediate_content.at( snipp_internal_ref.snippet_intermediate_content.size() - 1 ).x == snipp_internal_ref.point_of_interest_end.x ) && ( snipp_internal_ref.snippet_intermediate_content.at( snipp_internal_ref.snippet_intermediate_content.size() - 1 ).y == snipp_internal_ref.point_of_interest_end.y ) ){ + iter = snipp_internal_ref.snippet_intermediate_content.begin(); + iter += ( snipp_internal_ref.snippet_intermediate_content.size() - 1 ); + snipp_internal_ref.snippet_intermediate_content.erase( iter ); + } + } + + AppendToResultingSequence( snipp_internal_ref ); + + + } + + + + + void PolygonInfo::AppendEdgeToVector( std::vector& vector , int edge_internal ){ + + LegacyWorldCoordinate3D world_coordinate; + + switch( edge_internal ){ + + case 0 : + + world_coordinate.x =the_dose_index.x; + world_coordinate.y = the_dose_index.y; + world_coordinate.z = 0; + break; + + + case 1 : + + world_coordinate.x =the_dose_index.x + 1; + world_coordinate.y = the_dose_index.y; + world_coordinate.z = 0; + break; + + case 2 : + + world_coordinate.x =the_dose_index.x + 1; + world_coordinate.y = the_dose_index.y + 1; + world_coordinate.z = 0; + break; + + case 3 : + + world_coordinate.x =the_dose_index.x; + world_coordinate.y = the_dose_index.y + 1; + world_coordinate.z = 0; + break; + + default : + + assert( 0 ); + + } + + vector.push_back( world_coordinate ); + + + } + + + + void PolygonInfo::AppendToResultingSequence( PolygonSnippet& snipp_ref ){ + + int which_polygon = ( resulting_sequence.size() - 1 ); + + if( resulting_sequence.at( which_polygon ).size() > 0 ){ + + LegacyWorldCoordinate3D the_world_coordinate_3D = resulting_sequence.at( which_polygon ).at( resulting_sequence.at( which_polygon ).size() - 1 ); + + if( ( snipp_ref.point_of_interest_start.x == the_world_coordinate_3D.x ) && ( snipp_ref.point_of_interest_start.y == the_world_coordinate_3D.y ) ){ + + DoAppendForward( snipp_ref , which_polygon ); + + } + else if( ( snipp_ref.point_of_interest_end.x == the_world_coordinate_3D.x ) && ( snipp_ref.point_of_interest_end.y == the_world_coordinate_3D.y ) ){ + + DoAppendBackward( snipp_ref , which_polygon ); + + } + else{ + + the_world_coordinate_3D = resulting_sequence.at( which_polygon ).at( 0 ); + + if( ( snipp_ref.point_of_interest_start.x == the_world_coordinate_3D.x ) && ( snipp_ref.point_of_interest_start.y == the_world_coordinate_3D.y ) ){ + + DoInsertForward( snipp_ref , which_polygon ); + + } + else if( ( snipp_ref.point_of_interest_end.x == the_world_coordinate_3D.x ) && ( snipp_ref.point_of_interest_end.y == the_world_coordinate_3D.y ) ){ + + DoInsertBackward( snipp_ref , which_polygon ); + + } + else{ + assert( 0 ); // this should never happen + } + + } + + } + else AddFirstForward( snipp_ref , which_polygon ); + + } + + + + void PolygonInfo::DoAppendForward( PolygonSnippet& snipp_ref , int which_polygon ){ + + for( unsigned int counter = 0 ; counter < snipp_ref.snippet_intermediate_content.size() ; counter++ ){ + resulting_sequence.at( which_polygon ).push_back( snipp_ref.snippet_intermediate_content.at( counter ) ); + } + + LegacyWorldCoordinate3D coord_3_d; + coord_3_d.x = snipp_ref.point_of_interest_end.x; + coord_3_d.y = snipp_ref.point_of_interest_end.y; + coord_3_d.z = 0; + + resulting_sequence.at( which_polygon ).push_back( coord_3_d ); + + } + + + + void PolygonInfo::DoAppendBackward( PolygonSnippet& snipp_ref , int which_polygon ){ + + int index = 0; + + for( unsigned int counter = 0 ; counter < snipp_ref.snippet_intermediate_content.size() ; counter++ ){ + + index = snipp_ref.snippet_intermediate_content.size() - counter - 1; + + resulting_sequence.at( which_polygon ).push_back( snipp_ref.snippet_intermediate_content.at( index ) ); + + } + + LegacyWorldCoordinate3D coord_3_d; + coord_3_d.x = snipp_ref.point_of_interest_start.x; + coord_3_d.y = snipp_ref.point_of_interest_start.y; + coord_3_d.z = 0; + + resulting_sequence.at( which_polygon ).push_back( coord_3_d ); + + } + + + void PolygonInfo::DoInsertForward( PolygonSnippet& snipp_ref , int which_polygon ){ + + LegacyPolygonTypeIterator it; + + for( unsigned int counter = 0 ; counter < snipp_ref.snippet_intermediate_content.size() ; counter++ ){ + it = resulting_sequence.at( which_polygon ).begin(); + resulting_sequence.at( which_polygon ).insert( it , snipp_ref.snippet_intermediate_content.at( counter ) ); + } + + it = resulting_sequence.at( which_polygon ).begin(); + + LegacyWorldCoordinate3D coord_3_d; + coord_3_d.x = snipp_ref.point_of_interest_end.x; + coord_3_d.y = snipp_ref.point_of_interest_end.y; + coord_3_d.z = 0; + + resulting_sequence.at( which_polygon ).insert( it , coord_3_d ); + + } + + + void PolygonInfo::DoInsertBackward( PolygonSnippet& snipp_ref , int which_polygon ){ + + int index = 0; + + LegacyPolygonTypeIterator it; + + for( unsigned int counter = 0 ; counter < snipp_ref.snippet_intermediate_content.size() ; counter++ ){ + + index = snipp_ref.snippet_intermediate_content.size() - counter - 1; + + it = resulting_sequence.at( which_polygon ).begin(); + + resulting_sequence.at( which_polygon ).insert( it , snipp_ref.snippet_intermediate_content.at( index ) ); + + } + + it = resulting_sequence.at( which_polygon ).begin(); + + LegacyWorldCoordinate3D coord_3_d; + coord_3_d.x = snipp_ref.point_of_interest_start.x; + coord_3_d.y = snipp_ref.point_of_interest_start.y; + coord_3_d.z = 0; + + resulting_sequence.at( which_polygon ).insert( it , coord_3_d ); + + } + + + void PolygonInfo::AddFirstForward( PolygonSnippet& snipp_ref , int which_polygon ){ + + LegacyWorldCoordinate3D coord_3_d; + coord_3_d.x = snipp_ref.point_of_interest_start.x; + coord_3_d.y = snipp_ref.point_of_interest_start.y; + coord_3_d.z = 0; + + resulting_sequence.at( which_polygon ).push_back( coord_3_d ); + + DoAppendForward( snipp_ref , which_polygon ); + + } + + + + + void PolygonInfo::GoAlongSnippetToNextIntersectionAndAppendToResultingSequence( PolygonSnippet& snipp_ref , int& continue_counter_ref ){ + + AppendToResultingSequence( snipp_ref ); + FindCorrespondingOneAndSetMovingIndexAndSetContinueCounter( snipp_ref , continue_counter_ref ); + + } + + + + + + bool PolygonInfo::IsCorresponding( PolygonSnippet& snipp_ref_compare , PolygonSnippet& snipp_ref ){ + + + if( ( static_cast( snipp_ref_compare.why_here.x ) == static_cast( snipp_ref.point_of_interest_end.x ) ) && ( static_cast( snipp_ref_compare.why_here.y ) == static_cast( snipp_ref.point_of_interest_end.y ) ) ){ + + if( ! ( ( static_cast( snipp_ref_compare.point_of_interest_end.x ) == static_cast( snipp_ref.why_here.x ) ) || ( static_cast( snipp_ref_compare.point_of_interest_start.x ) == static_cast( snipp_ref.why_here.x ) ) ) && ( ( static_cast( snipp_ref_compare.point_of_interest_end.y ) == static_cast( snipp_ref.why_here.y ) ) || ( static_cast( snipp_ref_compare.point_of_interest_start.y ) == static_cast( snipp_ref.why_here.y ) ) ) ) return false; + + if( snipp_ref_compare.snippet_intermediate_content.size() != snipp_ref.snippet_intermediate_content.size() ) return false; + + for( GridIndexType counter = 0 ; counter < snipp_ref_compare.snippet_intermediate_content.size() ; counter++ ){ + + if( static_cast( snipp_ref_compare.snippet_intermediate_content.at( counter ).x ) != static_cast( snipp_ref.snippet_intermediate_content.at( snipp_ref.snippet_intermediate_content.size() - ( counter + 1 ) ).x ) ) return false; + if( static_cast( snipp_ref_compare.snippet_intermediate_content.at( counter ).y ) != static_cast( snipp_ref.snippet_intermediate_content.at( snipp_ref.snippet_intermediate_content.size() - ( counter + 1 ) ).y ) ) return false; + + } + + return true; + + } + else return false; + + } + + + + LegacyWorldCoordinate2D PolygonInfo::GetOtherEnd( PolygonSnippet& snipp_ref ){ + + if( ( snipp_ref.why_here.x == snipp_ref.point_of_interest_start.x ) && ( snipp_ref.why_here.y == snipp_ref.point_of_interest_start.y ) ){ + return snipp_ref.point_of_interest_end; + } + else if( ( snipp_ref.why_here.x == snipp_ref.point_of_interest_end.x ) && ( snipp_ref.why_here.y == snipp_ref.point_of_interest_end.y ) ){ + return snipp_ref.point_of_interest_start; + } + else assert( 0 ); // this should never happen + return LegacyWorldCoordinate2D(); + } + + + int PolygonInfo::GetSnippetIndexDistance( SnippetIndex behind_snip_index , SnippetIndex front_snip_index ){ + + int dist = 0; + + while( !SnippetIndexIdentical( behind_snip_index , front_snip_index ) ){ + + dist++; + IncrementSnippetIndex( behind_snip_index ); + + } + + return dist; + } + + + // returns true in case indicees are identical + bool PolygonInfo::SnippetIndexIdentical( SnippetIndex first , SnippetIndex second ){ + if( ( first.edge == second.edge ) && ( first.snip == second.snip ) )return true; + else return false; + } + + + + void PolygonInfo::FindCorrespondingOneAndSetMovingIndexAndSetContinueCounter( PolygonSnippet& snipp_ref , int& continue_counter_ref ){ + + + SnippetIndex another_snip_index; + another_snip_index.edge = current_moving_index.edge; + another_snip_index.snip = current_moving_index.snip; + + IncrementSnippetIndex( another_snip_index ); + + bool there_is_one = false; + + while( ( !there_is_one ) && ( ( another_snip_index.edge != current_moving_index.edge ) || ( another_snip_index.snip != current_moving_index.snip ) ) ){ + + PolygonSnippet& snipp_ref_compare = pol_inf_vec.at( another_snip_index.edge ).at( another_snip_index.snip ) ; + if( IsCorresponding( snipp_ref_compare , snipp_ref ) ){ + + continue_counter_ref += GetSnippetIndexDistance( current_moving_index , another_snip_index ); + current_moving_index.edge = another_snip_index.edge; + current_moving_index.snip = another_snip_index.snip; + SetCurrentMovingProcessed(); + there_is_one = true; + + } + + IncrementSnippetIndex( another_snip_index ); + + } + + + assert( there_is_one ); + + } + + + + bool PolygonInfo::IsOtherEdge( PolygonSnippet& snipp_ref , int the_previous_edge ){ + + if( ( the_previous_edge == 0 ) || ( the_previous_edge == 2 ) ){ + + if( static_cast( snipp_ref.point_of_interest_start.y ) != static_cast( snipp_ref.point_of_interest_end.y ) ) return true; + + } + else if( ( the_previous_edge == 1 ) || ( the_previous_edge == 3 ) ){ + + if( static_cast( snipp_ref.point_of_interest_start.x ) != static_cast( snipp_ref.point_of_interest_end.x ) ) return true; + + } + else assert(0); + + return false; + + } + + + + bool PolygonInfo::TryGetPolygonsnippetEdge( int& continue_counter_ref , int the_previous_edge ){ + + if( pol_inf_vec.at( current_moving_index.edge ).at( current_moving_index.snip ).snippet_intermediate_content.size() > 0 ){ + + // ShowSnippet(pol_inf_vec.at( current_moving_index.edge ).at( current_moving_index.snip )); + + return true; + + } + else if( IsOtherEdge( pol_inf_vec.at( current_moving_index.edge ).at( current_moving_index.snip ) , the_previous_edge ) ){ + + return true; + + } + else{ + + continue_counter_ref++; + IncrementSnippetIndex( current_moving_index ); + return false; + + } + + } + + + void PolygonInfo::IncrementSnippetIndex( SnippetIndex& the_snippet_index ){ + + the_snippet_index.snip += 1; + + if( the_snippet_index.snip >= pol_inf_vec.at( the_snippet_index.edge ).size() ){ + + the_snippet_index.snip = 0; + the_snippet_index.edge++; + if( the_snippet_index.edge > 3 ) the_snippet_index.edge = 0; + + } + + } + + + bool PolygonInfo::GetNextIsEdge( SnippetIndex snip_index ){ + + CharacterizeIntersection characterize = pol_inf_vec.at( snip_index.edge ).at( snip_index.snip ).characterize; + if( characterize == inside_inside_touches ){ + return false; + } + else{ + return true; + } + + } + + + + bool PolygonInfo::DoContinue( int& max_num , int& continue_counter_ref ){ + + if( max_num > ( continue_counter_ref ) ) return true; + else if( max_num == ( continue_counter_ref ) )return false; + else{ + //std::cout<< " the_dose_index.x : " << the_dose_index.x <GetData( x, y, z ) == brightness_outside ) assert( 0 ); // this should never happen since we are dealing with a voxel that is touched by the structure. + //if( MaskFieldReceived->GetData( x, y, z ) == brightness_inside ) assert( 0 ); // this should never happen since we are dealing with a voxel that is touched by the structure. + + + if( ( CheckThereIsSuchNeighbour( x , y , z , -1 , -1 , 0, brightness_outside ) ) ){ + current_static_index_ref.edge = 0; + current_static_index_ref.snip = 0; + return true; + } + else if( ( CheckThereIsSuchNeighbour( x , y , z , 1 , -1 , 0 , brightness_outside ) ) ){ + current_static_index_ref.edge = 1; + current_static_index_ref.snip = 0; + return true; + } + else if( ( CheckThereIsSuchNeighbour( x , y , z , 1 , 1 , 0, brightness_outside ) ) ){ + current_static_index_ref.edge = 2; + current_static_index_ref.snip = 0; + return true; + } + else if( ( CheckThereIsSuchNeighbour( x , y , z , -1 , 1 , 0, brightness_outside ) ) ){ + current_static_index_ref.edge = 3; + current_static_index_ref.snip = 0; + return true; + } + + return false; + + } + + + + // Gibt, falls es eine Ecke gibt, die drinnen liegt true zurueck und setzt current_static_index_ref gegebenenfalls auf die entsprechende Ecke. + bool PolygonInfo::CheckCornersInside( SnippetIndex& current_static_index_ref ){ + + + + Uint16 x = the_dose_index.x; + Uint16 y = the_dose_index.y; + Uint16 z = the_dose_index.z; + + + if( MaskFieldReceived->GetData( x, y, z ) == brightness_outside ) assert( 0 ); // this should never happen since we are dealing with a voxel that is touched by the structure. + //if( MaskFieldReceived->GetData( x, y, z ) == brightness_inside ) assert( 0 ); + + if( ( CheckThereIsSuchNeighbour( x , y , z , -1 , -1 , 0, brightness_inside ) ) ){ + current_static_index_ref.edge = 0; + current_static_index_ref.snip = 0; + return true; + } + else if( ( CheckThereIsSuchNeighbour( x , y , z , 1 , -1 , 0, brightness_inside ) ) ){ + current_static_index_ref.edge = 1; + current_static_index_ref.snip = 0; + return true; + } + else if( ( CheckThereIsSuchNeighbour( x , y , z , 1 , 1 , 0, brightness_inside ) ) ){ + current_static_index_ref.edge = 2; + current_static_index_ref.snip = 0; + return true; + } + else if( ( CheckThereIsSuchNeighbour( x , y , z , -1 , 1 , 0, brightness_inside ) ) ){ + current_static_index_ref.edge = 3; + current_static_index_ref.snip = 0; + return true; + } + + return false; + + } + + + bool PolygonInfo::CheckThereIsSuchNeighbour( Uint16 x , Uint16 y , Uint16 z , int shift_x , int shift_y , int shift_z, field_content_type brightness ){ + + Uint16 x_intern = x + shift_x; + Uint16 y_intern = y; + + if( ( x_intern < MaskFieldReceived->GetDimX() ) && ( x_intern >= 0 ) ){ + if( MaskFieldReceived->GetData( x_intern, y_intern, z ) == brightness ) return true; + } + + x_intern = x + shift_x; + y_intern = y + shift_y; + + if( ( x_intern < MaskFieldReceived->GetDimX() ) && ( x_intern >= 0 ) && ( y_intern < MaskFieldReceived->GetDimY() ) && ( y_intern >= 0 ) ){ + if( MaskFieldReceived->GetData( x_intern, y_intern, z ) == brightness ) return true; + } + + x_intern = x; + y_intern = y + shift_y; + + if( ( y_intern < MaskFieldReceived->GetDimY() ) && ( y_intern >= 0 ) ){ + if( MaskFieldReceived->GetData( x_intern, y_intern, z ) == brightness ) return true; + } + + return false; + + } + + + + + // Gibt true zurueck, falls das Voxel von der Kontur gar nicht angeschnitten wird. + bool PolygonInfo::CheckCenterSurroundingStructure( bool& inside ){ + + bool no_intersections = true; + + SnippetIndex a_snippet_index; + a_snippet_index.snip = 0; + a_snippet_index.edge = 0; + + PolygonSnippet a_snippet; + + for( a_snippet_index.edge = 0 ; a_snippet_index.edge < 4 ; a_snippet_index.edge++ ){ + for( a_snippet_index.snip = 0 ; a_snippet_index.snip < ( pol_inf_vec.at( a_snippet_index.edge ).size() ) ; a_snippet_index.snip++ ){ + + a_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + + if( !( ( a_snippet.characterize == outside_outside_touches ) || ( a_snippet.characterize == edge_to_edge ) || ( a_snippet.characterize == corner ) || (a_snippet.characterize == outside_to_edge ) || ( a_snippet.characterize == edge_to_outside ) ) ){ + no_intersections = false; + } + + } + } + + if( no_intersections ){ + + LegacyWorldCoordinate3D a_voxel_coordinate; + a_voxel_coordinate.x = (the_dose_index.x + 0.5); + a_voxel_coordinate.y = (the_dose_index.y + 0.5); + a_voxel_coordinate.z = the_dose_index.z; + + + LegacyWorldCoordinate3D a_world_coordinate = GetWorldCoordinate( a_voxel_coordinate ); + + Contour contour(the_polygon); + inside = contour.pointInPolygon(a_world_coordinate ); + + + } + + return no_intersections; + + } + + + + // Die folgende Funktionalitaet ist in DoseVoxel im Prinziep bereits vorhanden. Daher Lanlan fragen, ob man sie dort + // zugaenglich machen kann, damit wir sie hier nicht verdoppeln muessen. Allerdings ist der Input hier . + LegacyWorldCoordinate3D PolygonInfo::GetWorldCoordinate( LegacyWorldCoordinate3D voxel_coordinate ){ + + LegacyWorldCoordinate3D world_coordinate; + LegacyWorldCoordinate3D spacing; + spacing.x = GInformation.getPixelSpacingRow(); + spacing.y = GInformation.getPixelSpacingColumn(); + spacing.z = GInformation.getSliceThickness(); + OrientationMatrix orientation = GInformation.getOrientationMatrix(); + LegacyWorldCoordinate3D posPatient = GInformation.getImagePositionPatient(); + + world_coordinate.x = (orientation(0,0)*spacing.x*(voxel_coordinate.x-0.5)+orientation(0,1)*spacing.y*(voxel_coordinate.y-0.5)+posPatient.x); + world_coordinate.y = (orientation(1,0)*spacing.x*(voxel_coordinate.x-0.5)+orientation(1,1)*spacing.y*(voxel_coordinate.y-0.5)+posPatient.y); + world_coordinate.z = (posPatient.z + spacing.z *(voxel_coordinate.z-0.5)); + + return world_coordinate; + + } + + + void PolygonInfo::SetCurrentPosition( const LegacyDoseVoxelIndex3D& aDoseIndex ){ + current_position.x = static_cast(aDoseIndex.x); + current_position.y = static_cast(aDoseIndex.y); + } + + + + bool PolygonInfo::SnippetIndexIsIdentical( SnippetIndex first_index , SnippetIndex second_index ){ + if( ( first_index.edge == second_index.edge ) && ( first_index.snip == second_index.snip ) ) return true; + else return false; + } + + + + // Anpassung fuer beruehrende Strukturen noetig. Dann muss sichergestellt werdedn, dass current_static_index_ref nicht ein + // snippet repraesentiert das von einem anderen Snippet gefolgt wird fuer das .why_here identisch ist. + bool PolygonInfo::CheckPointOnEdgeOutside( SnippetIndex& current_static_index_ref , bool& inside ){ + + bool got_it = false; + + SnippetIndex a_snippet_index; + a_snippet_index.snip = 0; + a_snippet_index.edge = 0; + + PolygonSnippet a_snippet; + PolygonSnippet next_snippet; + + for( a_snippet_index.edge = 0 ; a_snippet_index.edge < 4 ; a_snippet_index.edge++ ){ + + for( a_snippet_index.snip = 0 ; a_snippet_index.snip < ( pol_inf_vec.at( a_snippet_index.edge ).size() ) ; a_snippet_index.snip++ ){ + + a_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + + if( ( a_snippet.characterize == real_intersection ) || ( a_snippet.characterize == edge_to_inside ) || ( a_snippet.characterize == edge_to_outside ) || ( a_snippet.characterize == inside_inside_touches ) ){ + + int at_begin = 0; + + while( ( got_it == false ) && ( at_begin < 2 ) ){ + + // folgenden Teil auslagern als Funkton IncrementSnippetIndex() + if( pol_inf_vec.at( a_snippet_index.edge ).size() > ( a_snippet_index.snip + 1 ) ){ + a_snippet_index.snip += 1; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + } + else if( a_snippet_index.edge < 3 ){ + a_snippet_index.edge += 1; + a_snippet_index.snip = 0; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + } + else{ + a_snippet_index.edge = 0; + a_snippet_index.snip = 0; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + at_begin++; + } + // ende auszulagern als Funkton IncrementSnippetIndex() + + if( ( static_cast( a_snippet.why_here.x ) != static_cast( next_snippet.why_here.x ) ) || ( static_cast( a_snippet.why_here.y ) != static_cast( next_snippet.why_here.y ) ) ){ + + LegacyWorldCoordinate3D a_voxel_coordinate; + a_voxel_coordinate.x = (( a_snippet.why_here.x + next_snippet.why_here.x ) * 0.5); + a_voxel_coordinate.y = (( a_snippet.why_here.y + next_snippet.why_here.y ) * 0.5); + a_voxel_coordinate.z = the_dose_index.z; + + LegacyWorldCoordinate3D a_world_coordinate = GetWorldCoordinate( a_voxel_coordinate ); + + /* + std::cout<< " a_voxel_coordinate.x : " << a_voxel_coordinate.x < 0 ){ + a_snippet_index.snip -= 1; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + } + else if( a_snippet_index.edge > 0 ){ + a_snippet_index.edge -= 1; + a_snippet_index.snip = pol_inf_vec.at( a_snippet_index.edge ).size() - 1; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + } + else{ + a_snippet_index.edge = 3; + a_snippet_index.snip = pol_inf_vec.at( a_snippet_index.edge ).size() - 1; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + at_end++; + } + // ende auszulagern als Funkton DecrementSnippetIndex() + + + if( ( static_cast( a_snippet.why_here.x ) != static_cast( next_snippet.why_here.x ) ) || ( static_cast( a_snippet.why_here.y ) != static_cast( next_snippet.why_here.y ) ) ){ + + LegacyWorldCoordinate3D a_voxel_coordinate; + a_voxel_coordinate.x = (( a_snippet.why_here.x + next_snippet.why_here.x ) * 0.5); + a_voxel_coordinate.y = (( a_snippet.why_here.y + next_snippet.why_here.y ) * 0.5); + a_voxel_coordinate.z = the_dose_index.z; + + + LegacyWorldCoordinate3D a_world_coordinate = GetWorldCoordinate( a_voxel_coordinate ); + + Contour contour(the_polygon); + inside = contour.pointInPolygon( a_world_coordinate ); + + + // folgenden Teil auslagern als Funkton IncrementSnippetIndex() + if( pol_inf_vec.at( a_snippet_index.edge ).size() > ( a_snippet_index.snip + 1 ) ){ + a_snippet_index.snip += 1; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + } + else if( a_snippet_index.edge < 3 ){ + a_snippet_index.edge += 1; + a_snippet_index.snip = 0; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + } + else{ + a_snippet_index.edge = 0; + a_snippet_index.snip = 0; + next_snippet = pol_inf_vec.at( a_snippet_index.edge ).at( a_snippet_index.snip ); + + } + // ende auszulagern als Funkton IncrementSnippetIndex() + + current_static_index_ref.snip = a_snippet_index.snip; + current_static_index_ref.edge = a_snippet_index.edge; + + got_it = true; + + } + + } + } + + } + } + + } + + return got_it; + + } + + + void PolygonInfo::ResetSnippetIndicees(){ + + current_static_index.edge = 0; + current_static_index.snip = 0; + current_moving_index.edge = 0; + current_moving_index.snip = 0; + CopySnippetIndex( current_static_index , begin_index ); + + } + + + void PolygonInfo::SetIndexIdentical( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_to_ref , IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_from_ref ){ + + intersection_index_to_ref.point_index = intersection_index_from_ref.point_index; + intersection_index_to_ref.intersection_index = intersection_index_from_ref.intersection_index; + intersection_index_to_ref.column_raw_or_unified = intersection_index_from_ref.column_raw_or_unified; + + } + + + + + + + + +}//namespace + }//namespace +}//namespace + + + diff --git a/code/masks/rttbPolygonInfo_LEGACY.h b/code/masks/rttbPolygonInfo_LEGACY.h new file mode 100644 index 0000000..17b1d7c --- /dev/null +++ b/code/masks/rttbPolygonInfo_LEGACY.h @@ -0,0 +1,449 @@ +// ----------------------------------------------------------------------- +// 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 __PolygonInfo_H +#define __PolygonInfo_H + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbStructure.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbField_LEGACY.h" +#include "rttbMaskType_LEGACY.h" +#include "rttbContour_LEGACY.h" +#include "rttbIterativePolygonInTermsOfIntersections_LEGACY.h" +#include +#include + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + + + typedef std::vector< std::vector< std::vector< IterativePolygonInTermsOfIntersections::WhichIntersection > > > edge_intersections_intersection_coord_type; + typedef std::vector< std::vector< IterativePolygonInTermsOfIntersections::WhichIntersection > > intersection_coord_type; + typedef std::vector< std::vector< IterativePolygonInTermsOfIntersections::WhichIntersection > >::iterator intersection_coord_iterator_type; + + struct VoxelIntersectionIndex{ + + public: + + unsigned int next_intersection; + unsigned int intersection_douplication; + + VoxelIntersectionIndex(){ + next_intersection = 0; + intersection_douplication = 0; + } + + void reset(){ + next_intersection = 0; + intersection_douplication = 0; + } + + + }; + + + struct IntersectionsThisVoxel{ + + public: + + IntersectionsThisVoxel(){ + polygon_number = -1; + corner_intersections_intersection_coord.resize(4); + edge_intersections_intersection_coord.resize(4); + edge_intersections_vox_coord.resize(4); + } + + ~IntersectionsThisVoxel(){}; + + int GetPolygonNumber(){ + return polygon_number; + } + + void SetPolygonNumber( int polygon_number_in ){ + polygon_number = polygon_number_in; + } + + + std::vector< std::vector< std::vector< double > > > edge_intersections_vox_coord; + intersection_coord_type corner_intersections_intersection_coord; + edge_intersections_intersection_coord_type edge_intersections_intersection_coord; + + private: + + int polygon_number; + + }; + + + + + + + + // Die folgende Klasse handhabt die konkrete Speicherung der Schnittpunkte. + // Kennt sich also mit der Speicherung der Schnittpunkte der Konturen mit den Voxelkanten aus. + // Information auf Struktur-Polygonebene. + // Nimmt PolygonInfo den Zugriff auf diese Information ab. + + + + + + // PolygonInfo: Handhabung der Polygonschnipsel die ein Voxel anschneiden : + // Information auf Voxelebene. + // Speichert diese derart, dass die Schnittpunkte mit der Voxelkante in Uhrzeigersinn-Reihenfolge repraesentiert sind. + // Berechnet daraus dann den Anteil des Voxels, der Innerhalb der betrachteten Kontur liegt. + + // PolygonInfo ist ein Objekt, das alle noetigen Informationen zur Berechnung des Polygonsets enthaelt und sinnvoll + //strukturiert. Es berechnet dann anhand der Gesamtheit der schneidenden Polygonschnipsel den Innenanteil. + + class PolygonInfo{ + + + public: + + + + + PolygonInfo(){}; + PolygonInfo( const LegacyPolygonType& the_polygon_in, int polygon_number_in, field_content_type brightness_outside_in, field_content_type brightness_inside_in , FieldOfScalar* MaskFieldReceived_in , const PolygonInTermsOfIntersections& Polygon_Intersections_In , const LegacyDoseVoxelIndex3D& aDoseIndex , core::GeometricInfo& GInfIn, const IntersectionsThisVoxel& intersections_this_voxel_in ); + + ~PolygonInfo(){}; + LegacyPolygonSequenceType& GetResultingSequence() { return resulting_sequence; }; + bool CreatePolygonSequence(); + + + + struct PolygonSnippet{ + + PolygonSnippet(){ + why_here.x =0; + why_here.y = 0; + point_of_interest_start.x =0; + point_of_interest_start.y = 0; + point_of_interest_end.x =0; + point_of_interest_end.y = 0; + is_edge = 0; + characterize = unknown; + angle_charact = (-1); + i_have_been_processed = false; + } + + + //Wesentlicher Punkt: + //Verantwortlich fuer die Position dieses Snipplets an dieser Stelle Position der Kante + //(die Snipplets sollen anhand ihrer Schnittpositionen entlang der Kante eingeordnet werden und es soll + //hier gekennzeichnet werden, welcher der beiden Schnittpunkte oder Beruehrpunkte fuer die Einordnung and dieser Stelle wesentlich war. + //Ein Polygonsnippet kommt in den Kantenvektoren jeweils doppelt vor, denn es hat zwei Schnitt/Beruehrpunkte. + //Einer der beiden begrenzenden Schnittpunkte/Beruehrpunkte mit der Voxlekante kann auch der Eckpunkt sein. + //In vielen Faellen wird der Eckpunkt jedoch selbst kein Beruehr/Schnittpunkt sein. + LegacyWorldCoordinate2D why_here; + + // Anfangsschnittpunkt + LegacyWorldCoordinate2D point_of_interest_start; + + // Endschnittpunkt + LegacyWorldCoordinate2D point_of_interest_end; + + // Polygonstueckchen zwischen den Schnitt-/Beruehrpunkten + std::vector snippet_intermediate_content; + + bool i_have_been_processed; + + // handelt es sich um eine Ecke? + bool is_edge; + + //characterize ist ein Integer: + //sagt, ob es sich hier + + //a) um einen wahren Schnittpunkt handelt + // drinnen drausen Wechsel . + //Sein Wert ist dann 0. + //Drinnen Drausen Wechsel geschieht. + + //b) um einen Beruehrpunkt vom Typ 1 : + // Beide Snippets ganz auf der Kante kein Wechsel . + //Kontur und Voxelkante beider angrenzenden Polygonsnipplets parallell und deckungsgleich, die Voxelkante wird von beiden zugehoerigen + //Polygonsnippets nicht verlassen + //Sein Wert ist dann 1 + //Ein Drinnen Drausen Wechsel findet hier nicht statt. + + //c) um einen Beruehrpunkt vom Typ 2: + //Polygon laueft halb raus. Wechsel je nachdem was folgt. + //Das Erste der beiden angrenzenden Polygonsnipplets endet direkt auf der Voxelkante und ist nicht parallel, liegt also nicht in seinder + //ganzen Laenge auf der Kante. Es kommt von drinnen, aus dem Voxel heraus. + //Das zweite der Angrenzenden Polygonsnippets verlaueft auf seiner angrenzenden Seite zunaechst auf der Kante. + //Ein Wechsel von drausen nach drinnen findet nur statt, falls im Folgenden das an das zweite Snippet + //angrenzende nach drausen weiterverlaueft. + //Der Wert des integers ist 2 + + //d) Um einen Beruehrpunkt vom Typ 3: + //Polygon laueft halb rein. + //Wechsel nach drausen, je nachdem was folgt. + //Das zweite der beiden angrenzenden Polygonsnipplets endet direkt auf der Voxelkante und ist nicht parallel, liegt also nicht in seinder + //ganzen Laenge auf der Kante. Es Fuehrt nach drinnen ins voxel hinein. + //Das erste der Angrenzenden Polygonsnippets verlaueft auf seiner angrenzenden Seite zunaechst auf der Kante. + //Ein wechsel von drinnen nach drausen findet nur statt, falls das Polygon zuletzt von drausen auf die Voxlekante zugelaufen ist. + //Der Wert des integers ist dann 3 + + //e) Um einen Beruehrpunkt vom Typ 4: + // Polygon laueft halb rein. + //Das Erste der beiden angrenzenden Polygonsnipplets endet direkt auf der Voxelkante und ist nicht parallel, liegt also nicht in seinder + //ganzen Laenge auf der Kante. Es kommt von draussen auf das Voxel zu. + //Das zweite der Angrenzenden Polygonsnippets verlaueft auf seiner angrenzenden Seite zunaechst auf der Kante. + // Ein Wechsel findet dann statt, falls das Polygon im Folgenden nach drinnen weiter verlaueft. + //Der Wert des integers ist dann 4 + + //f) Um einen Beruehrpunkt vom Typ 5: + //Das Zweite der beiden angrenzenden Polygonsnipplets endet direkt auf der Voxelkante und ist nicht parallel, liegt also nicht in seinder + //ganzen Laenge auf der Kante. Es Fuehrt nach drausen aus dem Voxel heraus. + //Das erste der Angrenzenden Polygonsnippets verlaueft auf seiner angrenzenden Seite zunaechst auf der Kante. + //Ein Wechsel findet dann statt, wenn das Polygon zuvor aus dem inneren des Voxels heraus kam. + //Der Wert des integers ist dann 5 + + + //g) Beruehrpunkte vom Typ 6 : + // Kein Wechsel aber eventuell ein Innenbereich zu erkennen und als Polygon anzuhaengen. + //(Beide angrenzenden Polygonsnipplets enden auf der Voxelkante und sind zur Voxelkante nicht paralell) + // Der Wert des integers ist dann 6. + // Falls der eine Schnipsel von drinnen kommt und der andere nach drinnen weiterlaueft, muss man einmal im Kreis rum fahren und + // das Polygon, das dabei eintsteht anhaengen. + + //h) Beruehrpunkte vom Typ 7 : + // Beruehrt, beide angrenzenden Polygonstuecke drausen + + // Ecke 8 + // Ecke darf nur dann gewaehlt werden, wenn die Ecke mit sicherheit kein Schittpunkt oder Beruherpunkt mit der Kontur ist + // CheckCornersOutside ist davon abhaengig. + + // Unklar -1. + + CharacterizeIntersection characterize; + + float angle_charact; + + }; + + typedef std::vector snippet_vector; + typedef std::vector PolygonInfoVectorOfEdges; + typedef snippet_vector::iterator snippet_vector_iterator; + + + enum WhichSector{ not_known = -1 , on_edge_twelve_o_clock = 0 , section_twelve_to_three_o_clock = 1 , on_edge_three_o_clock = 2 , section_three_to_six_o_clock = 3 , on_edge_six_o_clock = 4 , section_six_to_nine_o_clock = 5 , on_edge_nine_o_clock = 6, section_nine_to_twelve_o_clock = 7 }; + enum EdgeStatus{ unclear = -1 , inside = 0 , edge_or_outside = 1 }; + EdgeStatus edge_status; + + struct SnippetIndex{ + unsigned int edge; + unsigned int snip; + }; + + bool selfTest(); + IterativePolygonInTermsOfIntersections GetIterativePolygoneInTermsOfIntersections(); + + // Constructor of PolygonInfo calls function SetIntersectionsAndResetIntersectionIndex + // which calls function CreateUnifiedListOfRawAndColumnIntersections + // success of this function is checked here. Calculation of the intersection points is checked as well in this process. + bool TestCreateUnifiedListOfRawAndColumnIntersections( PolygonInTermsOfIntersections Expected_Unified_Polygon_Intersections ); + bool CompareCalculatedAndExpectedResultingSequenceForTest( LegacyPolygonSequenceType& the_resulting_sequence_ref ); + + // Checks the member pol_inf_vec wihch is a vector that consists of four vectors of PolygonSnippets + // representing the voxel edges. + bool CheckResultingPolygonInfoVectorOfEdgesForTestStructure( PolygonInfoVectorOfEdges pol_inf_vec_expected ); + void ReferenceIterativePolygonForTest( IterativePolygonInTermsOfIntersections& it_poly_ref ){ it_poly_ref = it_poly; } + + + private: + + + void CalcSnippetVectors(); + void SetCurrentMovingProcessed(); + void AppendEdgeToVector( std::vector& vector , int edge_internal ); + bool GetCurrentMovingSnippet( PolygonSnippet& snipp_ref ); + bool CurrentSnippetIsProcessed(); + void ShowResultingSequence(); + void ShowPolygonInTermsOfIntersections(); + void ShowSnippet( PolygonSnippet a_snippet ); + void DoCharacterize( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , CharacterizeIntersection& charact_ref , std::vector anti_outside_combination_vector , WhichSector& inside_ref , WhichSector& preceding_edge_ref , WhichSector& following_edge_ref ); + void SetDoseIndex( const LegacyDoseVoxelIndex3D& aDoseIndex ); + void AppendEdges( PolygonInfoVectorOfEdges& pol_inf_vec_ref ); + void CheckEdgeIntersections(); + void DoAppendIntermediateEdgeUpToThisSnippet( PolygonSnippet& snipp_ref , LegacyWorldCoordinate2D why_here , int the_previous_edge , int the_current_edge ); + int EdgeDist( int edge_old , int the_current_edge ); + void IncrementEdge( int& edge_increment ); + bool GetCornerFromVoxelIntersections( unsigned int the_index, SnippetIndex edge_index , IterativePolygonInTermsOfIntersections::WhichIntersection& intersect_index ); + int GetNumberOfSnppetsThisEdge( SnippetIndex edge_index ); + bool SetToNextIntersectonAlongThisEdgeAndReturnDistanceBasedOnVoxelIntersections( LegacyWorldCoordinate2D& closest_local_current_position_ref , int current_edge, VoxelIntersectionIndex next_voxel_intersection ); + snippet_vector CreateSnippetVector( int number_of_snippets_this_edge , PolygonSnippet template_snip ); + snippet_vector SortClockwise( snippet_vector snip_vec_local ); + void InsertToSnippetVectorFront( snippet_vector snip_vec_local , SnippetIndex edge_index ); + void RemoveFromLocalSnippetVector( snippet_vector& snip_vec_local , GridIndexType which_one ); + void CheckEdegeTouchCharacteristicsAndAppendIntermediateContent( SnippetIndex edge_index ); + void WorkWithForwardAndBackwardEdgeSnippet( int which_one, float& angle_charact_a , float& angle_charact_b , PolygonSnippet& forward_snippet_ref , PolygonSnippet& backward_snippet_ref , PolygonSnippet& edge_snippet_ref , std::vector& snip_vec_local , int& erased ); + void WorkWithForwardEdgeSnippet( LegacyWorldCoordinate2D edge_coord, LegacyWorldCoordinate3D contour_point_one, int which_one, float& angle_charact_a , float& angle_charact_b , PolygonSnippet& forward_snippet_ref , PolygonSnippet& backward_snippet_ref , PolygonSnippet& edge_snippet_ref , std::vector& snip_vec_local , int& erased ); + void WorkWithBackwardEdgeSnippet( LegacyWorldCoordinate2D edge_coord, LegacyWorldCoordinate3D contour_point_two , int which_one, float& angle_charact_a , float& angle_charact_b , PolygonSnippet& forward_snippet_ref , PolygonSnippet& backward_snippet_ref , PolygonSnippet& edge_snippet_ref , std::vector& snip_vec_local , int& erased ); + + + + // The following function is just for test and easy debugging. + void GetSectorsAndSetCharacteristicsDoubleCheck( SnippetIndex edge_index, LegacyWorldCoordinate2D edge_coord , LegacyWorldCoordinate3D& contour_point_one , LegacyWorldCoordinate3D& contour_point_two ); + void GetSectors( LegacyWorldCoordinate2D edge_coord , LegacyWorldCoordinate3D& contour_point_one , LegacyWorldCoordinate3D& contour_point_two, WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ); + void GetSectorFirstPoint( LegacyWorldCoordinate2D edge_coord , LegacyWorldCoordinate3D& contour_point_one , WhichSector& sector_first_point_ref ); + + // Anmerkung : Funktion verdoppelt die obige. Kann man loeschen und ihre Aufrufe durch GetSectorFirstPoint ersetzen, + // wobei GetSectorFirstPoint dann in GetSectorThisPoint umbenannt werden sollte. + void GetSectorSecondPoint( LegacyWorldCoordinate2D edge_coord , LegacyWorldCoordinate3D& contour_point_two, WhichSector& sector_second_point_ref ); + CharacterizeIntersection GetCharacteristicFirstCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ); + void GetAntiOutsideCombinationFirstCorner( std::vector& anti_outside_combination_vector_ref ); + void GetInsideFirstCorner( WhichSector& inside_ref ); + void GetPrecedingEdgeFirstCorner( WhichSector& preceding_edge_ref ); + void GetFollowingEdgeFirstCorner( WhichSector& following_edge_ref ); + bool BothOnEdge( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& preceding_edge_ref , WhichSector& following_edge_ref ); + bool FromEdgeIn( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& preceding_edge_ref , WhichSector& inside_ref ); + bool FromEdgeOut( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& preceding_edge_ref , std::vector second_sector_vector ); + bool OutToEdge( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& following_edge_ref , std::vector second_sector_vector ); + bool InToEdge( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& following_edge_ref , WhichSector& inside_ref ); + bool BothIn( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& inside_ref ); + bool BothOut( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , std::vector sector_vector ); + bool OneInOneOut( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref , WhichSector& inside_ref , std::vector first_sector_vector ); + bool FirstSectorIsOneOfTheseAndSecondSectorIsOneOfThose( WhichSector& sector_first_point_ref , std::vector first_sector_vector , WhichSector& sector_second_point_ref , std::vector second_sector_vector ); + bool FirstSectorIsOneOfTheseAndSecondSectorIsNoneOfThose( WhichSector& sector_first_point_ref , std::vector first_sector_vector , WhichSector& sector_second_point_ref , std::vector second_sector_vector ); + bool FirstSectorIsNoneOfTheseAndSecondSectorIsNoneOfThose( WhichSector& sector_first_point_ref , std::vector first_sector_vector , WhichSector& sector_second_point_ref , std::vector second_sector_vector ); + CharacterizeIntersection GetCharacteristicSecondCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ); + void GetAntiOutsideCombinationSecondCorner( std::vector& anti_outside_combination_vector_ref ); + void GetInsideSecondCorner( WhichSector& inside_ref ); + void GetPrecedingEdgeSecondCorner( WhichSector& preceding_edge_ref ); + void GetFollowingEdgeSecondCorner( WhichSector& following_edge_ref ); + CharacterizeIntersection GetCharacteristicThirdCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ); + void GetAntiOutsideCombinationThirdCorner( std::vector& anti_outside_combination_vector_ref ); + void GetInsideThirdCorner( WhichSector& inside_ref ); + void GetPrecedingEdgeThirdCorner( WhichSector& preceding_edge_ref ); + void GetFollowingEdgeThirdCorner( WhichSector& following_edge_ref ); + CharacterizeIntersection GetCharacteristicFourthCorner( WhichSector& sector_first_point_ref , WhichSector& sector_second_point_ref ); + void GetAntiOutsideCombinationFourthCorner( std::vector& anti_outside_combination_vector_ref ); + void GetInsideFourthCorner( WhichSector& inside_ref ); + void GetPrecedingEdgeFourthCorner( WhichSector& preceding_edge_ref ); + void GetFollowingEdgeFourthCorner( WhichSector& following_edge_ref ); + bool AppendNextIntersectionAndCorrespoindingPolygonSnippets( snippet_vector& snip_vec_ref_local , snippet_vector& snip_vec_ref , int current_edge, VoxelIntersectionIndex& next_voxel_intersection ); + float SetToNextIntersectionAlongThisEdgeAndReturnDistance( LegacyWorldCoordinate2D& closest_local_current_position_ref , float distance_current_position , int current_edge ); + bool IsTheSameEdgeButNoCorner( LegacyWorldCoordinate2D local_current_position , LegacyWorldCoordinate2D current_position , int current_edge ); + bool AddSnippetsThisIntersection( snippet_vector& snip_vec_ref , LegacyWorldCoordinate2D closest_local_current_position ); + bool JustEdge( PolygonSnippet& snippet_ref ); + void Switch( PolygonSnippet& snippet_one_ref , PolygonSnippet& snippet_two_ref ); + void CopySnippet( PolygonSnippet& to_snippet, PolygonSnippet& snip_in ); + bool GoForwardAndCreatePolygonSnippet( PolygonSnippet& forward_snippet_ref ); + bool GoBackwardAndCreatePolygonSnippet( PolygonSnippet& backward_snippet_ref); + bool CheckPolygonIsInsideForward( float& angle_charact_forward ); + void GetAngle( LegacyWorldCoordinate2D the_next_point , LegacyWorldCoordinate2D the_first_point , float& angle_charact ); + bool CheckPolygonIsInsideBackward( float& angle_charact_backward ); + bool CheckPointInVoxelRegardingDoseIndex( LegacyWorldCoordinate2D voxel_coordinate_to_be_checked ); + double GetDistanceNextEdge( int current_edge ); + float GetDistanceAlongEdge( LegacyDoseVoxelIndex3D edge_position , LegacyWorldCoordinate2D local_position ); + float GetPeriodicDist( float distance_current_position_local , float distance_current_position ); + double GetClockwiseDist( double distance_refer_to , double distance_compare ); + void AppendNewPolygonToResultingSequence(); + void AppendVoxelContour(); + void SlicePositionUnifyAndCheckPeriodicSequence(); + int GetMaxNumberSnippets(); + void GoGetPoly( bool& do_continue_ref , bool& next_is_edge_ref, int& continue_counter_ref , int& max_num ); + void AppendToResultingSequence( PolygonSnippet& snipp_ref ); + void DoAppendForward( PolygonSnippet& snipp_ref , int which_polygon ); + void DoAppendBackward( PolygonSnippet& snipp_ref , int which_polygon ); + void DoInsertForward( PolygonSnippet& snipp_ref , int which_polygon ); + void DoInsertBackward( PolygonSnippet& snipp_ref , int which_polygon ); + void AddFirstForward( PolygonSnippet& snipp_ref , int which_polygon ); + void GoAlongSnippetToNextIntersectionAndAppendToResultingSequence( PolygonSnippet& snipp_ref , int& continue_counter_ref ); + bool IsCorresponding( PolygonSnippet& snipp_ref_compare , PolygonSnippet& snipp_ref ); + LegacyWorldCoordinate2D GetOtherEnd( PolygonSnippet& snipp_ref ); + int GetSnippetIndexDistance( SnippetIndex behind_snip_index , SnippetIndex front_snip_index ); + bool SnippetIndexIdentical( SnippetIndex first , SnippetIndex second ); + void FindCorrespondingOneAndSetMovingIndexAndSetContinueCounter( PolygonSnippet& snipp_ref , int& continue_counter_ref ); + bool TryGetPolygonsnippetEdge( int& continue_counter_ref , int the_previous_edge ); + bool IsOtherEdge( PolygonSnippet& snipp_ref , int the_previous_edge ); + void IncrementSnippetIndex( SnippetIndex& the_snippet_index ); + bool GetNextIsEdge( SnippetIndex snip_index ); + bool DoContinue( int& max_num , int& continue_counter_ref ); + void CopySnippetIndex( SnippetIndex snip_index_from , SnippetIndex& snip_index_to ); + void ToZero( SnippetIndex snip_index ); + void CheckInside( SnippetIndex current_moving_index , bool& it_is_inside_ref , bool& inside_switch_ref ); + void SetEdgeStatus( CharacterizeIntersection characterize ); + bool SetCurrentStaticIndexKnownInsideOrOutside( SnippetIndex& current_static_index_ref , bool& nothing_to_do_ref ); + bool CheckCornersOutside( SnippetIndex& current_static_index_ref ); + bool CheckCornersInside( SnippetIndex& current_static_index_ref ); + bool CheckThereIsSuchNeighbour( Uint16 x , Uint16 y , Uint16 z , int shift_x , int shift_y , int shift_z , field_content_type brightness ); + bool CheckCenterSurroundingStructure( bool& inside ); + LegacyWorldCoordinate3D GetWorldCoordinate( LegacyWorldCoordinate3D actually_a_voxel_coordinate ); + void SetCurrentPosition( const LegacyDoseVoxelIndex3D& aDoseIndex ); + bool SnippetIndexIsIdentical( SnippetIndex first_index , SnippetIndex second_index ); + bool CheckPointOnEdgeOutside( SnippetIndex& current_static_index_ref , bool& inside ); + void ResetSnippetIndicees(); + void SetIndexIdentical( IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_to_ref , IterativePolygonInTermsOfIntersections::WhichIntersection& intersection_index_from_ref ); + void SetCurrentPositionToEdge( int voxel_edge ); + bool SameSnippet( PolygonSnippet& pol_snip_a , PolygonSnippet& pol_snip_b ); + void GetInCombinationFirstCorner( std::vector& in_combination_vector_ref ); + void GetInCombinationSecondCorner( std::vector& in_combination_vector_ref ); + void GetInCombinationThirdCorner( std::vector& in_combination_vector_ref ); + void GetInCombinationFourthCorner( std::vector& in_combination_vector_ref ); + void IncrementVoxelIntersectionIndex( int current_edge, VoxelIntersectionIndex& vox_inters_index , bool& changed_location ); + + // begin some functions and stuff for test + bool CheckConstructurSuccessForTest(); + // end some functions and stuff for test + + PolygonInfoVectorOfEdges pol_inf_vec; + LegacyPolygonSequenceType resulting_sequence; + FieldOfScalar* MaskFieldReceived; + field_content_type brightness_outside; + field_content_type brightness_inside; + LegacyDoseVoxelIndex3D the_dose_index; + LegacyPolygonType the_polygon; + + IterativePolygonInTermsOfIntersections it_poly; + IntersectionsThisVoxel voxel_intersections; + + LegacyWorldCoordinate2D current_position; + + int current_edge; + + SnippetIndex current_static_index; + SnippetIndex current_moving_index; + SnippetIndex begin_index; + + core::GeometricInfo GInformation; + + }; +} + + } +} + + + + + + + +#endif diff --git a/code/masks/rttbSelfIntersectingStructureException.cpp b/code/masks/rttbSelfIntersectingStructureException.cpp new file mode 100644 index 0000000..a747cc0 --- /dev/null +++ b/code/masks/rttbSelfIntersectingStructureException.cpp @@ -0,0 +1,39 @@ +// ----------------------------------------------------------------------- +// 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 "rttbSelfIntersectingStructureException.h" + +namespace rttb{ + namespace masks{ + + const char* SelfIntersectingStructureException::what() const throw() { + return rttb_what.c_str(); + } + + const char* SelfIntersectingStructureException::GetNameOfClass() const{ + return "SelfIntersectingStructureException"; + } + + }//end namespace masks + }//end namespace rttb diff --git a/code/masks/rttbSelfIntersectingStructureException.h b/code/masks/rttbSelfIntersectingStructureException.h new file mode 100644 index 0000000..bdd8692 --- /dev/null +++ b/code/masks/rttbSelfIntersectingStructureException.h @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// 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 __SELF_INTERSECTING_STRUCTURE_EXCEPTION_H +#define __SELF_INTERSECTING_STRUCTURE_EXCEPTION_H + +#include +#include + +#include "rttbException.h" + +namespace rttb{ + namespace masks{ + + /*! @class SelfIntersectingStructureException + @brief This exception will be thrown in case a Structure intersects with itself in a context where + this is not allowed. + */ + class SelfIntersectingStructureException: public core::Exception + { + public: + SelfIntersectingStructureException(const std::string& aWhat):Exception(aWhat){} + + virtual ~SelfIntersectingStructureException() throw() {} + + /*! @brief Get the exception description + */ + virtual const char * what() const throw(); + + /*! @brief Get the name of the exception class + */ + virtual const char* GetNameOfClass() const; + }; + } + } + +#endif diff --git a/code/masks/rttbStructure_LEGACY.cpp b/code/masks/rttbStructure_LEGACY.cpp new file mode 100644 index 0000000..a45e02f --- /dev/null +++ b/code/masks/rttbStructure_LEGACY.cpp @@ -0,0 +1,256 @@ +// ----------------------------------------------------------------------- +// 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 "rttbStructure_LEGACY.h" + +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbContour_LEGACY.h" + +//#include +//#include + +#include +#include +#include + +#include +#include + + + +namespace rttb{ + namespace masks{ + namespace legacy{ + + StructureLegacy::StructureLegacy(const core::Structure& copy) { + this->_structureVector=copy.getStructureVector(); + this->_legacyStructureVector.clear(); + this->_strUID=copy.getUID(); + this->_label=copy.getLabel(); + + //initialize legacy structureVector + LegacyWorldCoordinate3D p; + if (!(_structureVector.size() == _legacyStructureVector.size())){ //needs to be converted + _legacyStructureVector.clear(); + PolygonSequenceType::const_iterator itVV=_structureVector.begin(); + for(;itVV!=_structureVector.end();itVV++){ + PolygonType v1=*itVV; + rttb::masks::legacy::LegacyPolygonType legacyPolygon; + for(PolygonType::const_iterator itPP=(*itVV).begin();itPP!=(*itVV).end();itPP++){ + p.x = itPP->x(); + p.y = itPP->y(); + p.z = itPP->z(); + legacyPolygon.push_back(p); + } + _legacyStructureVector.push_back(legacyPolygon); + } + } + } + + StructureLegacy::~StructureLegacy(){ + } + + const PolygonSequenceType& StructureLegacy::getStructureVector() const{ + return _structureVector; + } + + + const LegacyPolygonSequenceType& StructureLegacy::getLegacyStructureVector() const{ + + return _legacyStructureVector; + } + + VolumeType StructureLegacy::getVolume() const{ + + /*check precondition: contourGeometricType must be closed planar! */ + for(int i=0;i_contourGeometricTypeVector.size();i++){ + std::cout << _contourGeometricTypeVector[i]<x<_legacyStructureVector.size(); + std::cout <<"numberOfContours:"<getVolume() < str2.getVolume()){ + LegacyPolygonSequenceType::const_iterator itVV; + for(itVV=this->_legacyStructureVector.begin();itVV!=_legacyStructureVector.end();itVV++){ + LegacyPolygonType::iterator itV; + LegacyPolygonType vectorW=*itVV; + for(itV=vectorW.begin();itV!=vectorW.end();itV++){ + LegacyWorldCoordinate3D p=*itV; + if(str2.pointInStructure(p)){ + count++; + //std::cout << "p: "<getNumberOfEndpoints()); + } + //this structure bigger than str2 + else{ + LegacyPolygonSequenceType::const_iterator itVV; + const LegacyPolygonSequenceType& str2Vector = str2.getLegacyStructureVector(); + for(itVV=str2Vector.begin();itVV!=str2Vector.end();itVV++){ + LegacyPolygonType::iterator itV; + LegacyPolygonType vectorW=*itVV; + for(itV=vectorW.begin();itV!=vectorW.end();itV++){ + LegacyWorldCoordinate3D p=*itV; + if(this->pointInStructure(p)){ + count++; + } + /*else + std::cout << "p: "<= _legacyStructureVector.at(0).at(0).z && aPoint.z <=_legacyStructureVector.at(size-1).at(0).z){ + + int polygonNr=0; + //get correspoinding slice + for(int i=0;i= p1.z && aPoint.z +#include + +#include "rttbBaseType.h" +#include "rttbBaseType_LEGACY.h" +#include "rttbStructure.h" + + +namespace rttb{ + + namespace masks{ + namespace legacy{ + + /*! @class Structure + @brief This is a class representing a RT Structure + */ + class StructureLegacy + { + private: + /*! @brief WorldCoordinate3D in mm + */ + PolygonSequenceType _structureVector; + + //legacy data structure for faster computation in original toolbox voxelization + LegacyPolygonSequenceType _legacyStructureVector; + + /*! @brief Contour Geometric Type using DICOM-RT definition (3006,0042). + * POINT: indicates that the contour is a single point, defining a specific location of significance. + * OPEN_PLANAR: indicates that the last vertex shall not be connected to the first point, and that all points + * in Contour Data (3006,0050) shall be coplanar. + * OPEN_NONPLANAR: indicates that the last vertex shall not be connected to the first point, and that the points + * in Contour Data(3006,0050) may be non-coplanar. + * CLOSED_PLANAR: indicates that the last point shall be connected to the first point, where the first point is + * not repeated in the Contour Data. All points in Contour Data (3006,0050) shall be coplanar. + */ + + // wo und wie wird das Initialisier? Ist Vector die geeignete Datenstruktur? + std::vector _contourGeometricTypeVector; + + /*! @brief Structure UID*/ + IDType _strUID; + + /*! @brief Structure Label*/ + StructureLabel _label; + + + + + + public: + /** copy constructor + * @param copy Structure object to be copied + */ + StructureLegacy(const core::Structure& copy); + + + /*! @brief Structure Destructor + */ + ~StructureLegacy(); + + /*! @brief Get the structure vector + * @Return Return a LegacyPolygonSequenceType + */ + const LegacyPolygonSequenceType& getLegacyStructureVector()const; + + /*! @brief Get the structure vector + * @Return Return a PolygonSequenceType + */ + const PolygonSequenceType& getStructureVector() const; + + + /*! @brief Get the volume of this Structure. Calculation using volume = 1/3 * h* (S1+S2+(S1*S2)^0,5) + @return Return the absolute volume in mm3 + */ + VolumeType getVolume() const; + + /*! @brief Structure operation: contain + @return Return the contain factor of str2 in this structure + @brief 0 + +#include "rttbBaseType.h" + +namespace rttb{ + namespace models{ + + typedef double BioModelParamType; + typedef double BioModelValueType; + + const double infinity = 1e30; + + struct TcpParams { + double alphaMean; + double alphaVariance; + double rho; + std::vector volumeVector; + std::vector bedVector; + }; + + + } + } + +#endif \ No newline at end of file diff --git a/code/models/rttbBioModel.cpp b/code/models/rttbBioModel.cpp new file mode 100644 index 0000000..91dcba9 --- /dev/null +++ b/code/models/rttbBioModel.cpp @@ -0,0 +1,48 @@ +// ----------------------------------------------------------------------- +// 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 +#define _USE_MATH_DEFINES +#include +#include + +#include "rttbBioModel.h" + +namespace rttb{ + namespace models{ + bool BioModel::init(const double doseFactor) + { + if(_dvh!=NULL) + { + _value= this->calcModel(doseFactor); + return true; + } + return false; + } + + void BioModel::setDVH(const DVHPointer aDVH){_dvh=aDVH;} + + const BioModel::DVHPointer BioModel::getDVH() const{return _dvh;} + + const BioModelValueType BioModel::getValue() const{ return _value; } + + }//end namespace models + }//end namespace rttb diff --git a/code/models/rttbBioModel.h b/code/models/rttbBioModel.h new file mode 100644 index 0000000..00c318d --- /dev/null +++ b/code/models/rttbBioModel.h @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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 __BIO_MODEL_H +#define __BIO_MODEL_H + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbBaseTypeModels.h" + +namespace rttb{ + + namespace models{ + + /*! @class BioModel + @brief This is the interface class for biological models + */ + class BioModel + { + public: + typedef std::vector ParamVectorType; + typedef core::DVH::DVHPointer DVHPointer; + + protected: + DVHPointer _dvh; + + BioModelValueType _value; + + /*! @brief Calculate the model value + @param doseFactor scaling factor for the dose. The model calculation will use the dvh with each di=old + di*doseFactor. + */ + virtual BioModelValueType calcModel(const double doseFactor=1)=0; + + + public: + BioModel(): _value(0){}; + BioModel(DVHPointer aDvh): _dvh(aDvh), _value(0){}; + + /*! @brief Start the calculation. If any parameter changed, init() should be called again and return =true + before getValue() is called! + @return Return true if successful + */ + bool init(const double doseFactor=1); + + /*! @param aDVH must be a DVH calculated by a cumulative dose distribution, not a fraction DVH! + */ + void setDVH(const DVHPointer aDVH); + + const DVHPointer getDVH() const; + + /*! @brief Set parameter vector, where index of vector is the parameter ID. + */ + virtual void setParameterVector(const ParamVectorType aParameterVector)=0; + + virtual void setParameterByID(const int aParamId, const BioModelParamType aValue)=0; + + /*! @brief Get parameter by ID. + @return Return -1 if ID is not found. + */ + virtual const int getParameterID(const std::string aParamName) const=0; + + /*! @brief Get the value of biological model + @pre init() must be called and =true! + */ + const BioModelValueType getValue() const; + }; + + }//end namespace models + }//end namespace rttb + +#endif diff --git a/code/models/rttbBioModelCurve.cpp b/code/models/rttbBioModelCurve.cpp new file mode 100644 index 0000000..f1e593c --- /dev/null +++ b/code/models/rttbBioModelCurve.cpp @@ -0,0 +1,82 @@ +// ----------------------------------------------------------------------- +// 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 "rttbBioModelCurve.h" +#include "rttbDvhBasedModels.h" + +namespace rttb{ + namespace models{ + + CurveDataType getCurveDoseVSBioModel(BioModel& aModel, double normalisationDose, int aBin, + double minDose, double maxDose){ + CurveDataType _curveData; + + double minFactor=0, maxFactor=0; + + minFactor=minDose/normalisationDose; + maxFactor=maxDose/normalisationDose; + + double lastValue=0; + for(int i=0;igetDataDifferential(),_dvh->getDeltaD()*factor,_dvh->getDeltaV(), + "temporary","temporary"); + + boost::shared_ptr spDVH = boost::make_shared(variantDVH); + double eud=getEUD(spDVH,aModel.getA()); + + aModel.init(factor); + BioModelValueType value=aModel.getValue(); + _curveData.insert(CurvePointType(eud,value)); + } + return _curveData; + } + } + } \ No newline at end of file diff --git a/code/models/rttbBioModelCurve.h b/code/models/rttbBioModelCurve.h new file mode 100644 index 0000000..f3275f4 --- /dev/null +++ b/code/models/rttbBioModelCurve.h @@ -0,0 +1,55 @@ +// ----------------------------------------------------------------------- +// 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 __MODEL_CURVE_H +#define __MODEL_CURVE_H + +#include "rttbBioModel.h" +#include "rttbBaseType.h" +#include "rttbNTCPLKBModel.h" + +namespace rttb{ + namespace models{ + //map of dose value and model value + typedef std::map CurveDataType; + //pair of dose value and model value + typedef std::pair CurvePointType; + + /*! @brief Get the curve TCP/NTCP Value vs normalisationDose, normalisationDose variant between minDose and + maxDose. + @param bin the size of the map + @param minDose min value for x axis + @param maxDose max value for x axis + @param normalisationDose prescribed dose of the current _dvh or mean/maximum. + */ + CurveDataType getCurveDoseVSBioModel(BioModel& aModel, double normalisationDose, int aBin=201, + double minDose=0.1, double maxDose=150); + + /*! @brief Get the curve NTCP Value vs EUD, dvh variant between minFactor*deltaD and maxFactor*deltaD. + @param bin the size of the map + @param minFactor min factor for dvh deltaD + @param maxFactor max factor for dvh deltaD + */ + CurveDataType getCurveEUDVSBioModel(NTCPLKBModel& aModel, DoseCalcType maxFactor=10, + DoseCalcType minFactor=0.1, int aBin=201); + } + } +#endif \ No newline at end of file diff --git a/code/models/rttbBioModelScatterPlots.cpp b/code/models/rttbBioModelScatterPlots.cpp new file mode 100644 index 0000000..e85d567 --- /dev/null +++ b/code/models/rttbBioModelScatterPlots.cpp @@ -0,0 +1,221 @@ +// ----------------------------------------------------------------------- +// 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); + + double paramValue; + double probability; + int i = 0; + + while (i < numberOfPoints) + { + 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; + 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; + double probability; + + + // vary all parameters for each scattered point + int i = 0; + + while (i < numberOfPoints) + { + 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/rttbBioModelScatterPlots.h b/code/models/rttbBioModelScatterPlots.h new file mode 100644 index 0000000..269d47e --- /dev/null +++ b/code/models/rttbBioModelScatterPlots.h @@ -0,0 +1,90 @@ +// ----------------------------------------------------------------------- +// 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 __MODEL_SCATTER_H +#define __MODEL_SCATTER_H + +#include "rttbBioModel.h" + +namespace rttb{ + namespace models{ + + // maps dose to a pair of model value and probability + typedef std::multimap > ScatterPlotType; + + /*! @brief Get the points (TCP/NTCP Value, probability of the value) if 1 parameter vary from a normal- + distribution with mean=aMean, variance=aVariance. + @param aModel biological model for which the scatter plot will be generated + @param aParamId ID of the parameter to be varied to generate the scatter plot + @param aMean mean value for the distribution of the varied parameter + @param aVariance variance of the varied parameter. The variance may not be exactly zero. If so, it is set to 1e-30 + to avoid numerical instability. + @param normalisationDose prescribed dose of the current _dvh + @param numberOfPoints the size of the map, number of points to be calculated + @param aMinDose dose will be randomly selected from [aMinDose] (uniform distribution). + They will define the minvalue for x axis + @param aMaxDose dose will be randomly selected from [aMaxDose] (uniform distribution). + They will define the max value for x axis + @return Map of scattered values. If all parameters are valid, this map contains numberOfPoints valid scatter values. + If aMaxDose<=aMinDose, the scatter plot cannot be generated. The map will therefore be empty. + @todo This method is slow do not use with too many points. Because the scatter plot map must contain numberOfPoints + the scatter plot generation may run more often (producing invalid values). In tests the generation process runs on average + approximatly 20% more often. + @exception InvalidParameterException Thrown if aNormalisationDose<=0 or aMinDose<=aMaxiDose + */ + ScatterPlotType getScatterPlotVary1Parameter(BioModel& aModel, int aParamId, + BioModelParamType aMean, BioModelParamType aVariance, DoseTypeGy aNormalisationDose, int numberOfPoints=100, + DoseTypeGy aMinDose=0, DoseTypeGy aMaxDose=150); + + /*! @brief Get the points (TCP/NTCP Value, probability of the value) if >=1 parameter vary from a normal- + distribution with mean of parameter aParamIdVec.at(i)=aMeanVec.at(i), variance of parameter aParamIdVec.at(i)= + aVarianceVec.at(i). + @param aModel biological model for which the scatter plot will be generated + @param aParamIdVec a vector containing the IDs of the parameters to be varied to generate the scatter plot + @param aMeanVec a vector of mean values for the distribution of individually the varied parameters + @param aVarianceVec a vector of variance values of the individually varied parameter. The variance may not be exactly zero for + any parameter. If so, it is set to 1e-30 to avoid numerical instability. + @param normalisationDose prescribed dose of the current _dvh + @param numberOfPoints the size of the map, number of points to be calculated + @param aMinDose dose will be randomly selected from [aMinDose] (uniform distribution). + They will define the min value for x axis + @param aMaxDose dose will be randomly selected from [aMaxDose] (uniform distribution). + They will define the max value for x axis + @throw InvalidParameterException is thrown if the parameter vectors do not have the same size. + @return Map of scattered values. If all parameters are valid, this map contains numberOfPoints valid scatter values. + If aMaxDose<=aMinDose, the scatter plot cannot be generated. The map will therefore be empty. + @todo This method is very slow do not use with too many points.Because the scatter plot map must contain numberOfPoints + the scatter plot generation may run more often (producing invalid values). In tests the generation process runs on average + approximatly 20% more often. + @exception InvalidParameterException Thrown if aNormalisationDose<=0 or aMinDose<=aMaxiDose + */ + ScatterPlotType getScatterPlotVaryParameters(BioModel& aModel, std::vector aParamIdVec, + BioModel::ParamVectorType aMeanVec, BioModel::ParamVectorType aVarianceVec, + DoseTypeGy aNormalisationDose, int numberOfPoints=50, DoseTypeGy aMinDose=0, DoseTypeGy aMaxDose=150); + + /*! Compute normal probability density function for zero mean at aValue with aVariance. + */ + double normal_pdf(double aValue, double aVariance); + + } +} + +#endif \ No newline at end of file diff --git a/code/models/rttbDvhBasedModels.cpp b/code/models/rttbDvhBasedModels.cpp new file mode 100644 index 0000000..57cf5b5 --- /dev/null +++ b/code/models/rttbDvhBasedModels.cpp @@ -0,0 +1,153 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDvhBasedModels.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace models{ + DoseStatisticType getEUD(const DVHPointer dvh, const DoseCalcType aA) { + if(aA==0) + { + throw core::InvalidParameterException("Parameter invalid: aA should not be zero!"); + } + DataDifferentialType dataDifferential = dvh->getDataDifferential(); + if(dataDifferential.empty()) + { + throw core::InvalidParameterException("Parameter invalid: DVH data differential should not be empty!"); + } + + double eud=0; + + DoseTypeGy deltaD = dvh->getDeltaD(); + DVHVoxelNumber numberOfVoxels = dvh->getNumberOfVoxels(); + for(GridIndexType i=0; i calcBEDDVH(const DVHPointer dvh, const int numberOfFractions, const DoseCalcType alpha_beta, const bool relativeVolume){ + std::map dataBED; + std::map dataBEDRelative; + + DataDifferentialType dataDifferential = dvh->getDataDifferential(); + if(dataDifferential.empty()) + { + throw core::InvalidParameterException("Parameter invalid: DVH data differential should not be empty!"); + } + if(alpha_beta<=0) + { + throw core::InvalidParameterException("Parameter invalid: alpha_beta must be >0!"); + } + if(numberOfFractions<=0) + { + throw core::InvalidParameterException("Parameter invalid: numberOfFractions must be >0!"); + } + + DoseTypeGy deltaD = dvh->getDeltaD(); + DVHVoxelNumber numberOfVoxels = dvh->getNumberOfVoxels(); + + std::deque::iterator it; + int i=0; + + for(it=dataDifferential.begin();it!=dataDifferential.end();it++ ) + { + DoseTypeGy doseGyi=((i+0.5)*deltaD); + DoseTypeGy bedi=0; + bedi=(doseGyi*(1+doseGyi/(numberOfFractions*alpha_beta))); + if(!relativeVolume) + { + dataBED.insert(std::pair(bedi,(*it))); + } + else + { + dataBEDRelative.insert(std::pair(bedi,(*it)/numberOfVoxels)); + } + i++; + } + + if(!relativeVolume) + { + return dataBED; + } + else + { + return dataBEDRelative; + } + } + + std::map calcLQED2DVH(const DVHPointer dvh, const int numberOfFractions, const DoseCalcType alpha_beta, const bool relativeVolume) { + std::map dataLQED2; + std::map dataLQED2Relative; + + DataDifferentialType dataDifferential = dvh->getDataDifferential(); + if(dataDifferential.empty()) + { + throw core::InvalidParameterException("Parameter invalid: DVH data differential should not be empty!"); + } + if(alpha_beta<=0) + { + throw core::InvalidParameterException("Parameter invalid: alpha_beta must be >0!"); + } + if(numberOfFractions<=0) + { + throw core::InvalidParameterException("Parameter invalid: numberOfFractions must be >0!"); + } + + DoseTypeGy deltaD = dvh->getDeltaD(); + DVHVoxelNumber numberOfVoxels = dvh->getNumberOfVoxels(); + + std::deque::iterator it; + int i=0; + + for(it=dataDifferential.begin();it!=dataDifferential.end();it++ ) + { + DoseTypeGy doseGyi=((i+0.5)*deltaD); + DoseTypeGy lqed2i=0; + lqed2i=(doseGyi*((alpha_beta+doseGyi/numberOfFractions)/(alpha_beta+2))); + + if(!relativeVolume) + { + dataLQED2.insert(std::pair(lqed2i,*it)); + } + else + { + dataLQED2Relative.insert(std::pair(lqed2i,(*it)/numberOfVoxels)); + } + i++; + } + + if(!relativeVolume) + { + return dataLQED2; + } + else + { + return dataLQED2Relative; + } + } + } +} \ No newline at end of file diff --git a/code/models/rttbDvhBasedModels.h b/code/models/rttbDvhBasedModels.h new file mode 100644 index 0000000..856bb7d --- /dev/null +++ b/code/models/rttbDvhBasedModels.h @@ -0,0 +1,45 @@ +#include + +#include "rttbDVH.h" +#include "rttbBaseType.h" + +namespace rttb{ + namespace models{ + typedef core::DVH::DataDifferentialType DataDifferentialType; + typedef core::DVH::DVHPointer DVHPointer; + + /*! @brief Get Equivalent Uniform Dose (EUD) + @pre dvh data differential is not empty, + @pre aA is not zero, + @return Return calculated EUD value, + @exception InvalidParameterException Thrown if parameters were not set correctly. + */ + DoseStatisticType getEUD(const DVHPointer dvh, const DoseCalcType aA); + + /*! @brief Calculate Biological Effective/Equivalent Dose (BED) of dvh + @param relativeVolume default false-> the corresponding bed value is the voxel number of the dose bin; + if true-> the corresponding bed value is the relative volume % between 0 and 1, + (the voxel number of this dose bin)/(number of voxels) + @pre dvh data differential is not empty + @pre alpha_beta > 0 + @pre numberOfFractions > 0 + @return map of BED values + @exception InvalidParameterException Thrown if parameters were not set correctly. + */ + std::map calcBEDDVH(const DVHPointer dvh, const int numberOfFractions, + const DoseCalcType alpha_beta, const bool relativeVolume=false); + + /*! @brief Calculate Linear-quadratic equivalent dose for 2-Gy (LQED2) of dvh + @param relativeVolume default false-> the corresponding LQED2 value is the voxel number of the dose bin; + if true-> the corresponding LQED2 value is the relative volume % between 0 and 1, + (the voxel number of this dose bin)/(number of voxels) + @pre dvh data differential is not empty + @pre alpha_beta > 0 + @pre numberOfFractions > 0 + @return map of LQED2 values + @exception InvalidParameterException Thrown if parameters were not set correctly. + */ + std::map calcLQED2DVH(const DVHPointer dvh, const int numberOfFractions, + const DoseCalcType alpha_beta, const bool relativeVolume=false); + } + } \ No newline at end of file diff --git a/code/models/rttbIntegration.cpp b/code/models/rttbIntegration.cpp new file mode 100644 index 0000000..6cbb314 --- /dev/null +++ b/code/models/rttbIntegration.cpp @@ -0,0 +1,155 @@ +// ----------------------------------------------------------------------- +// 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 "rttbIntegration.h" +#include "rttbInvalidParameterException.h" + + +namespace rttb{ + namespace models{ + + double tcpModelFunctor::calculate(double x) const{ + if(x==0) + x=1e-30; + + return tcpFunction(a+(1-x)/x,params)/(x*x); + } + + double LkbModelFunctor::calculate(double x) const{ + if(x==0) + x=1e-30; + return lkbFunction(b-(1-x)/x)/(x*x); + } + + + double tcpFunction(double x, const TcpParams& tcp_params){ + double alphaVariance=tcp_params.alphaVariance; + if(alphaVariance==0) + alphaVariance=1e-30; + + double f=exp(-pow((x-tcp_params.alphaMean)/alphaVariance,2)/2); + double tmp,tmp1,tmp2,tmp3; + for(int i=0;i(BMFunction,aNew,bNew); + } + + double lkbFunction(double x){ + + double tmp = -pow(x,2)/2; + + double step = exp(tmp); + + return step; + } + + + double integrateLKB(double b){ + + double aNew=1e-30; + double bNew=1.0; + + LkbModelFunctor BMFunction; + BMFunction.b=b; + + return iterativeIntegration(BMFunction,aNew,bNew); + + } + + //returns the nth stage of refinement of the extended trapezoidal rule + template + integrationType trapzd(const FunctorType& BMfunction, integrationType a, integrationType b, int stepNum) + { + integrationType x, tnm, sum, del; + int it, j; + static integrationType result; + + if(stepNum==1) + result = 0.5*(b-a)*(BMfunction.calculate(a)+BMfunction.calculate(b)); + else{ + for(it=1,j=1;j + integrationType iterativeIntegration(const FunctorType& BMfunction, integrationType a, integrationType b){ + integrationType s = 0.0; + integrationType st = 0.0; + integrationType ost = 0.0; + integrationType os = 0.0; + int maxSteps = 50; + double eps = 1e-6; + + int i = 1; + for(; i <= maxSteps; ++i){ + + st = trapzd(BMfunction,a,b,i); + s = (4.0*st-ost)/3.0; + if (i > 5) + { + if(fabs(s-os) + 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/code/models/rttbNTCPLKBModel.cpp b/code/models/rttbNTCPLKBModel.cpp new file mode 100644 index 0000000..60d8dd8 --- /dev/null +++ b/code/models/rttbNTCPLKBModel.cpp @@ -0,0 +1,141 @@ +// ----------------------------------------------------------------------- +// 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 + +#define _USE_MATH_DEFINES +#include + +#include "rttbIntegration.h" +#include "rttbNTCPLKBModel.h" +#include "rttbDvhBasedModels.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + + +namespace rttb{ + + namespace models{ + NTCPLKBModel::NTCPLKBModel():NTCPModel(), _m(0), _a(0) {} + + NTCPLKBModel::NTCPLKBModel(DVHPointer aDvh, BioModelParamType aD50, BioModelParamType aM, BioModelParamType aA): + NTCPModel(aDvh, aD50), _m(aM), _a(aA){} + + void NTCPLKBModel::setA(const BioModelParamType aA){ + _a=aA; + } + + const BioModelParamType NTCPLKBModel::getA(){return _a;} + + void NTCPLKBModel::setM(const BioModelParamType aM){ + _m=aM; + } + + const BioModelParamType NTCPLKBModel::getM(){return _m;} + + void NTCPLKBModel::setParameterVector(const ParamVectorType aParameterVector){ + if(aParameterVector.size()!=3) + { + throw core::InvalidParameterException("Parameter invalid: aParameterVector.size must be 3! "); + } + else + { + _d50=aParameterVector.at(0); + _m=aParameterVector.at(1); + _a=aParameterVector.at(2); + } + } + + void NTCPLKBModel::setParameterByID(const int aParamId, const BioModelParamType aValue){ + if(aParamId==0) + { + _d50=aValue; + } + else if(aParamId==1) + { + _m=aValue; + } + else if(aParamId==2) + { + _a=aValue; + } + else + { + throw core::InvalidParameterException("Parameter invalid: aParamID must be 0(for d50) or 1(for m) or 2(for a)! "); + } + } + + const int NTCPLKBModel::getParameterID(const std::string aParamName) const{ + if(aParamName=="d50") + { + return 0; + } + else if(aParamName=="m") + { + return 1; + } + else if(aParamName=="a") + { + return 2; + } + else + { + rttbExceptionMacro(core::InvalidParameterException, << "Parameter name "<getDataDifferential(),(DoseTypeGy)(_dvh->getDeltaD()*doseFactor), + _dvh->getDeltaV(),"temporary","temporary"); + + boost::shared_ptr spDVH = boost::make_shared(variantDVH); + double eud=getEUD(spDVH,this->_a); + //_m must not be zero + double t=(eud-this->_d50)/(this->_m*this->_d50); + double value=1/pow(2*M_PI, 0.5); + + double result = integrateLKB(t); + + if (result != -100){ + value*=result; + + return value; + } + else{ + return false; + } + } + + }//end namespace models +}//end namespace rttb diff --git a/code/models/rttbNTCPLKBModel.h b/code/models/rttbNTCPLKBModel.h new file mode 100644 index 0000000..89530b7 --- /dev/null +++ b/code/models/rttbNTCPLKBModel.h @@ -0,0 +1,99 @@ +// ----------------------------------------------------------------------- +// 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 __NTCP_LKB_MODEL_H +#define __NTCP_LKB_MODEL_H + +#include +#include + +#include "rttbBaseType.h" +#include "rttbNTCPModel.h" +#include "rttbBaseTypeModels.h" + + + +namespace rttb{ + + namespace models{ + + /*! @class NTCPLKBModel + @brief This class represents a NTCP(Normal Tissue Complication Probability) LKB model + (Lyman 1985, Kutcher and Burman 1989) + @see NTCPModel + */ + class NTCPLKBModel: public NTCPModel + { + public: + typedef NTCPModel::ParamVectorType ParamVectorType; + typedef NTCPModel::DVHPointer DVHPointer; + + private: + /*! The steepness of the dose-response curve. Must not be zero on model evaluation. + */ + BioModelParamType _m; + + /*! Tumor or normal tissue-specific parameter that describes the dose–volume effect, + e.g. -10 for prostate (Wu 2002). Must not be zero on model evaluation, because EUD calculation will fail. + */ + BioModelParamType _a; + + protected: + /*! @brief Calculate the model value + * @param doseFactor: scaling factor for the dose. The model calculation will use the dvh with each di=old di*doseFactor. + * @throw if either _a or _m is zero for the model calculation + */ + BioModelValueType calcModel(const double doseFactor=1); + + public: + NTCPLKBModel(); + + NTCPLKBModel(DVHPointer aDvh, BioModelParamType aD50, BioModelParamType aM, BioModelParamType aA); + + void setM(const BioModelParamType aM); + + const BioModelParamType getM(); + + void setA(const BioModelParamType aA); + + const BioModelParamType getA(); + + /*! @brief Set parameter with ID. "d50":0,"m":1,"a":2 + @exception InvalidParameterException Thrown if aParamId is not 0 or 1 or 2. + */ + virtual void setParameterByID(const int aParamId, const BioModelParamType aValue); + + /*! @brief Set parameter vector, where index of vector is the parameter ID. "d50":0,"m":1,"a":2 + @exception InvalidParameterException Thrown if aParamterVector.size()!=3. + */ + virtual void setParameterVector(const ParamVectorType aParameterVector); + + /*! @brief Get parameter ID. "d50":0,"m":1,"a":2 + @return 0 for "d50", 1 for "m", 2 for "a" + @exception InvalidParameterException Thrown if aParamName is not d50 or m or a. + */ + virtual const int getParameterID(const std::string aParamName) const; + }; + + } + } + + +#endif diff --git a/code/models/rttbNTCPModel.h b/code/models/rttbNTCPModel.h new file mode 100644 index 0000000..559a39e --- /dev/null +++ b/code/models/rttbNTCPModel.h @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------- +// 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 __NTCP_MODEL_H +#define __NTCP_MODEL_H + + +#include "rttbBaseType.h" + +#include "rttbBioModel.h" +#include "rttbBaseTypeModels.h" + + + +namespace rttb{ + namespace models{ + + /*! @class NTCPModel + @brief This is the interface class for NTCP(Normal Tissue Complication Probability) models + */ + class NTCPModel: public BioModel + { + public: + typedef BioModel::ParamVectorType ParamVectorType; + typedef BioModel::DVHPointer DVHPointer; + + protected: + BioModelParamType _d50; + + public: + NTCPModel(): BioModel(), _d50(0){} + NTCPModel(const BioModelParamType aD50): BioModel(), _d50(aD50){} + NTCPModel(DVHPointer aDvh, const BioModelParamType aD50): BioModel(aDvh), _d50(aD50){} + + const BioModelParamType getD50(){return _d50;} + + void setD50(const BioModelParamType aD50){_d50=aD50;} + }; + + }//end namespace models + }//end namespace rttb + +#endif diff --git a/code/models/rttbNTCPRSModel.cpp b/code/models/rttbNTCPRSModel.cpp new file mode 100644 index 0000000..911e8ec --- /dev/null +++ b/code/models/rttbNTCPRSModel.cpp @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// 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 + +#define _USE_MATH_DEFINES +#include + +#include "rttbNTCPRSModel.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + +namespace rttb{ + + namespace models{ + NTCPRSModel::NTCPRSModel():NTCPModel(), _gamma(0), _s(0){} + + NTCPRSModel::NTCPRSModel(DVHPointer aDvh, BioModelParamType aD50, BioModelParamType aGamma, BioModelParamType aS): + NTCPModel(aDvh, aD50), _gamma(aGamma), _s(aS){} + + void NTCPRSModel::setGamma(const BioModelParamType aGamma){_gamma=aGamma;} + + const BioModelParamType NTCPRSModel::getGamma(){return _gamma;} + + void NTCPRSModel::setS(const BioModelParamType aS){ + _s=aS; + } + + const BioModelParamType NTCPRSModel::getS(){return _s;} + + void NTCPRSModel::setParameterVector(const ParamVectorType aParameterVector){ + if(aParameterVector.size()!=3) + { + throw core::InvalidParameterException("Parameter invalid: aParameterVector.size must be 3! "); + } + else + { + _d50=aParameterVector.at(0); + _gamma=aParameterVector.at(1); + _s=aParameterVector.at(2); + } + } + + void NTCPRSModel::setParameterByID(const int aParamId, const BioModelParamType aValue){ + if(aParamId==0) + { + _d50=aValue; + } + else if(aParamId==1) + { + _gamma=aValue; + } + else if(aParamId==2) + { + _s=aValue; + } + else + { + throw core::InvalidParameterException("Parameter invalid: aParamID must be 0(for d50) or 1(for gamma) or 2(for s)! "); + } + + } + + const int NTCPRSModel::getParameterID(const std::string aParamName) const{ + if(aParamName=="d50") + { + return 0; + } + else if(aParamName=="gamma") + { + return 1; + } + else if(aParamName=="s") + { + return 2; + } + else + { + rttbExceptionMacro(core::InvalidParameterException, << "Parameter name "<_gamma*(1-dose/this->_d50))); + } + + BioModelValueType NTCPRSModel::calcModel(double doseFactor){ + if (_d50==0){ + throw core::InvalidParameterException("d50 must not be zero"); + } + if (_s==0){ + throw core::InvalidParameterException("s must not be zero"); + } + + std::deque dataDifferential=this->_dvh->getDataDifferential(); + double ntcp=1; + for(GridIndexType i=0; ipoissonModel(i*this->_dvh->getDeltaD()*doseFactor),this->_s); + + double vi=dataDifferential[i]/this->_dvh->getNumberOfVoxels(); + ntcp*=pow((1-pd),vi); + } + //_s must not be zero + return (BioModelValueType)(pow((1-ntcp),1/this->_s)); + } + + }//end namespace models + }//end namespace rttb + diff --git a/code/models/rttbNTCPRSModel.h b/code/models/rttbNTCPRSModel.h new file mode 100644 index 0000000..05e12d8 --- /dev/null +++ b/code/models/rttbNTCPRSModel.h @@ -0,0 +1,104 @@ +// ----------------------------------------------------------------------- +// 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 __NTCP_RS_MODEL_H +#define __NTCP_RS_MODEL_H + +#include +#include + +#include "rttbBaseType.h" +#include "rttbNTCPModel.h" +#include "rttbBaseTypeModels.h" + + +namespace rttb{ + + namespace models{ + + /*! @class NTCPRSModel + @brief This class represents a NTCP(Normal Tissue Complication Probability) relative seriality model (Källman 1992) + @see NTCPModel + */ + class NTCPRSModel: public NTCPModel + { + public: + typedef NTCPModel::ParamVectorType ParamVectorType; + typedef NTCPModel::DVHPointer DVHPointer; + + private: + /*! _gamma The normalised dose-response gradient, values between 1.7 and 2.0 are typical for human tumours. + (Källman 1992) + */ + BioModelParamType _gamma; + + /*! _s The relative seriality factor, e.g. s=3.4 for the esophagus (highly serial structure) and s=0.0061 + for the lung(highly parallel structure). Must not be zero on model evaluation. + */ + BioModelParamType _s; + + const double poissonModel(const double dose); + + protected: + /*! @brief Calculate the model value + @param doseFactor scaling factor for the dose. The model calculation will use the dvh with each di=old di*doseFactor. + @throw if either _s or _d50 is zero for the model calculation. + */ + BioModelValueType calcModel(const double doseFactor=1); + + public: + NTCPRSModel(); + + /*!@brief Constructor initializing all member variables with given parameters. + */ + NTCPRSModel(DVHPointer aDvh, BioModelParamType aD50, BioModelParamType aGamma, BioModelParamType aS); + + void setGamma(const BioModelParamType aGamma); + + const BioModelParamType getGamma(); + + void setS(const BioModelParamType aS); + + const BioModelParamType getS(); + + /*! @brief Set parameter with ID. "d50":0,"gamma":1,"s":2 + @exception InvalidParameterException Thrown if aParamId is not 0 or 1 or 2. + */ + virtual void setParameterByID(const int aParamId, const BioModelParamType aValue); + + /*! @brief Set parameter vector, where index of vector is the parameter Id. + "d50":0,"gamma":1,"s":2 + @exception InvalidParameterException Thrown if aParamterVector.size()!=3. + */ + virtual void setParameterVector(const ParamVectorType aParameterVector); + + /*! @brief Get parameter ID. "d50":0,"gamma":1,"s":2 + @return 0 for "d50", 1 for "gamma", 2 for "s" + @exception InvalidParameterException Thrown if aParamName is not d50 or gamma or s. + */ + virtual const int getParameterID(const std::string aParamName) const; + }; + + } + } + + +#endif diff --git a/code/models/rttbTCPLQModel.cpp b/code/models/rttbTCPLQModel.cpp new file mode 100644 index 0000000..50f99da --- /dev/null +++ b/code/models/rttbTCPLQModel.cpp @@ -0,0 +1,229 @@ +// ----------------------------------------------------------------------- +// 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 +#define _USE_MATH_DEFINES +#include +#include + +#include +#include + +#include "rttbTCPLQModel.h" +#include "rttbDvhBasedModels.h" +#include "rttbBaseTypeModels.h" +#include "rttbIntegration.h" +#include "rttbInvalidParameterException.h" +#include "rttbExceptionMacros.h" + + +namespace rttb{ + + namespace models{ + + TCPLQModel::TCPLQModel(): TCPModel(),_alphaMean(0), _alphaVariance(0), _alpha_beta(0), _rho(0){} + + TCPLQModel::TCPLQModel(DVHPointer aDVH, BioModelParamType aAlphaMean, BioModelParamType aBeta, BioModelParamType aRho, + int aNumberOfFractions): TCPModel(aDVH,aNumberOfFractions),_alphaMean(aAlphaMean), _alphaVariance(0), + _alpha_beta(aAlphaMean/aBeta), _rho(aRho){} + + + TCPLQModel::TCPLQModel(DVHPointer aDVH, BioModelParamType aRho, int aNumberOfFractions, BioModelParamType aAlpha_Beta, + BioModelParamType aAlphaMean, BioModelParamType aAlphaVariance): TCPModel(aDVH,aNumberOfFractions),_alphaMean(aAlphaMean), + _alphaVariance(aAlphaVariance), _alpha_beta(aAlpha_Beta), _rho(aRho){} + + void TCPLQModel::setParameters(const BioModelParamType aAlphaMean, const BioModelParamType aAlpha_Beta, + const BioModelParamType aRho, const BioModelParamType aAlphaVariance){ + _alphaMean=aAlphaMean; + _alphaVariance=aAlphaVariance; + _alpha_beta=aAlpha_Beta; + _rho=aRho; + + //reset _value, because parameters have changed. + _value=0; + } + + void TCPLQModel::setAlpha(const BioModelParamType aAlphaMean, const BioModelParamType aAlphaVariance){ + _alphaVariance=aAlphaVariance; + _alphaMean=aAlphaMean; + } + + void TCPLQModel::setAlphaBeta(const BioModelParamType aAlpha_Beta){ + _alpha_beta=aAlpha_Beta; + } + + void TCPLQModel::setRho(const BioModelParamType aRho){_rho=aRho;} + + const BioModelParamType TCPLQModel::getAlpahBeta(){return _alpha_beta;} + + const BioModelParamType TCPLQModel::getAlphaMean(){return _alphaMean;} + + const BioModelParamType TCPLQModel::getAlphaVariance(){return _alphaVariance;} + + const BioModelParamType TCPLQModel::getRho(){return _rho;} + + long double TCPLQModel::calcTCPi(BioModelParamType aRho, BioModelParamType aAlphaMean, double vj, double bedj){ + return exp(-aRho*vj*exp(-aAlphaMean*bedj)); + } + + long double TCPLQModel::calcTCP(std::map aBEDDVH, BioModelParamType aRho, + BioModelParamType aAlphaMean, double aDeltaV){ + std::map::iterator it; + long double tcp=1; + for(it=aBEDDVH.begin(); it!=aBEDDVH.end();it++) + { + long double tcpi=this->calcTCPi(aRho,aAlphaMean,(*it).second*aDeltaV,(*it).first); + tcp=tcp*tcpi; + } + return tcp; + } + + long double TCPLQModel::calcTCPAlphaNormalDistribution(std::map aBEDDVH, + BioModelParamType aRho, BioModelParamType aAlphaMean, + BioModelParamType aAlphaVariance, double aDeltaV){ + + std::map::iterator it; + int bedSize=aBEDDVH.size(); + std::vector volumeV2; + std::vector bedV2; + int i=0; + for(it=aBEDDVH.begin();it!=aBEDDVH.end();it++) + { + volumeV2.push_back((*it).second*aDeltaV); + bedV2.push_back((*it).first); + i++; + } + + struct TcpParams params={aAlphaMean,aAlphaVariance,aRho,volumeV2,bedV2}; + + double result = integrateTCP(0, params); + + if(result == -100) + { + std::cerr << "Integration error!\n"; + return -1; + } + else{ + long double tcp=1/(pow(2*M_PI, 0.5)*_alphaVariance)*result; + + return tcp; + } + } + + BioModelValueType TCPLQModel::calcModel(const double doseFactor){ + core::DVH variantDVH=core::DVH(_dvh->getDataDifferential(),(DoseTypeGy)(_dvh->getDeltaD()*doseFactor), + _dvh->getDeltaV(),"temporary","temporary"); + boost::shared_ptr spDVH = boost::make_shared(variantDVH); + + BioModelValueType value=0; + if(_alphaVariance==0){ + if(_alphaMean<=0 && _alpha_beta<=0 && _rho<=0 && _numberOfFractions<=0) + { + throw core::InvalidParameterException("Parameter invalid: alpha, alpha/beta, rho and number of fractions must >0!"); + } + + long double tcp=1; + std::map dataBED=calcBEDDVH(spDVH,_numberOfFractions, _alpha_beta); + + value=(BioModelValueType)this->calcTCP(dataBED,_rho,_alphaMean,variantDVH.getDeltaV()); + return value; + } + + //if alpha normal distribution + else { + if(this->_alpha_beta<=0 && this->_alphaMean<=0 && this->_alphaVariance<0 && _rho<=0 && _numberOfFractions<=0) + { + throw core::InvalidParameterException("Parameter invalid: alpha/beta, alphaMean, rho and number of fractions must >0!"); + } + + std::map dataBED=calcBEDDVH(spDVH,_numberOfFractions, _alpha_beta); + value=(BioModelValueType)(this->calcTCPAlphaNormalDistribution(dataBED,_rho,_alphaMean,_alphaVariance, + variantDVH.getDeltaV())); + return value; + } + } + + void TCPLQModel::setParameterVector(const ParamVectorType aParameterVector){ + if(aParameterVector.size()!=4) + { + throw core::InvalidParameterException("Parameter invalid: aParameterVector.size must be 4! "); + } + else + { + _alphaMean=aParameterVector.at(0); + _alphaVariance=aParameterVector.at(1); + _alpha_beta=aParameterVector.at(2); + _rho=aParameterVector.at(3); + } + } + + void TCPLQModel::setParameterByID(const int aParamId, const BioModelParamType aValue){ + if(aParamId==0) + { + _alphaMean=aValue; + } + else if(aParamId==1) + { + _alphaVariance=aValue; + } + else if(aParamId==2) + { + _alpha_beta=aValue; + } + else if(aParamId==3) + { + _rho=aValue; + } + else + { + throw core::InvalidParameterException("Parameter invalid: aParamID must be 0(alphaMean) or 1(alphaVariance) or 2(alpha_beta) or 3(rho)! "); + } + + } + + const int TCPLQModel::getParameterID(const std::string aParamName) const{ + if(aParamName=="alphaMean") + { + return 0; + } + else if(aParamName=="alphaVariance") + { + return 1; + } + else if(aParamName=="alpha_beta") + { + return 2; + } + else if(aParamName=="rho") + { + return 3; + } + else + { + rttbExceptionMacro(core::InvalidParameterException, << "Parameter name "< +#include +#include + +#include "rttbBaseType.h" +#include "rttbTCPModel.h" +#include "rttbBaseTypeModels.h" + + +namespace rttb{ + + namespace models{ + + /*! @class TCPLQModel + @brief This class represents a TCP(Tumor Control Probability) LQ model (Nahum and Sanchez-Nieto 2001, + Hall and Giaccia 2006) + @see TCPModel + */ + + class TCPLQModel: public TCPModel + { + public: + typedef TCPModel::ParamVectorType ParamVectorType; + typedef TCPModel::DVHPointer DVHPointer; + + private: + /*! @brief Calculate intermediate tcp using alpha constant. This is a helper function for calcTCP() + @see calcTCP + */ + long double calcTCPi(BioModelParamType aRho, BioModelParamType aAlphaMean, double vj, double bedj); + + /*! @brief Calculate tcp using alpha constant. + */ + long double calcTCP(std::map aBEDDVH, BioModelParamType aRho, + BioModelParamType aAlphaMean, double aDeltaV); + + /*! @brief Calculate tcp using a normal distribution for alpha. + */ + long double calcTCPAlphaNormalDistribution(std::map aBEDDVH, + BioModelParamType aRho, BioModelParamType aAlphaMean, + BioModelParamType aAlphaVariance, double aDeltaV); + + protected: + BioModelParamType _alphaMean; + + BioModelParamType _alphaVariance; + + BioModelParamType _alpha_beta; + + /*! Roh is the initial clonogenic cell density + */ + BioModelParamType _rho; + + + /*! @brief Calculate the model value + @param doseFactor scaling factor for prescribed dose. The model calculation will use the dvh + with each di=old di*doseFactor. + @pre _alphaMean >0 + @pre _alphaVariance >= 0 + @pre _alpha_beta > 0 + @pre _rho > 0 + @exception InvalidParameterException Thrown if parameters were not set correctly. + */ + BioModelValueType calcModel(const double doseFactor=1); + + public: + TCPLQModel(); + + /*! @brief Constructor initializes member variables with given parameters. + */ + TCPLQModel(DVHPointer aDVH, BioModelParamType aAlphaMean, BioModelParamType aBeta, BioModelParamType aRho, + int aNumberOfFractions); + + /*! @brief Constructor for alpha distribution initializes member variables with given parameters. + */ + TCPLQModel(DVHPointer aDVH, BioModelParamType aRho, int aNumberOfFractions, BioModelParamType aAlpha_Beta, + BioModelParamType aAlphaMean, BioModelParamType aAlphaVariance); + + const BioModelParamType getRho(); + + void setRho(const BioModelParamType aRho); + + const BioModelParamType getAlphaMean(); + + const BioModelParamType getAlphaVariance(); + + /*! @brief The distribution of the parameter alpha, which is characteristic for a population of cells, + is described by the its mean and variance. If alpha is constant the variance is 0. + @param aAlphaVariance The variance of alpha can be given, the default value is 0 resulting in constant + alpha. + */ + void setAlpha(const BioModelParamType aAlphaMean, const BioModelParamType aAlphaVariance=0); + + const BioModelParamType getAlpahBeta(); + + void setAlphaBeta(const BioModelParamType aAlpha_Beta); + + /*! @brief Set parameters for the TCP model. _value will be reset to 0. + @param aAlpha_Beta alpha/beta constant . + @param aAlphaMean mean of alpha distribution. + @param aAlphaVariance variance of alpha distribution. + */ + void setParameters(const BioModelParamType aAlphaMean, const BioModelParamType aAlpha_Beta, + const BioModelParamType aRho, const BioModelParamType aAlphaVariance=0); + + /*! @brief Set parameter with ID. "alphaMean":0,"alphaVariance":1,"alpha_beta":2, "rho":3 + @exception InvalidParameterException Thrown if aParamId is not 0 or 1 or 2 or 3. + */ + virtual void setParameterByID(const int aParamId, const BioModelParamType aValue); + + /*! @brief Set parameter vector, where index of vector is the parameter id. + "alphaMean":0,"alphaVariance":1,"alpha_beta":2, "rho":3 + @exception InvalidParameterException Thrown if aParamterVector.size()!=4. + */ + virtual void setParameterVector(const ParamVectorType aParameterVector); + + /*! @brief Get parameter id. "alphaMean":0,"alphaVariance":1,"alpha_beta":2, "rho":3 + @return 0 for "alphaMean", 1 for "alphaVariance", 2 for "alpha_beta", 3 for "rho" + @exception InvalidParameterException Thrown if aParamName is not alphaMean or alphaVariance or alpha_beta or rho. + */ + virtual const int getParameterID(const std::string aParamName) const; + }; + + }//end algorithms + }//end rttb + + +#endif diff --git a/code/models/rttbTCPModel.cpp b/code/models/rttbTCPModel.cpp new file mode 100644 index 0000000..fb3c157 --- /dev/null +++ b/code/models/rttbTCPModel.cpp @@ -0,0 +1,38 @@ +// ----------------------------------------------------------------------- +// 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 "rttbTCPModel.h" + +namespace rttb{ + + namespace models{ + + void TCPModel::setNumberOfFractions(const int aNumberOfFractions){ + _numberOfFractions=aNumberOfFractions; + } + + + const int TCPModel::getNumberOfFractions(){ + return _numberOfFractions; + } + + } + } diff --git a/code/models/rttbTCPModel.h b/code/models/rttbTCPModel.h new file mode 100644 index 0000000..2cafa04 --- /dev/null +++ b/code/models/rttbTCPModel.h @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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 __TCP_MODEL_H +#define __TCP_MODEL_H + + +#include "rttbBaseType.h" +#include "rttbBioModel.h" +#include "rttbBaseTypeModels.h" + +namespace rttb{ + + namespace models{ + + + /*! @class TCPModel + @brief This is the interface class for TCP(Tumor Control Probability) models + */ + class TCPModel: public BioModel + { + public: + typedef BioModel::ParamVectorType ParamVectorType; + typedef BioModel::DVHPointer DVHPointer; + + protected: + int _numberOfFractions; + + public: + TCPModel(): BioModel(), _numberOfFractions(0){}; + TCPModel(int aNum): BioModel(), _numberOfFractions(aNum){}; + TCPModel(DVHPointer aDvh, int aNum): BioModel(aDvh), _numberOfFractions(aNum){}; + + void setNumberOfFractions(const int aNumberOfFractions); + + const int getNumberOfFractions(); + + }; + + }//end namespace models + }//end namespace rttb + +#endif diff --git a/demoapps/CMakeLists.txt b/demoapps/CMakeLists.txt new file mode 100644 index 0000000..22ca34d --- /dev/null +++ b/demoapps/CMakeLists.txt @@ -0,0 +1,6 @@ +MESSAGE(STATUS "processing RTToolbox demo apps") + +OPTION(BUILD_DemoApp_DoseAcc "Determine if the demo application DoseAcc will be generated." OFF) +IF(BUILD_DemoApp_DoseAcc) +ADD_SUBDIRECTORY(DoseAcc) +ENDIF(BUILD_DemoApp_DoseAcc) diff --git a/demoapps/DoseAcc/CMakeLists.txt b/demoapps/DoseAcc/CMakeLists.txt new file mode 100644 index 0000000..8eda38d --- /dev/null +++ b/demoapps/DoseAcc/CMakeLists.txt @@ -0,0 +1,3 @@ +MESSAGE (STATUS "generating demo app: DoseAcc - simple dose accumulation tool example") + +RTTB_CREATE_APPLICATION(DoseAcc DEPENDS RTTBCore RTTBAlgorithms RTTBInterpolation RTTBMatchPointBinding RTTBITKIO RTTBVirtuosIO PACKAGE_DEPENDS MatchPoint ITK) \ No newline at end of file diff --git a/demoapps/DoseAcc/DoseAcc.cpp b/demoapps/DoseAcc/DoseAcc.cpp new file mode 100644 index 0000000..27e4a82 --- /dev/null +++ b/demoapps/DoseAcc/DoseAcc.cpp @@ -0,0 +1,192 @@ +// ----------------------------------------------------------------------- +// 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 "DoseAccApplicationData.h" +#include "DoseAccHelper.h" + +rttb::apps::doseAcc::ApplicationData appData; + +int main(int argc, char** argv) +{ + int result = 0; + + std::cout << "DoseAcc - RTTB demo app for simple dose accumulation." << std::endl; + + switch (rttb::apps::doseAcc::ParseArgumentsForAppData(argc, argv, appData)) + { + case 1: + { + //showed version or help info. Done. + return 1; + } + + case 2: + { + std::cerr << "Missing Parameters. Use one of the following flags for more information:" << + std::endl; + std::cerr << "-? or --help" << std::endl; + return 2; + } + + case 3: + { + //wrong option usage. + return 3; + } + } + + if (appData._fileCount < 3) + { + std::cerr << "Missing Parameters. Use one of the following flags for more information:" << + std::endl; + std::cerr << "-? or --help" << std::endl; + return 1; + } + + std::cout << std::endl << "*******************************************" << std::endl; + std::cout << "Dose 1 file: " << appData._dose1FileName << std::endl; + if (!(appData._dose1VirtuosPlanFileName.empty())) + { + std::cout << " as virtuos. Plan: " << appData._dose1VirtuosPlanFileName << std::endl; + } + std::cout << "Dose 2 file: " << appData._dose2FileName << std::endl; + if (!(appData._dose2VirtuosPlanFileName.empty())) + { + std::cout << " as virtuos. Plan: " << appData._dose2VirtuosPlanFileName << std::endl; + } + std::cout << "Dose output file: " << appData._outputFileName << std::endl; + + if (!(appData._regFileName.empty())) + { + std::cout << "Registration file: " << appData._regFileName << std::endl; + } + std::cout << "Dose 1 weight: " << appData._weightDose1 << std::endl; + std::cout << "Dose 2 weight: " << appData._weightDose2 << std::endl; + + try + { + if (appData._dose1VirtuosPlanFileName.empty()) + { + appData._Dose1 = rttb::apps::doseAcc::loadDose(appData._dose1FileName); + } + else + { + appData._Dose1 = rttb::apps::doseAcc::loadVirtuosDose(appData._dose1FileName, appData._dose1VirtuosPlanFileName); + } + } + catch (::itk::ExceptionObject& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e << std::endl; + return 4; + } + catch (std::exception& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e.what() << std::endl; + return 4; + } + catch (...) + { + std::cerr << "Error!!! unknown error while reading input image." << std::endl; + return 4; + } + + try + { + if (appData._dose2VirtuosPlanFileName.empty()) + { + appData._Dose2 = rttb::apps::doseAcc::loadDose(appData._dose2FileName); + } + else + { + appData._Dose2 = rttb::apps::doseAcc::loadVirtuosDose(appData._dose2FileName, appData._dose2VirtuosPlanFileName); + } + } + catch (::itk::ExceptionObject& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e << std::endl; + return 4; + } + catch (std::exception& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e.what() << std::endl; + return 4; + } + catch (...) + { + std::cerr << "Error!!! unknown error while reading input image." << std::endl; + return 4; + } + + if (!(appData._regFileName.empty())) + { + try + { + appData._spReg = rttb::apps::doseAcc::loadRegistration(appData._regFileName); + } + catch (::itk::ExceptionObject& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e << std::endl; + return 5; + } + catch (std::exception& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e.what() << std::endl; + return 5; + } + catch (...) + { + std::cerr << "Error!!! unknown error while reading registration file." << std::endl; + return 5; + } + } + + try + { + rttb::apps::doseAcc::processData(appData); + } + catch (::itk::ExceptionObject& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e << std::endl; + return 9; + } + catch (std::exception& e) + { + std::cerr << "Error!!!" << std::endl; + std::cerr << e.what() << std::endl; + return 9; + } + catch (...) + { + std::cerr << "Error!!! unknown error while mapping and writing image." << std::endl; + return 9; + } + + std::cout << std::endl; + + return result; +} diff --git a/demoapps/DoseAcc/DoseAccApplicationData.cpp b/demoapps/DoseAcc/DoseAccApplicationData.cpp new file mode 100644 index 0000000..22995e8 --- /dev/null +++ b/demoapps/DoseAcc/DoseAccApplicationData.cpp @@ -0,0 +1,178 @@ +// ----------------------------------------------------------------------- +// 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 "DoseAccApplicationData.h" + +#include "itksys/SystemTools.hxx" +#include "itksys/CommandLineArguments.hxx" + +#include "RTToolboxConfigure.h" + +namespace rttb +{ + namespace apps + { + namespace doseAcc + { + + int unknown_argument(const char* argument, void* call_data) + { + std::cout << "Got unknown argument: \"" << argument << "\"" << std::endl; + return 0; + } + + ApplicationData:: + ApplicationData() + { + this->Reset(); + } + + void + ApplicationData:: + Reset() + { + _dose1FileName = ""; + _dose2FileName = ""; + _outputFileName = ""; + _regFileName = ""; + _dose1VirtuosPlanFileName = ""; + _dose2VirtuosPlanFileName = ""; + + + _showVersion = false; + _showHelp = false; + + _weightDose1 = 1.0; + _weightDose2 = 1.0; + + _fileCount = 0; + } + + unsigned int + ParseArgumentsForAppData(int argc, char** argv, ApplicationData& appData) + { + itksys::CommandLineArguments cmdParser; + + appData.Reset(); + + if (argc > 3) + { + appData._dose1FileName = argv[1]; + ++appData._fileCount; + --argc; + ++argv; + + appData._dose2FileName = argv[1]; + ++appData._fileCount; + --argc; + ++argv; + + appData._outputFileName = argv[1]; + ++appData._fileCount; + --argc; + ++argv; + } + + cmdParser.Initialize(argc, argv); + + cmdParser.SetUnknownArgumentCallback(unknown_argument); + + cmdParser.AddArgument("--weight1", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._weightDose1), + "Specifies the weight for dose 1. If not specified the weight is 1.0."); + cmdParser.AddArgument("-w1", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._weightDose1), + "Specifies the weight for dose 1. If not specified the weight is 1.0."); + cmdParser.AddArgument("--weight2", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._weightDose2), + "Specifies the weight for dose 2. If not specified the weight is 1.0."); + cmdParser.AddArgument("-w2", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._weightDose2), + "Specifies the weight for dose 2. If not specified the weight is 1.0."); + + cmdParser.AddArgument("--registration", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._regFileName), + "Specifies name and location of the registration file that should be used to map dose 2 befor accumulating it with dose 1. Default is no mapping, thus direct accumulation. The registration should be stored as MatchPoint registration."); + cmdParser.AddArgument("-r", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._regFileName), + "Specifies name and location of the registration file that should be used to map dose 2 befor accumulating it with dose 1. Default is no mapping, thus direct accumulation. The registration should be stored as MatchPoint registration."); + + cmdParser.AddArgument("--virtuosPlan1", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._dose1VirtuosPlanFileName), + "Indicates that the dose file of dose 1 should be handled as Virtuos dose and specifies the plan file that should be used to load the dose."); + cmdParser.AddArgument("-v1", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._dose1VirtuosPlanFileName), + "Indicates that the dose file of dose 1 should be handled as Virtuos dose and specifies the plan file that should be used to load the dose."); + + cmdParser.AddArgument("--virtuosPlan2", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._dose2VirtuosPlanFileName), + "Indicates that the dose file of dose 2 should be handled as Virtuos dose and specifies the plan file that should be used to load the dose."); + cmdParser.AddArgument("-v2", itksys::CommandLineArguments::SPACE_ARGUMENT, &(appData._dose2VirtuosPlanFileName), + "Indicates that the dose file of dose 2 should be handled as Virtuos dose and specifies the plan file that should be used to load the dose."); + + cmdParser.AddArgument("-v", itksys::CommandLineArguments::NO_ARGUMENT, &(appData._showVersion), + "Shows the version of the program."); + + cmdParser.AddArgument("-h", itksys::CommandLineArguments::NO_ARGUMENT, &(appData._showHelp), + "Shows this help information for the program."); + cmdParser.AddArgument("--help", itksys::CommandLineArguments::NO_ARGUMENT, &(appData._showHelp), + "Shows this help information for the program."); + cmdParser.AddArgument("-?", itksys::CommandLineArguments::NO_ARGUMENT, &(appData._showHelp), + "Shows this help information for the program."); + cmdParser.AddArgument("/h", itksys::CommandLineArguments::NO_ARGUMENT, &(appData._showHelp), + "Shows this help information for the program."); + cmdParser.AddArgument("/?", itksys::CommandLineArguments::NO_ARGUMENT, &(appData._showHelp), + "Shows this help information for the program."); + + if (!cmdParser.Parse()) + { + std::cerr << "Wrong command line option or insufficient number of arguments." << std::endl; + std::cerr << "The last correct argument was: " << argv[cmdParser.GetLastArgument()] << std::endl << + "Use one of the following flags for more information:" << std::endl; + std::cerr << "-? or --help" << std::endl; + return 3; + }; + + if (appData._showHelp) + { + std::cout << std::endl << "Usage: " << std::endl << std::endl; + std::cout << " DoseAcc [options]" << std::endl << std::endl; + std::cout << " Dose1: File path to the 1st dose." << std::endl; + std::cout << " Dose2: File path to the 2nd dose." << std::endl; + std::cout << " DoseOutput: File path where the output should be stored." << std::endl << std::endl; + std::cout << "Command-Line Options:" << std::endl << std::endl; + std::cout << cmdParser.GetHelp() << std::endl << std::endl; + std::cout << " Example:" << std::endl << std::endl; + std::cout << " DoseCalc dose1.mhd dose2.mhd result.mhd -w1 2 -r reg.mapr" << std::endl << std::endl; + std::cout << + " This will accumulate \"dose1.mhd\" and \"dose2.mhd\" by using \"reg.mapr\" to map dose 2. For the accumlation dose 1 will be multiplied by 2. The resulting dose will be stored in \"result.mhd\"." + << std::endl; + return 1; + } + + if (appData._showVersion) + { + std::cout << std::endl << "Version: " << RTT_FULL_VERSION_STRING; + return 1; + } + + if (appData._fileCount < 3) + { + return 2; + } + + return 0; + }; + + } + } +} diff --git a/demoapps/DoseAcc/DoseAccApplicationData.h b/demoapps/DoseAcc/DoseAccApplicationData.h new file mode 100644 index 0000000..06171b2 --- /dev/null +++ b/demoapps/DoseAcc/DoseAccApplicationData.h @@ -0,0 +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) +*/ + + +#ifndef __DOSE_ACCAPPLICATION_DATA_H +#define __DOSE_ACCAPPLICATION_DATA_H + +#include "mapRegistration.h" + +#include "rttbBaseType.h" +#include "rttbDoseAccessorInterface.h" + +namespace rttb +{ + namespace apps + { + namespace doseAcc + { + + class ApplicationData + { + public: + typedef map::core::Registration<3, 3> RegistrationType; + + /** Loaded Dose.*/ + core::DoseAccessorInterface::DoseAccessorPointer _Dose1; + std::string _dose1FileName; + std::string _dose1VirtuosPlanFileName; + core::DoseAccessorInterface::DoseAccessorPointer _Dose2; + std::string _dose2FileName; + std::string _dose2VirtuosPlanFileName; + RegistrationType::Pointer _spReg; + std::string _regFileName; + + std::string _outputFileName; + + double _weightDose1; + double _weightDose2; + + bool _showVersion; + bool _showHelp; + + int _fileCount; + + void Reset(); + + ApplicationData(); + }; + + /** Parse the application argument passed when starting the application. + * If no error or special request occurred the return is 0. Otherwise the return values + * have the following meaning: \n + * 0: Normal parsing.\n + * 1: showed help or version (flag was set).\n + * 2: not enough required input files.\n + * 3: Parsing error.\n + * @param argc Number of parameter arguments + * @param argv Pointer to the passed arguments + * @return Result code of the parsing (see above).**/ + unsigned int ParseArgumentsForAppData(int argc, char** argv, ApplicationData& appData); + + } + } +} +#endif diff --git a/demoapps/DoseAcc/DoseAccHelper.cpp b/demoapps/DoseAcc/DoseAccHelper.cpp new file mode 100644 index 0000000..905eb74 --- /dev/null +++ b/demoapps/DoseAcc/DoseAccHelper.cpp @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// 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 "DoseAccHelper.h" + +#include "mapRegistrationFileReader.h" +#include "itkImageFileWriter.h" + +#include "rttbVirtuosPlanFileDoseAccessorGenerator.h" + +#include "rttbITKImageFileDoseAccessorGenerator.h" +#include "rttbSimpleMappableDoseAccessor.h" +#include "rttbMatchPointTransformation.h" +#include "rttbLinearInterpolation.h" +#include "rttbITKImageDoseAccessorConverter.h" +#include "rttbArithmetic.h" +#include "rttbBinaryFunctorDoseAccessor.h" + + +rttb::core::DoseAccessorInterface::DoseAccessorPointer +rttb::apps::doseAcc::loadDose(const std::string& fileName) +{ + rttb::io::itk::ITKImageFileDoseAccessorGenerator generator(fileName); + return generator.generateDoseAccessor(); +}; + +rttb::core::DoseAccessorInterface::DoseAccessorPointer +rttb::apps::doseAcc::loadVirtuosDose(const std::string& fileName, const std::string& planFileName) +{ + rttb::io::virtuos::VirtuosPlanFileDoseAccessorGenerator generator(fileName, planFileName); + return generator.generateDoseAccessor(); +}; + +rttb::apps::doseAcc::ApplicationData::RegistrationType::Pointer +rttb::apps::doseAcc::loadRegistration(const std::string& fileName) +{ + map::io::RegistrationFileReader::Pointer spRegReader = map::io::RegistrationFileReader::New(); + + map::io::RegistrationFileReader::LoadedRegistrationPointer spReg; + + std::cout << std::endl << "read registration file... "; + spReg = spRegReader->read(fileName); + std::cout << "done." << std::endl; + + ApplicationData::RegistrationType::Pointer resultPtr = + dynamic_cast(spReg.GetPointer()); + + if (resultPtr.IsNull()) + { + mapDefaultExceptionStaticMacro( << + "Loaded registration cannot be used. Only 3D 3D registrations are allowed."); + } + + return resultPtr; +}; + + +/**Private helper function for processData(). Generates a suitable output accessor + * (depending on the configuration in appData a suitable accessor pipeline is established) + * which performs the accumulation of the doses and returns the output.to */ +rttb::core::DoseAccessorInterface::DoseAccessorPointer +assembleOutputAccessor(rttb::apps::doseAcc::ApplicationData& appData) +{ + + rttb::core::DoseAccessorInterface::DoseAccessorPointer dose2Accessor = appData._Dose2; + + if (appData._spReg.IsNotNull()) + { + rttb::interpolation::LinearInterpolation::Pointer interpolate = + rttb::interpolation::LinearInterpolation::Pointer(new + rttb::interpolation::LinearInterpolation()); + rttb::interpolation::TransformationInterface::Pointer transform = + rttb::interpolation::TransformationInterface::Pointer(new + rttb::interpolation::MatchPointTransformation(appData._spReg)); + dose2Accessor = rttb::core::DoseAccessorInterface::DoseAccessorPointer( + new rttb::interpolation::SimpleMappableDoseAccessor(appData._Dose1->getGeometricInfo(), + appData._Dose2, transform, interpolate)); + } + + rttb::algorithms::arithmetic::doseOp::AddWeighted addOp(appData._weightDose1, appData._weightDose2); + rttb::core::DoseAccessorInterface::DoseAccessorPointer outputAccessor = + rttb::core::DoseAccessorInterface::DoseAccessorPointer(new + rttb::algorithms::BinaryFunctorDoseAccessor + (appData._Dose1, dose2Accessor, addOp)); + + return outputAccessor; +} + +void +rttb::apps::doseAcc::processData(rttb::apps::doseAcc::ApplicationData& appData) +{ + rttb::core::DoseAccessorInterface::DoseAccessorPointer outputAccessor = assembleOutputAccessor( + appData); + + std::cout << std::endl << "generate output image... "; + io::itk::ITKImageDoseAccessorConverter converter(outputAccessor); + converter.setFailOnInvalidIDs(true); + converter.process(); + io::itk::ITKDoseImageType::Pointer itkImage = converter.getITKImage(); + std::cout << "done." << std::endl; + + typedef ::itk::ImageFileWriter WriterType; + + std::cout << std::endl << "write output image... "; + WriterType::Pointer writer = WriterType::New(); + writer->SetInput(itkImage); + writer->SetFileName(appData._outputFileName); + writer->Write(); + std::cout << "done." << std::endl; +}; diff --git a/demoapps/DoseAcc/DoseAccHelper.h b/demoapps/DoseAcc/DoseAccHelper.h new file mode 100644 index 0000000..7bfb6d7 --- /dev/null +++ b/demoapps/DoseAcc/DoseAccHelper.h @@ -0,0 +1,49 @@ +// ----------------------------------------------------------------------- +// 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_ACC_HELPER_H +#define __DOSE_ACC_HELPER_H + +#include "DoseAccApplicationData.h" + +namespace rttb +{ + namespace apps + { + namespace doseAcc + { + /**loads the dose from a file stored in an ITK supported data format. Throws exception if loading fails*/ + core::DoseAccessorInterface::DoseAccessorPointer loadDose(const std::string& fileName); + /**loads the dose from a file stored in Virtuos data format. Throws exception if loading fails*/ + core::DoseAccessorInterface::DoseAccessorPointer loadVirtuosDose(const std::string& fileName, const std::string& planFileName); + + ApplicationData::RegistrationType::Pointer loadRegistration(const std::string& fileName); + + /**Containes the business logic for the accumulation of the doses and the storing of the result. + Uses appData for the input data and the correct configuration.*/ + void processData(ApplicationData& appData); + + } + } +} + + +#endif diff --git a/demoapps/DoseAcc/files.cmake b/demoapps/DoseAcc/files.cmake new file mode 100644 index 0000000..df077f8 --- /dev/null +++ b/demoapps/DoseAcc/files.cmake @@ -0,0 +1,13 @@ +SET(CPP_FILES +DoseAcc.cpp +DoseAccHelper.cpp +DoseAccApplicationData.cpp +) + +SET(H_FILES +DoseAccHelper.h +DoseAccApplicationData.h +) + +SET(TPP_FILES +) diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt new file mode 100644 index 0000000..148e568 --- /dev/null +++ b/documentation/CMakeLists.txt @@ -0,0 +1,5 @@ +MESSAGE (STATUS "processing RTToolbox documentation") + +CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/documentation/doxygen.config.in + ${RTToolbox_BINARY_DIR}/documentation/doxygen.config) + diff --git a/documentation/RTToolbox_info.txt b/documentation/RTToolbox_info.txt new file mode 100644 index 0000000..cf8a76a --- /dev/null +++ b/documentation/RTToolbox_info.txt @@ -0,0 +1,76 @@ +/** \mainpage RTToolbox - DKFZ radiotherapy quantitative evaluation library +\section intro Introduction + +Welcome to RTToolbox ...\n\n + +\image html Homepage-RTToolbox.png +Radiotherapy is a comprehensive and fast-moving discipline which plays a major role +in cancer care. Software tools which support and simplify the quantitative evaluation +process of radiotherapy are essential to support decision makings and improve therapy +effectiveness.\n +RTToolbox is a software library to support quantitative analysis of treatment outcome +for radiotherapy.\n\n + +The RTToolbox was designed following object-oriented design (OOD) principles and was +implemented in the language C++. It supports the import of radiotherapy data (e.g. dose +distributions and structure sets) from arbitrary format (e.g. stored in DICOM-RT +standard format using the open source library DCMTK).\n\n + +The RTToolbox supports the full range of radiotherapy evaluation. +Tools such as DVH calculation, arithmetic operations on dose distributions and +structure relationship analysis (e.g. fully-contained, partially-contained) +are provided as a basis for further calculations. Dose comparison indices such as +Conformity Index (CI), Homogeneity Index (HI) and Conformation Number (CN), +and biological models including TCP, NTCP, EUD and BED are determined from dose +distributions and DVHs. The RTToolbox can be easily extended to support other +data format, comparison indices and models. A PostgreSQL database was designed +to store study information and analysis results. The communication interfaces +between toolbox and database are provided as well. Thus, using the RTToolbox +radiotherapy evaluation applications can be built easily and quickly. + +\section Structure +The RTToolbox code is contained in topic related folders: +
    +
  • \b code: you will find all relevant code for RT data evaluation. +
      +
    • \b core: The main code of the RTToolbox. Here you'll find all interface classes, the main objects used and the DVH +calculation.
    • +
    • \b algorithm: Here you find the code for dose statistics.
    • +
    • \b indices: The code for dose comparison indices can be found here.
    • +
    • \b io: Containes sub folderes for different formats.
    • +
        +
      • \b dicom: Support for the dicom format (You need DCMTK to build this option).
      • +
      • \b other: Data from text formats.
      • +
      +
    • \b masks: Here you can find the code for the original toolbox voxelisation. We are currently +re-designing the atructure of the RT-Toolbox, therefore this folder also includes some +legacy code required by this voxelization, that should not be used elsewhere. Please use the +data objects and interfaces defined in 'core'.
    • +
    • \b models: It contains the code for the calculation of TCP and NTCP models.
    • +
    +
  • \b testing: In this folder you find corresponding unit tests (similar +folder structure as in code) and more extensive tests, which use multiple classes in 'examples'.
  • +
  • \b utilities: This folder contains third party projects needed to build RTToolbox (depending on the activated modules).
  • +
+ +\section Contact +RTToolbox homepage: http://www.dkfz.de/en/sidt/projects/rttb/info.html\n\n + +Software Development for Integrated Diagnostics and Therapy (SIDT),\n +German Cancer Research Center (DKFZ), Heidelberg, Germany.\n\n + +Web: http://www.dkfz.de/en/sidt/index.html\n +Tel.: +49 6221 42-30-21\n +E-mail: l.zhang@dkfz.de (L. Zhang) or sbr@dkfz.de\n + +\section copyright Copyright +Copyright (c) German Cancer Research Center (DKFZ), +Software development for Integrated Diagnostics and Therapy (SIDT).\n +ALL RIGHTS RESERVED.\n +See rttbCopyright.txt or +http://www.dkfz.de/en/sidt/projects/rttb/copyright.html\n\n + +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. +*/ \ No newline at end of file diff --git a/documentation/doxygen.config.in b/documentation/doxygen.config.in new file mode 100644 index 0000000..858566c --- /dev/null +++ b/documentation/doxygen.config.in @@ -0,0 +1,289 @@ +# Doxyfile 1.5.6 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = RTToolbox +PROJECT_NUMBER = "@RTToolbox_FULL_VERSION_STRING@" +OUTPUT_DIRECTORY = "@RTToolbox_BINARY_DIR@/documentation/" +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = "@RTToolbox_BINARY_DIR@/documentation/" \ + "@RTToolbox_SOURCE_DIR@" +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = "eguarantee=\par \"Exception guarantee:\"" +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = YES +EXTRACT_STATIC = YES +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_CLASSES = YES +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = YES +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = "@RTToolbox_SOURCE_DIR@/documentation/RTToolbox_info.txt" \ + "@RTToolbox_SOURCE_DIR@/code" +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.tpp +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */.svn/* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = "@RTToolbox_SOURCE_DIR@/documentation/images" +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NONE +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = YES +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = "" +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/documentation/images/Homepage-RTToolbox.png b/documentation/images/Homepage-RTToolbox.png new file mode 100644 index 0000000..a07475a Binary files /dev/null and b/documentation/images/Homepage-RTToolbox.png differ diff --git a/documentation/sqlQuery/create.txt b/documentation/sqlQuery/create.txt new file mode 100644 index 0000000..555fc4d --- /dev/null +++ b/documentation/sqlQuery/create.txt @@ -0,0 +1,509 @@ +-- Database: rttb + +-- DROP DATABASE rttb; + +CREATE DATABASE rttb + WITH OWNER = postgres + ENCODING = 'UTF8'; +GRANT ALL ON DATABASE rttb TO public; +GRANT ALL ON DATABASE rttb TO postgres; +COMMENT ON DATABASE rttb IS 'RT Auswertungsdatenbank'; + + +-- Schema: "public" + +-- DROP SCHEMA public; + +CREATE SCHEMA public + AUTHORIZATION postgres; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO public; +COMMENT ON SCHEMA public IS 'standard public schema'; + +-- Table: bio_model_algorithm + +-- DROP TABLE bio_model_algorithm; + +CREATE TABLE bio_model_algorithm +( + id serial NOT NULL, + "name" character varying(255), + reference character varying(255), + CONSTRAINT bio_model_algorithm_pkey PRIMARY KEY (id) +) +WITH (OIDS=FALSE); +ALTER TABLE bio_model_algorithm OWNER TO postgres; + + +-- Table: bio_model_parameter + +-- DROP TABLE bio_model_parameter; + +CREATE TABLE bio_model_parameter +( + id serial NOT NULL, + "name" character varying(255), + CONSTRAINT bio_model_parameter_pkey PRIMARY KEY (id) +) +WITH (OIDS=FALSE); +ALTER TABLE bio_model_parameter OWNER TO postgres; + +-- Table: bio_model_parameter_value + +-- DROP TABLE bio_model_parameter_value; + +CREATE TABLE bio_model_parameter_value +( + id serial NOT NULL, + bio_model_parameter_id integer NOT NULL, + "value" double precision, + CONSTRAINT bio_model_parameter_value_pkey PRIMARY KEY (id), + CONSTRAINT bio_model_parameter_value_bio_model_parameter_id_fkey FOREIGN KEY (bio_model_parameter_id) + REFERENCES bio_model_parameter (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE bio_model_parameter_value OWNER TO postgres; + +-- Table: clinical_trial + +-- DROP TABLE clinical_trial; + +CREATE TABLE clinical_trial +( + id serial NOT NULL, + "name" character varying(255), + CONSTRAINT clinical_trial_pkey PRIMARY KEY (id) +) +WITH (OIDS=FALSE); +ALTER TABLE clinical_trial OWNER TO postgres; +COMMENT ON TABLE clinical_trial IS 'Clinical Trial'; + +-- Table: patient + +-- DROP TABLE patient; + +CREATE TABLE patient +( + id serial NOT NULL, + size integer, + "name" character varying(255), + sex character varying(255), + age integer, + weight double precision, + clinical_trial_id integer, + CONSTRAINT patient_pkey PRIMARY KEY (id), + CONSTRAINT patient_clinical_trial_id_fkey FOREIGN KEY (clinical_trial_id) + REFERENCES clinical_trial (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + +-- Table: therapy + +-- DROP TABLE therapy; + +CREATE TABLE therapy +( + id serial NOT NULL, + patient_id integer NOT NULL, + CONSTRAINT therapy_pkey PRIMARY KEY (id), + CONSTRAINT therapy_patient_id_fkey FOREIGN KEY (patient_id) + REFERENCES patient (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + + +-- Table: fraction_manager + +-- DROP TABLE fraction_manager; + +CREATE TABLE fraction_manager +( + id serial NOT NULL, + therapy_id integer NOT NULL, + CONSTRAINT fraction_manager_pkey PRIMARY KEY (id), + CONSTRAINT fraction_manager_therapy_id_fkey FOREIGN KEY (therapy_id) + REFERENCES therapy (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + +CREATE TABLE fraction +( + id serial NOT NULL, + fraction_manager_id integer NOT NULL, + CONSTRAINT fraction_pkey PRIMARY KEY (id), + CONSTRAINT fraction_fraction_manager_id_fkey FOREIGN KEY (fraction_manager_id) + REFERENCES fraction_manager (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + +-- Table: dvh_set + +-- DROP TABLE dvh_set; + +CREATE TABLE dvh_set +( + id serial NOT NULL, + therapy_id integer NOT NULL, + fraction_id integer NOT NULL, + CONSTRAINT dvh_set_pkey PRIMARY KEY (id), + CONSTRAINT dvh_set_fraction_id_fkey FOREIGN KEY (fraction_id) + REFERENCES fraction (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT dvh_set_therapy_id_fkey FOREIGN KEY (therapy_id) + REFERENCES therapy (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + +-- Table: coverage_index + +-- DROP TABLE coverage_index; + +CREATE TABLE coverage_index +( + id serial NOT NULL, + "value" double precision, + dvh_set_id integer NOT NULL, + CONSTRAINT coverage_index_pkey PRIMARY KEY (id), + CONSTRAINT coverage_index_dvh_set_id_fkey FOREIGN KEY (dvh_set_id) + REFERENCES dvh_set (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE coverage_index OWNER TO postgres; + +-- Table: dose + +-- DROP TABLE dose; + +CREATE TABLE dose +( + id serial NOT NULL, + therapy_id integer NOT NULL, + fraction_id integer NOT NULL, + path character varying(255), + CONSTRAINT dose_pkey PRIMARY KEY (id), + CONSTRAINT dose_fraction_id_fkey FOREIGN KEY (fraction_id) + REFERENCES fraction (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT dose_therapy_id_fkey FOREIGN KEY (therapy_id) + REFERENCES therapy (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + + + +-- Table: dose_statistics + +-- DROP TABLE dose_statistics; + +CREATE TABLE dose_statistics +( + id serial NOT NULL, + maximum double precision, + minimum double precision, + mean double precision, + std_deviation double precision, + variance double precision, + dose_id integer NOT NULL, + CONSTRAINT dose_statistics_pkey PRIMARY KEY (id), + CONSTRAINT dose_statistics_dose_id_fkey FOREIGN KEY (dose_id) + REFERENCES dose (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); + + +-- Table: plan + +-- DROP TABLE plan; + +CREATE TABLE plan +( + id serial NOT NULL, + therapy_id integer NOT NULL, + fraction_id integer NOT NULL, + CONSTRAINT plan_pkey PRIMARY KEY (id), + CONSTRAINT plan_fraction_id_fkey FOREIGN KEY (fraction_id) + REFERENCES fraction (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT plan_therapy_id_fkey FOREIGN KEY (therapy_id) + REFERENCES therapy (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE plan OWNER TO postgres; + +-- Table: structure_set + +-- DROP TABLE structure_set; + +CREATE TABLE structure_set +( + id serial NOT NULL, + therapy_id integer NOT NULL, + CONSTRAINT structure_set_pkey PRIMARY KEY (id), + CONSTRAINT structure_set_therapy_id_fkey FOREIGN KEY (therapy_id) + REFERENCES therapy (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE structure_set OWNER TO postgres; + +-- Table: structure + +-- DROP TABLE structure; + +CREATE TABLE structure +( + id serial NOT NULL, + structure_set_id integer NOT NULL, + CONSTRAINT structure_pkey PRIMARY KEY (id), + CONSTRAINT structure_structure_set_id_fkey FOREIGN KEY (structure_set_id) + REFERENCES structure_set (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE structure OWNER TO postgres; + +-- Table: dvh_type + +-- DROP TABLE dvh_type; + +CREATE TABLE dvh_type +( + id serial NOT NULL, + "name" character varying(255), + CONSTRAINT dvh_type_pkey PRIMARY KEY (id) +) +WITH (OIDS=FALSE); +ALTER TABLE dvh_type OWNER TO postgres; + + +-- Table: dvh + +-- DROP TABLE dvh; + +CREATE TABLE dvh +( + id serial NOT NULL, + plan_id integer, + structure_id integer, + dose_id integer, + dvh_set_id integer, + "deltaD" double precision, -- Absolute dose value of a dose-bin in Gy + "deltaV" double precision, -- Volume of a voxel in cm3 + dvh_type_id integer NOT NULL, + CONSTRAINT dvh_pkey PRIMARY KEY (id), + CONSTRAINT dvh_dose_id_fkey FOREIGN KEY (dose_id) + REFERENCES dose (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT dvh_dvh_set_id_fkey FOREIGN KEY (dvh_set_id) + REFERENCES dvh_set (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT dvh_dvh_type_id_fkey FOREIGN KEY (dvh_type_id) + REFERENCES dvh_type (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT dvh_plan_id_fkey FOREIGN KEY (plan_id) + REFERENCES plan (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT dvh_structure_id_fkey FOREIGN KEY (structure_id) + REFERENCES structure (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE dvh OWNER TO postgres; +COMMENT ON COLUMN dvh."deltaD" IS 'Absolute dose value of a dose-bin in Gy'; +COMMENT ON COLUMN dvh."deltaV" IS 'Volume of a voxel in cm3'; + + +-- Table: dvh_data + +-- DROP TABLE dvh_data; + +CREATE TABLE dvh_data +( + id serial NOT NULL, + dose double precision, -- dose_bin or absolute dose + volume double precision, -- numbe of voxels or absolute volume in cm3 or mm3 + dvh_id integer NOT NULL, + CONSTRAINT dvh_data_pkey PRIMARY KEY (id), + CONSTRAINT dvh_data_dvh_id_fkey FOREIGN KEY (dvh_id) + REFERENCES dvh (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE dvh_data OWNER TO postgres; +COMMENT ON COLUMN dvh_data.dose IS 'dose_bin or absolute dose'; +COMMENT ON COLUMN dvh_data.volume IS 'numbe of voxels or absolute volume in cm3 or mm3'; + + + + + + + +-- Table: homogeneity_index + +-- DROP TABLE homogeneity_index; + +CREATE TABLE homogeneity_index +( + id serial NOT NULL, + "value" double precision, + dvh_set_id integer NOT NULL, + CONSTRAINT homogeneity_index_pkey PRIMARY KEY (id), + CONSTRAINT homogeneity_index_dvh_set_id_fkey FOREIGN KEY (dvh_set_id) + REFERENCES dvh_set (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE homogeneity_index OWNER TO postgres; + + +-- Table: ntcp + +-- DROP TABLE ntcp; + +CREATE TABLE ntcp +( + id serial NOT NULL, + "value" double precision, + dvh_id integer NOT NULL, + bio_model_algorithm_id integer NOT NULL, + CONSTRAINT ntcp_pkey PRIMARY KEY (id), + CONSTRAINT ntcp_bio_model_algorithm_id_fkey FOREIGN KEY (bio_model_algorithm_id) + REFERENCES bio_model_algorithm (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT ntcp_dvh_id_fkey FOREIGN KEY (dvh_id) + REFERENCES dvh (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE ntcp OWNER TO postgres; + + +-- Table: ntcp_parameter_set + +-- DROP TABLE ntcp_parameter_set; + +CREATE TABLE ntcp_parameter_set +( + id serial NOT NULL, + ntcp_id integer NOT NULL, + bio_model_parameter_value_id integer NOT NULL, + CONSTRAINT ntcp_parameter_set_pkey PRIMARY KEY (id), + CONSTRAINT ntcp_parameter_set_bio_model_parameter_value_id_fkey FOREIGN KEY (bio_model_parameter_value_id) + REFERENCES bio_model_parameter_value (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT ntcp_parameter_set_ntcp_id_fkey FOREIGN KEY (ntcp_id) + REFERENCES ntcp (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE ntcp_parameter_set OWNER TO postgres; + + + + + + + +-- Table: structure_geo_relation + +-- DROP TABLE structure_geo_relation; + +CREATE TABLE structure_geo_relation +( + id serial NOT NULL, + structure_id1 integer NOT NULL, + structure_id2 integer NOT NULL, + relationship character varying(255), + CONSTRAINT structure_geo_relation_pkey PRIMARY KEY (id), + CONSTRAINT structure_geo_relation_structure_id1_fkey FOREIGN KEY (structure_id1) + REFERENCES structure (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT structure_geo_relation_structure_id2_fkey FOREIGN KEY (structure_id2) + REFERENCES structure (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE structure_geo_relation OWNER TO postgres; + + + + + +-- Table: structure_volume_change + +-- DROP TABLE structure_volume_change; + +CREATE TABLE structure_volume_change +( + id serial NOT NULL, + "value" double precision, + begin_time time with time zone, + end_time time with time zone, + structure_id integer NOT NULL, + CONSTRAINT structure_volume_change_pkey PRIMARY KEY (id), + CONSTRAINT structure_volume_change_structure_id_fkey FOREIGN KEY (structure_id) + REFERENCES structure (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE structure_volume_change OWNER TO postgres; + + +-- Table: tcp + +-- DROP TABLE tcp; + +CREATE TABLE tcp +( + id serial NOT NULL, + "value" double precision, + dvh_id integer NOT NULL, + bio_model_algorithm_id integer NOT NULL, + CONSTRAINT tcp_pkey PRIMARY KEY (id), + CONSTRAINT tcp_bio_model_algorithm_id_fkey FOREIGN KEY (bio_model_algorithm_id) + REFERENCES bio_model_algorithm (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT tcp_dvh_id_fkey FOREIGN KEY (dvh_id) + REFERENCES dvh (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE tcp OWNER TO postgres; + + +-- Table: tcp_parameter_set + +-- DROP TABLE tcp_parameter_set; + +CREATE TABLE tcp_parameter_set +( + id serial NOT NULL, + tcp_id integer NOT NULL, + bio_model_parameter_value_id integer NOT NULL, + CONSTRAINT tcp_parameter_set_pkey PRIMARY KEY (id), + CONSTRAINT tcp_parameter_set_bio_model_parameter_value_id_fkey FOREIGN KEY (bio_model_parameter_value_id) + REFERENCES bio_model_parameter_value (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT tcp_parameter_set_tcp_id_fkey FOREIGN KEY (tcp_id) + REFERENCES tcp (id) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION +) +WITH (OIDS=FALSE); +ALTER TABLE tcp_parameter_set OWNER TO postgres; + + + + + + diff --git a/documentation/sqlQuery/insert.txt b/documentation/sqlQuery/insert.txt new file mode 100644 index 0000000..d92344d --- /dev/null +++ b/documentation/sqlQuery/insert.txt @@ -0,0 +1,48 @@ + + +INSERT INTO dvh_type(name) +VALUES ('DIFFERENTIAL'); + +INSERT INTO dvh_type(name) +VALUES ('CUMMULATIVE'); + +INSERT INTO dvh_type(name) +VALUES ('BED'); + +INSERT INTO dvh_type(name) +VALUES ('LQED2'); + +INSERT INTO dvh_type(name) +VALUES ('OER_WEIGHTED'); + +INSERT INTO bio_model_algorithm( + "name", reference) + VALUES ('TCP LQ Model', 'LQ Model (Nahum and Sanchez-Nieto 2001, Hall and Giaccia 2006)'); +INSERT INTO bio_model_algorithm( + "name", reference) + VALUES ('NTCP LKB Model', 'L-K-B Model (Lyman 1985, Kutcher and Burman 1989)'); +INSERT INTO bio_model_algorithm( + "name", reference) + VALUES ('NTCP RS Model', 'The relative seriality model (Källman 1992)'); + +INSERT INTO bio_model_parameter( + "name") + VALUES ('alphaMean'); +INSERT INTO bio_model_parameter( + "name") + VALUES ('alpha/beta'); + INSERT INTO bio_model_parameter( + "name") + VALUES ('alphaVariance'); + INSERT INTO bio_model_parameter( + "name") + VALUES ('rho'); + INSERT INTO bio_model_parameter( + "name") + VALUES ('a'); + INSERT INTO bio_model_parameter( + "name") + VALUES ('m'); + INSERT INTO bio_model_parameter( + "name") + VALUES ('D50'); \ No newline at end of file diff --git a/rttbCopyright.txt b/rttbCopyright.txt new file mode 100644 index 0000000..0c08147 --- /dev/null +++ b/rttbCopyright.txt @@ -0,0 +1,30 @@ +RTToolbox - DKFZ radiotherapy quantitative evaluation library + +Copyright (C) 2013 German Cancer Research Center (DKFZ), +Software development for Integrated Diagnostics and Therapy (SIDT) +All rights reserved. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + + +Contact: + +Software Development for Integrated Diagnostics and Therapy (SIDT), +German Cancer Research Center (DKFZ), Heidelberg, Germany. + +Web: http://www.dkfz.de/en/sidt/index.html +Tel.: +49 6221 42-30-21. +E-mail: l.zhang@dkfz.de (L. Zhang) or sbr@dkfz.de + + diff --git a/rttbGenerateRTToolboxConfig.cmake b/rttbGenerateRTToolboxConfig.cmake new file mode 100644 index 0000000..3112236 --- /dev/null +++ b/rttbGenerateRTToolboxConfig.cmake @@ -0,0 +1,58 @@ +# Generate the RTToolboxConfig.cmake file in the build tree. Also configure +# one for installation. The file tells external projects how to use +# RTToolbox. + +#----------------------------------------------------------------------------- +# Settings specific to the build tree. + +# The "use" file. +SET(RTToolbox_USE_FILE ${RTToolbox_BINARY_DIR}/UseRTToolbox.cmake) + +# The library dependencies file. +SET(RTToolbox_LIBRARY_DEPENDS_FILE ${RTToolbox_BINARY_DIR}/RTToolboxLibraryDepends.cmake) + +# Library directory. +SET(RTToolbox_LIBRARY_DIRS_CONFIG ${RTToolbox_LIBRARY_PATH}) + +# Determine the include directories needed. +SET(RTToolbox_INCLUDE_DIRS_CONFIG "") +FILE(GLOB RTTB_confFiles "${RTTB_MODULES_CONF_DIR}/RTTB*Config.cmake") +FOREACH(RTTB_confFile ${RTTB_confFiles}) + INCLUDE(${RTTB_confFile}) + IF (${${MODULE_CURRENT_NAME}_IS_ENABLED}) + SET(RTToolbox_INCLUDE_DIRS_CONFIG ${RTToolbox_INCLUDE_DIRS_CONFIG} ${${MODULE_CURRENT_NAME}_INCLUDE_DIRS}) + ENDIF (${${MODULE_CURRENT_NAME}_IS_ENABLED}) +ENDFOREACH(RTTB_confFile) + +SET(RTToolbox_INCLUDE_DIRS_CONFIG ${RTToolbox_BINARY_DIR} ${RTTB_MODULES_CONF_DIR} + ${RTToolbox_INCLUDE_DIRS_CONFIG} +) + +#----------------------------------------------------------------------------- +# Configure RTToolboxConfig.cmake for the build tree. +CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/RTToolboxConfig.cmake.in + ${RTToolbox_BINARY_DIR}/RTToolboxConfig.cmake @ONLY IMMEDIATE) + +#----------------------------------------------------------------------------- +# Settings specific to the install tree. + +# The "use" file. +SET(RTToolbox_USE_FILE ${CMAKE_INSTALL_PREFIX}${RTToolbox_INSTALL_PACKAGE_DIR}/UseRTToolbox.cmake) + +# The library dependencies file. +SET(RTToolbox_LIBRARY_DEPENDS_FILE + ${CMAKE_INSTALL_PREFIX}${RTToolbox_INSTALL_PACKAGE_DIR}/RTToolboxLibraryDepends.cmake) + +# Include directories. +SET(RTToolbox_INCLUDE_DIRS_CONFIG + ${RTToolbox_INCLUDE_DIRS_INSTALL_TREE} + ${RTToolbox_INCLUDE_DIRS_SYSTEM} +) + +# Link directories. +SET(RTToolbox_LIBRARY_DIRS_CONFIG ${CMAKE_INSTALL_PREFIX}${RTToolbox_INSTALL_LIB_DIR}) + +#----------------------------------------------------------------------------- +# Configure RTToolboxConfig.cmake for the install tree. +CONFIGURE_FILE(${RTToolbox_SOURCE_DIR}/RTToolboxConfig.cmake.in + ${RTToolbox_BINARY_DIR}/RTToolboxConfig.cmake.install @ONLY IMMEDIATE) diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt new file mode 100644 index 0000000..dd17f67 --- /dev/null +++ b/testing/CMakeLists.txt @@ -0,0 +1,67 @@ +MESSAGE(STATUS "processing RTToolbox testing code") + +# Testing branch +PROJECT(RTTBTesting) + +#----------------------------------------------------------------------------- +# Find Litmus. +#----------------------------------------------------------------------------- +MESSAGE(STATUS "searching Litmus") +FIND_PACKAGE(Litmus) +IF(Litmus_FOUND) +INCLUDE(${Litmus_USE_FILE}) +SET(ADDITIONAL_TEST_INCLUDES ${Litmus_USE_FILE}) +ELSE(Litmus_FOUND) +MESSAGE(FATAL_ERROR + "Cannot build without Litmus. Please set Litmus_DIR.") +ENDIF(Litmus_FOUND) + +#----------------------------------------------------------------------------- +# Configure Testing branch +#----------------------------------------------------------------------------- +MAKE_DIRECTORY(${RTTBTesting_BINARY_DIR}/Temporary) + + +MESSAGE(STATUS "Process All Tests...") + +#----------------------------------------------------------------------------- +# Include sub directories +#----------------------------------------------------------------------------- +OPTION(BUILD_RTToolbox_core_Tester "build project on/off" OFF) +OPTION(BUILD_RTToolbox_Test_examples "build project on/off" OFF) +OPTION(BUILD_RTToolbox_algorithms_Tester "build project on/off" OFF) +OPTION(BUILD_RTToolbox_models_Tester "build project on/off" OFF) +OPTION(BUILD_RTToolbox_io_Tester "build project on/off" OFF) +OPTION(BUILD_RTToolbox_masks_Tester "build project on/off" OFF) +OPTION(BUILD_RTToolbox_interpolation_Tester "build project on/off" OFF) + +IF(${BUILD_RTToolbox_core_Tester} STREQUAL ON) +ADD_SUBDIRECTORY(core) +ENDIF(${BUILD_RTToolbox_core_Tester} STREQUAL ON) + +IF(${BUILD_RTToolbox_Test_examples} STREQUAL ON) +ADD_SUBDIRECTORY(examples) +ENDIF(${BUILD_RTToolbox_Test_examples} STREQUAL ON) + +IF(${BUILD_RTToolbox_algorithms_Tester} STREQUAL ON) +ADD_SUBDIRECTORY(algorithms) +ENDIF(${BUILD_RTToolbox_algorithms_Tester} STREQUAL ON) + +IF(${BUILD_RTToolbox_models_Tester} STREQUAL ON) +ADD_SUBDIRECTORY(models) +ENDIF(${BUILD_RTToolbox_models_Tester} STREQUAL ON) + +IF(${BUILD_RTToolbox_io_Tester} STREQUAL ON) +ADD_SUBDIRECTORY(io) +ENDIF(${BUILD_RTToolbox_io_Tester} STREQUAL ON) + +IF(${BUILD_RTToolbox_masks_Tester} STREQUAL ON) +ADD_SUBDIRECTORY(masks) +ENDIF(${BUILD_RTToolbox_masks_Tester} STREQUAL ON) + +IF(${BUILD_RTToolbox_interpolation_Tester} STREQUAL ON) +ADD_SUBDIRECTORY(interpolation) +ENDIF(${BUILD_RTToolbox_interpolation_Tester} STREQUAL ON) + + + diff --git a/testing/algorithms/ArithmeticTest.cpp b/testing/algorithms/ArithmeticTest.cpp new file mode 100644 index 0000000..c0f2798 --- /dev/null +++ b/testing/algorithms/ArithmeticTest.cpp @@ -0,0 +1,352 @@ +// ----------------------------------------------------------------------- +// 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 "../core/DummyDoseAccessor.h" +#include "../core/DummyMaskAccessor.h" +#include "../core/DummyMutableDoseAccessor.h" +#include "rttbDoseAccessorInterface.h" +#include "rttbMutableDoseAccessorInterface.h" +#include "rttbMutableMaskAccessorInterface.h" +#include "rttbArithmetic.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbGenericMutableMaskAccessor.h" +#include "rttbMaskAccessorInterface.h" + +namespace rttb +{ + namespace testing + { + + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef core::MutableDoseAccessorInterface::MutableDoseAccessorPointer MutableDoseAccessorPointer; + typedef DummyMaskAccessor::MaskVoxelListPointer MaskVoxelListPointer; + typedef DummyMaskAccessor::MaskVoxelList MaskVoxelList; + typedef core::MaskAccessorInterface::MaskAccessorPointer MaskAccessorPointer; + typedef core::MutableMaskAccessorInterface::MutableMaskAccessorPointer MutableMaskAccessorPointer; + + /*! @brief ArithmeticTest - tests arithmetic combinations of accessors + 1) test dose-dose operations + 2) test dose-mask operations + 3) test mask-mask operations + 4) test convenience functions + */ + + int ArithmeticTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + // initialize accessors for arithmetic + + //DOSE: + //Null pointers + DoseAccessorPointer spDoseNull; + MutableDoseAccessorPointer spMutableDoseNull; + //dose with random values between 0 and 100 + boost::shared_ptr spTestDoseAccessor1 = boost::make_shared(); + DoseAccessorPointer spDoseAccessor1(spTestDoseAccessor1); + //generate a 2nd dose with fixed values + core::GeometricInfo geoInfo = spDoseAccessor1->getGeometricInfo(); + + DoseTypeGy valFix = 101; // to ensure values are larger than 100 on adding + std::vector dose2Vals(geoInfo.getNumberOfVoxels(),valFix); + + boost::shared_ptr spTestDoseAccessor2 = boost::make_shared + (dose2Vals, geoInfo); + DoseAccessorPointer spDoseAccessor2(spTestDoseAccessor2); + + //generate result acessor + std::vector doseResultVals(geoInfo.getNumberOfVoxels(),-1); + boost::shared_ptr spTestMutableDoseAccessor = + boost::make_shared(doseResultVals, geoInfo); + MutableDoseAccessorPointer spMutableDoseAccessor(spTestMutableDoseAccessor); + + // different geometricInfo + core::GeometricInfo geoInfo2 = geoInfo; + geoInfo2.setNumColumns(5); + geoInfo2.setNumRows(5); + geoInfo2.setNumSlices(5); + std::vector dose3Vals(geoInfo2.getNumberOfVoxels(),valFix); + boost::shared_ptr spTestDoseAccessor3 = boost::make_shared + (dose3Vals, geoInfo2); + DoseAccessorPointer spDoseAccessorDiffGeoInfo(spTestDoseAccessor3); + + boost::shared_ptr spTestMutableDoseAccessor2 = + boost::make_shared(dose3Vals, geoInfo2); + MutableDoseAccessorPointer spMutableDoseAccessorDiffGeoInfo(spTestMutableDoseAccessor2); + + + //MASK: + //null pointer + MaskAccessorPointer spMaskAccessorNull; + MutableMaskAccessorPointer spMutableMaskAccessorNull; + + MaskVoxelList voxelList; + FractionType aVolumeFraction = 1; + VoxelGridID aVoxelGridID = 10; + + //generate a dummy mask + while (aVoxelGridID < geoInfo.getNumberOfVoxels() && aVoxelGridID <= 30) + { + voxelList.push_back(core::MaskVoxel(aVoxelGridID, aVolumeFraction)); + ++aVoxelGridID; + } + + MaskVoxelListPointer voxelListPtr = boost::make_shared(voxelList); + boost::shared_ptr dummyMask1 = boost::make_shared(geoInfo, + voxelListPtr); + MaskAccessorPointer spMaskAccessor1(dummyMask1); + + MaskVoxelList voxelList2; + aVoxelGridID = 20; + + //generate a 2nd dummy mask that partly overlaps with the 1st one + while (aVoxelGridID < geoInfo.getNumberOfVoxels() && aVoxelGridID <= 40) + { + voxelList2.push_back(core::MaskVoxel(aVoxelGridID, aVolumeFraction)); + ++aVoxelGridID; + } + + MaskVoxelListPointer voxelListPtr2 = boost::make_shared(voxelList2); + boost::shared_ptr dummyMask2 = boost::make_shared(geoInfo, + voxelListPtr2); + MaskAccessorPointer spMaskAccessor2(dummyMask2); + // result accessor + boost::shared_ptr mMask1 = + boost::make_shared(geoInfo); + MutableMaskAccessorPointer spMutableMask(mMask1); + + // different geometricInfo + boost::shared_ptr spDummyMaskDiffGeoInfo = boost::make_shared + (geoInfo2, voxelListPtr2); + MaskAccessorPointer spMaskAccessorDiffGeoInfo(spDummyMaskDiffGeoInfo); + boost::shared_ptr mMask2 = + boost::make_shared(geoInfo2); + MutableMaskAccessorPointer spMutableMaskDiffGeoInfo(mMask2); + + // 1) test dose-dose operations + //ADD + algorithms::arithmetic::doseOp::Add addOP; + CHECK_NO_THROW(algorithms::arithmetic::arithmetic(spDoseAccessor1, spDoseAccessor2, + spMutableDoseAccessor, addOP)); + VoxelGridID id = 5; + CHECK(spMutableDoseAccessor->getDoseAt(id)>100); + CHECK_EQUAL(spDoseAccessor1->getDoseAt(id)+valFix, spMutableDoseAccessor->getDoseAt(id)); + id = 10; + CHECK(spMutableDoseAccessor->getDoseAt(id)>100); + CHECK_EQUAL(spDoseAccessor1->getDoseAt(id)+valFix, spMutableDoseAccessor->getDoseAt(id)); + //@todo add MappableDoseAccessor Tests + + + //handling exceptions is tested once for dose-dose operations, because this does not change if the operation changes. + //handling null pointers + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor1, spDoseNull, + spMutableDoseAccessor, addOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseNull, spDoseAccessor2, + spMutableDoseAccessor, addOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor1, spDoseAccessor2, + spMutableDoseNull, addOP), + core::NullPointerException); + //handle different geometricInfos + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor1, spDoseAccessorDiffGeoInfo, + spMutableDoseAccessor, + addOP),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessorDiffGeoInfo, spDoseAccessor2, + spMutableDoseAccessor, + addOP),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor1, spDoseAccessor2, + spMutableDoseAccessorDiffGeoInfo, + addOP),core::InvalidParameterException); + + //ADD_WEIGHTED + algorithms::arithmetic::doseOp::AddWeighted addWOP(1,2); + CHECK_NO_THROW(algorithms::arithmetic::arithmetic(spDoseAccessor1, spDoseAccessor2, + spMutableDoseAccessor, addWOP)); + id = 5; + CHECK(spMutableDoseAccessor->getDoseAt(id)>201); + CHECK_EQUAL(spDoseAccessor1->getDoseAt(id)+2*valFix, spMutableDoseAccessor->getDoseAt(id)); + id = 10; + CHECK(spMutableDoseAccessor->getDoseAt(id)>201); + CHECK_EQUAL(spDoseAccessor1->getDoseAt(id)+2*valFix, spMutableDoseAccessor->getDoseAt(id)); + //@todo add MappableDoseAccessor Tests + + // 2) test dose-mask operations + //MULTIPLY + algorithms::arithmetic::doseMaskOp::Multiply multOP; + CHECK_NO_THROW(algorithms::arithmetic::arithmetic(spDoseAccessor2, spMaskAccessor1, + spMutableDoseAccessor, multOP)); + + core::MaskVoxel mVoxel (0); + id = 5; + CHECK_EQUAL(0,spMutableDoseAccessor->getDoseAt(id)); + spMaskAccessor1->getMaskAt(id,mVoxel); + CHECK_EQUAL(spMutableDoseAccessor->getDoseAt(id), mVoxel.getRelevantVolumeFraction()); + id = 15; + CHECK_EQUAL(valFix,spMutableDoseAccessor->getDoseAt(id)); + id = 35; + CHECK_EQUAL(0,spMutableDoseAccessor->getDoseAt(id)); + //@todo add MappableDoseAccessor Tests + + //handling exceptions is tested once for dose-dose operations, because this does not change if the operation changes. + //handling null pointers + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor1, spMaskAccessorNull, + spMutableDoseAccessor, multOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseNull, spDoseAccessor2, + spMutableDoseAccessor, multOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor2, spMaskAccessor1, + spMutableDoseNull, multOP), + core::NullPointerException); + //handle different geometricInfos + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor2, spMaskAccessorDiffGeoInfo, + spMutableDoseAccessor, + multOP),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessorDiffGeoInfo, spMaskAccessor1, + spMutableDoseAccessor, + multOP),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spDoseAccessor2, spMaskAccessor1, + spMutableDoseAccessorDiffGeoInfo, + multOP),core::InvalidParameterException); + + // 3) test mask-mask operations + //ADD + algorithms::arithmetic::maskOp::Add maskAddOP; + CHECK_NO_THROW(algorithms::arithmetic::arithmetic(spMaskAccessor1, spMaskAccessor2, spMutableMask, + maskAddOP)); + + id = 5; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + id = 15; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(1,mVoxel.getRelevantVolumeFraction()); + id = 35; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(1,mVoxel.getRelevantVolumeFraction()); + id = 45; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + + //handling exceptions is tested once for dose-dose operations, because this does not change if the operation changes. + //handling null pointers + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spMaskAccessor1, spMaskAccessorNull, + spMutableMask, maskAddOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spMaskAccessorNull, spMaskAccessor2, + spMutableMask, maskAddOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spMaskAccessor1, spMaskAccessor1, + spMutableMaskAccessorNull, maskAddOP), + core::NullPointerException); + //handle different geometricInfos + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spMaskAccessor1, spMaskAccessorDiffGeoInfo, + spMutableMask, + maskAddOP),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spMaskAccessorDiffGeoInfo, spMaskAccessor2, + spMutableMask, + maskAddOP),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(algorithms::arithmetic::arithmetic(spMaskAccessor1, spMaskAccessor1, + spMutableMaskDiffGeoInfo, + maskAddOP),core::InvalidParameterException); + + //SUBTRACT + algorithms::arithmetic::maskOp::Subtract maskSubOP; + CHECK_NO_THROW(algorithms::arithmetic::arithmetic(spMaskAccessor1, spMaskAccessor2, spMutableMask, + maskSubOP)); + + id = 5; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + id = 15; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(1,mVoxel.getRelevantVolumeFraction()); + id = 35; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + id = 45; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + + // 4) test convenience functions + // tests are similar to explicit calls + //@todo if convenience functions are implemented: add MappableDoseAccessor Tests + CHECK_NO_THROW(algorithms::arithmetic::add(spDoseAccessor1, spDoseAccessor2, + spMutableDoseAccessor)); + id = 5; + CHECK(spMutableDoseAccessor->getDoseAt(id)>100); + CHECK_EQUAL(spDoseAccessor1->getDoseAt(id)+valFix, spMutableDoseAccessor->getDoseAt(id)); + id = 10; + CHECK(spMutableDoseAccessor->getDoseAt(id)>100); + CHECK_EQUAL(spDoseAccessor1->getDoseAt(id)+valFix, spMutableDoseAccessor->getDoseAt(id)); + + CHECK_NO_THROW(algorithms::arithmetic::add(spMaskAccessor1,spMaskAccessor2,spMutableMask)); + id = 5; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + id = 15; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(1,mVoxel.getRelevantVolumeFraction()); + id = 35; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(1,mVoxel.getRelevantVolumeFraction()); + id = 45; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + + CHECK_NO_THROW(algorithms::arithmetic::subtract(spMaskAccessor1,spMaskAccessor2,spMutableMask)); + id = 5; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + id = 15; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(1,mVoxel.getRelevantVolumeFraction()); + id = 35; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + id = 45; + spMutableMask->getMaskAt(id,mVoxel); + CHECK_EQUAL(0,mVoxel.getRelevantVolumeFraction()); + + + CHECK_NO_THROW(algorithms::arithmetic::multiply(spDoseAccessor2, spMaskAccessor1, + spMutableDoseAccessor)); + id = 5; + CHECK_EQUAL(0,spMutableDoseAccessor->getDoseAt(id)); + spMaskAccessor1->getMaskAt(id,mVoxel); + CHECK_EQUAL(spMutableDoseAccessor->getDoseAt(id), mVoxel.getRelevantVolumeFraction()); + id = 15; + CHECK_EQUAL(valFix,spMutableDoseAccessor->getDoseAt(id)); + id = 35; + CHECK_EQUAL(0,spMutableDoseAccessor->getDoseAt(id)); + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + } +} \ No newline at end of file diff --git a/testing/algorithms/BinaryFunctorDoseAccessorTest.cpp b/testing/algorithms/BinaryFunctorDoseAccessorTest.cpp new file mode 100644 index 0000000..d987a80 --- /dev/null +++ b/testing/algorithms/BinaryFunctorDoseAccessorTest.cpp @@ -0,0 +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) +*/ + +#include +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDoseAccessorInterface.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbArithmetic.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbBinaryFunctorDoseAccessor.h" + +namespace rttb +{ + namespace testing + { + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef algorithms::BinaryFunctorDoseAccessor + BinaryFunctorDoseAccessorAddType; + typedef algorithms::BinaryFunctorDoseAccessor + BinaryFunctorDoseAccessorAddWeightedType; + + /*! @brief BinaryFunctorDoseAccessorTest - tests functors of dose-dose accessors + 1) test constructor + 2) test getDoseAt + */ + + int BinaryFunctorDoseAccessorTest(int argc, char* argv[]) + { + PREPARE_DEFAULT_TEST_REPORTING; + + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + + if (argc > 1) + { + RTDOSE_FILENAME = argv[1]; + } + + if (argc > 2) + { + RTDOSE2_FILENAME = argv[2]; + } + + + DoseAccessorPointer spDoseAccessorNull; + + DoseAccessorPointer spDoseAccessor = io::dicom::DicomFileDoseAccessorGenerator( + RTDOSE_FILENAME.c_str()).generateDoseAccessor(); + DoseAccessorPointer spDoseAccessor2 = io::dicom::DicomFileDoseAccessorGenerator( + RTDOSE2_FILENAME.c_str()).generateDoseAccessor(); + + algorithms::arithmetic::doseOp::Add addOP; + + algorithms::arithmetic::doseOp::AddWeighted addWeightedOP(1.0, 10.0); + algorithms::arithmetic::doseOp::AddWeighted addWeightedTwoOP(2.0, 2.0); + + + //1) Check constructor + + CHECK_THROW_EXPLICIT(BinaryFunctorDoseAccessorAddType(spDoseAccessorNull, spDoseAccessor, addOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(BinaryFunctorDoseAccessorAddType(spDoseAccessor, spDoseAccessorNull, addOP), + core::NullPointerException); + CHECK_THROW_EXPLICIT(BinaryFunctorDoseAccessorAddType(spDoseAccessorNull, spDoseAccessorNull, + addOP), core::NullPointerException); + CHECK_THROW_EXPLICIT(BinaryFunctorDoseAccessorAddType(spDoseAccessor, spDoseAccessor2, addOP), + core::InvalidParameterException); + + + CHECK_NO_THROW(BinaryFunctorDoseAccessorAddType(spDoseAccessor, spDoseAccessor, addOP)); + CHECK_NO_THROW(BinaryFunctorDoseAccessorAddWeightedType(spDoseAccessor, spDoseAccessor, + addWeightedOP)); + + boost::shared_ptr spBinaryFunctorDoseAccessorAdd( + new BinaryFunctorDoseAccessorAddType(spDoseAccessor, spDoseAccessor, addOP)); + boost::shared_ptr spBinaryFunctorDoseAccessorAddWeighted( + new BinaryFunctorDoseAccessorAddWeightedType(spDoseAccessor, spDoseAccessor, addWeightedOP)); + boost::shared_ptr + spBinaryFunctorDoseAccessorAddWeightedTwo(new BinaryFunctorDoseAccessorAddWeightedType( + spDoseAccessor, spDoseAccessor, addWeightedTwoOP)); + + //2) Test getDoseAt() + VoxelGridID aId[3] = {5, 6067, spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumberOfVoxels() - 1}; + VoxelGridIndex3D aIndex[3] = {VoxelGridIndex3D(5, 0, 0), VoxelGridIndex3D(37, 0, 2), VoxelGridIndex3D(spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumColumns() - 1, spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumRows() - 1, spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumSlices() - 1)}; + + for (int i = 0; i < 3; ++i) + { + CHECK_EQUAL(spBinaryFunctorDoseAccessorAdd->getDoseAt(aId[i]), 4.0); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAddWeighted->getDoseAt(aId[i]), 22.0); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAdd->getDoseAt(aIndex[i]), + spBinaryFunctorDoseAccessorAdd->getDoseAt(aId[i])); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAddWeighted->getDoseAt(aIndex[i]), + spBinaryFunctorDoseAccessorAddWeighted->getDoseAt(aId[i])); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAdd->getDoseAt(aId[i]) * 2.0, + spBinaryFunctorDoseAccessorAddWeightedTwo->getDoseAt(aId[i])); + } + + VoxelGridID aIdInvalid(spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumberOfVoxels()); + VoxelGridIndex3D aIndexInvalid(spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumColumns(), + spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumRows(), + spBinaryFunctorDoseAccessorAdd->getGeometricInfo().getNumSlices()); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAdd->getDoseAt(aIdInvalid), -1.0); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAdd->getDoseAt(aIndexInvalid), -1.0); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAddWeighted->getDoseAt(aIdInvalid), -1.0); + CHECK_EQUAL(spBinaryFunctorDoseAccessorAddWeighted->getDoseAt(aIndexInvalid), -1.0); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + } +} \ No newline at end of file diff --git a/testing/algorithms/CMakeLists.txt b/testing/algorithms/CMakeLists.txt new file mode 100644 index 0000000..4a106e5 --- /dev/null +++ b/testing/algorithms/CMakeLists.txt @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(ALGORITHMS_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbAlgorithmsTests) +SET(ALGORITHMS_HEADER_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbAlgorithmsHeaderTest) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(DoseStatisticsTest ${ALGORITHMS_TESTS} DoseStatisticsTest) +ADD_TEST(ArithmeticTest ${ALGORITHMS_TESTS} ArithmeticTest) +ADD_TEST(BinaryFunctorDoseAccessorTest ${ALGORITHMS_TESTS} BinaryFunctorDoseAccessorTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/dicompylerTestDose.dcm") + +RTTB_CREATE_TEST_MODULE(rttbAlgorithms DEPENDS RTTBAlgorithms RTTBMasks RTTBDicomIO PACKAGE_DEPENDS Boost Litmus DCMTK) + diff --git a/testing/algorithms/DoseStatisticsTest.cpp b/testing/algorithms/DoseStatisticsTest.cpp new file mode 100644 index 0000000..5c6a803 --- /dev/null +++ b/testing/algorithms/DoseStatisticsTest.cpp @@ -0,0 +1,217 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDoseStatistics.h" +#include "rttbInvalidDoseException.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; + + /*! @brief DoseStatisticsTest - test the API of DoseStatistics + 1) test constructors + 2) test setDoseIterator + 3) test getNumberOfVoxels + 4) get statistical values + floating point accuracy requires this otherwise the test 4 fails! + */ + + int DoseStatisticsTest(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_NO_THROW(rttb::algorithms::DoseStatistics myEmptyDoseStat); + rttb::algorithms::DoseStatistics myEmptyDoseStat; + rttb::algorithms::DoseStatistics myDoseStatToBeFilled; + + CHECK_THROW_EXPLICIT(rttb::algorithms::DoseStatistics myDoseStats(spDoseIteratorNull),core::NullPointerException); + + CHECK_NO_THROW(rttb::algorithms::DoseStatistics myDoseStats(spDoseIterator)); + rttb::algorithms::DoseStatistics myDoseStats(spDoseIterator); + + //2) test setDoseIterator + CHECK_THROW_EXPLICIT(myDoseStatToBeFilled.setDoseIterator(spDoseIteratorNull),core::NullPointerException); + CHECK_NO_THROW(myDoseStatToBeFilled.setDoseIterator(spDoseIterator)); + + //3) test getNumberOfVoxels + CHECK_THROW_EXPLICIT(myEmptyDoseStat.getNumberOfVoxels(),core::InvalidDoseException); + CHECK_EQUAL(myDoseStats.getNumberOfVoxels(),doseVals->size()); + CHECK_EQUAL(myDoseStatToBeFilled.getNumberOfVoxels(),doseVals->size()); + + //4) get statistical values + CHECK_THROW_EXPLICIT(myEmptyDoseStat.getNumberOfVoxels(),core::InvalidDoseException); + CHECK_EQUAL(myDoseStats.getNumberOfVoxels(),doseVals->size()); + CHECK_EQUAL(myDoseStatToBeFilled.getNumberOfVoxels(),doseVals->size()); + + //compute values for comparison + DoseStatisticType maximum = 0; + DoseStatisticType minimum = 1000000; + DoseStatisticType mean = 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(); + float compMean = (int(mean*100))/100; + + boost::shared_ptr< std::vector > > myResultPairs = + boost::make_shared< std::vector > >(); + ResultListPointer spMyResultPairs(myResultPairs); + boost::shared_ptr< std::vector > > myResultPairs2 = + boost::make_shared< std::vector > >(); + ResultListPointer spMyResultPairs2(myResultPairs2); + + CHECK_THROW_EXPLICIT(myEmptyDoseStat.getMaximum(spMyResultPairs),core::InvalidDoseException); + CHECK_EQUAL(myDoseStats.getMaximum(spMyResultPairs),maximum); + CHECK_EQUAL(myDoseStatToBeFilled.getMaximum(spMyResultPairs2),maximum); + CHECK(spMyResultPairs->size()>0); + + CHECK_EQUAL(spMyResultPairs->size(),spMyResultPairs2->size()); + std::vector >::iterator pairIt = spMyResultPairs->begin(); + std::vector >::iterator pairIt2 = spMyResultPairs2->begin(); + + for (;pairIt != spMyResultPairs->end();++pairIt) + { + CHECK_EQUAL(pairIt->first,pairIt2->first); + CHECK_EQUAL(pairIt->second,pairIt2->second); + ++pairIt2; + } + + spMyResultPairs = boost::make_shared< std::vector > >(); + spMyResultPairs2 = boost::make_shared< std::vector > >(); + CHECK_THROW_EXPLICIT(myEmptyDoseStat.getMinimum(spMyResultPairs),core::InvalidDoseException); + CHECK_EQUAL(myDoseStats.getMinimum(spMyResultPairs),minimum); + CHECK_EQUAL(myDoseStatToBeFilled.getMinimum(spMyResultPairs2),minimum); + CHECK(spMyResultPairs->size()>0); + + CHECK_EQUAL(spMyResultPairs->size(),spMyResultPairs2->size()); + pairIt = spMyResultPairs->begin(); + pairIt2 = spMyResultPairs2->begin(); + + for (;pairIt != spMyResultPairs->end();++pairIt) + { + CHECK_EQUAL(pairIt->first,pairIt2->first); + CHECK_EQUAL(pairIt->second,pairIt2->second); + ++pairIt2; + } + + CHECK_THROW_EXPLICIT(myEmptyDoseStat.getMean(),core::InvalidDoseException); + float tmpMean = myDoseStats.getMean(); + tmpMean = (int(tmpMean*100))/100; + CHECK_EQUAL(tmpMean,compMean); + tmpMean = myDoseStatToBeFilled.getMean(); + tmpMean = (int(tmpMean*100))/100; + CHECK_EQUAL(tmpMean,compMean); + + //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::DoseStatistics myDoseStats2(spDoseIterator2); + + spMyResultPairs = boost::make_shared< std::vector > >(); + CHECK_EQUAL(myDoseStats2.getMaximum(spMyResultPairs),maximum); + CHECK(spMyResultPairs->size()>0); + + CHECK_EQUAL(spMyResultPairs->size(),sizeTemplate); + pairIt = spMyResultPairs->begin(); + + for (;pairIt != spMyResultPairs->end();++pairIt) + { + CHECK_EQUAL(pairIt->first,maximum); + } + + spMyResultPairs = boost::make_shared< std::vector > >(); + int maxmialFound = 200; + CHECK_EQUAL(myDoseStats2.getMinimum(spMyResultPairs, maxmialFound),minimum); + CHECK(spMyResultPairs->size()>0); + + CHECK_EQUAL(spMyResultPairs->size(),maxmialFound); + pairIt = spMyResultPairs->begin(); + + for (;pairIt != spMyResultPairs->end();++pairIt) + { + CHECK_EQUAL(pairIt->first,minimum); + } + + CHECK_EQUAL(myDoseStats2.getMean(),mean); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing + }//end namespace rttb diff --git a/testing/algorithms/files.cmake b/testing/algorithms/files.cmake new file mode 100644 index 0000000..31ddd28 --- /dev/null +++ b/testing/algorithms/files.cmake @@ -0,0 +1,19 @@ +SET(CPP_FILES + DoseStatisticsTest.cpp + ArithmeticTest.cpp + BinaryFunctorDoseAccessorTest.cpp + rttbAlgorithmsTests.cpp + + #include dummy accessor files + ../core/DummyDoseAccessor.cpp + ../core/DummyMaskAccessor.cpp + ../core/DummyMutableDoseAccessor.cpp + + ) + +SET(H_FILES + #include dummy accessor files + ../core/DummyDoseAccessor.h + ../core/DummyMaskAccessor.h + ../core/DummyMutableDoseAccessor.h +) diff --git a/testing/algorithms/rttbAlgorithmsTests.cpp b/testing/algorithms/rttbAlgorithmsTests.cpp new file mode 100644 index 0000000..e03c3e9 --- /dev/null +++ b/testing/algorithms/rttbAlgorithmsTests.cpp @@ -0,0 +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) +*/ + +// 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(DoseStatisticsTest); + LIT_REGISTER_TEST(ArithmeticTest); + LIT_REGISTER_TEST(BinaryFunctorDoseAccessorTest); + } + } +} + +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/core/CMakeLists.txt b/testing/core/CMakeLists.txt new file mode 100644 index 0000000..2e617db --- /dev/null +++ b/testing/core/CMakeLists.txt @@ -0,0 +1,28 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(CORE_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbCoreTests) +SET(CORE_HEADER_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbCoreHeaderTest) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(GeometricInfoTest ${CORE_TESTS} GeometricInfoTest) +ADD_TEST(MaskVoxelTest ${CORE_TESTS} MaskVoxelTest) +ADD_TEST(GenericDoseIteratorTest ${CORE_TESTS} GenericDoseIteratorTest) +ADD_TEST(GenericMaskedDoseIteratorTest ${CORE_TESTS} GenericMaskedDoseIteratorTest) +ADD_TEST(DVHCalculatorTest ${CORE_TESTS} DVHCalculatorTest) +ADD_TEST(DVHTest ${CORE_TESTS} DVHTest) +ADD_TEST(DVHSetTest ${CORE_TESTS} DVHSetTest) +ADD_TEST(StructureTest ${CORE_TESTS} StructureTest) +ADD_TEST(StrVectorStructureSetGeneratorTest ${CORE_TESTS} StrVectorStructureSetGeneratorTest) + + +RTTB_CREATE_TEST_MODULE(rttbCore DEPENDS RTTBCore PACKAGE_DEPENDS Boost Litmus) + diff --git a/testing/core/CreateTestStructure.cpp b/testing/core/CreateTestStructure.cpp new file mode 100644 index 0000000..c5b7f08 --- /dev/null +++ b/testing/core/CreateTestStructure.cpp @@ -0,0 +1,614 @@ +// ----------------------------------------------------------------------- +// 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 "CreateTestStructure.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbInvalidDoseException.h" + +namespace rttb{ + + namespace testing{ + + CreateTestStructure::~CreateTestStructure(){} + + CreateTestStructure::CreateTestStructure(const core::GeometricInfo& aGeoInfo){ + _geoInfo = aGeoInfo; + } + + PolygonType CreateTestStructure::createPolygonLeftUpper(std::vector aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + for(int i=0;i aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + + if( aVoxelVector.size() > 0 ) + { + int i=0; + + VoxelGridIndex3D voxelIndex; + + voxelIndex(0)=(aVoxelVector.at(i)).x(); + voxelIndex(1)=(aVoxelVector.at(i)).y(); + voxelIndex(2)=sliceNumber; + + WorldCoordinate3D p1; + _geoInfo.indexToWorldCoordinate(voxelIndex,p1); + SpacingVectorType3D delta=_geoInfo.getSpacing(); + + WorldCoordinate3D wp1; + wp1(0)=p1.x(); + wp1(1)=p1.y(); + wp1(2)=p1.z(); + + WorldCoordinate3D wp_intermediate; + wp_intermediate(0)= 0; + wp_intermediate(1)= 0; + wp_intermediate(2)= p1.z(); + + double radius = 2 * delta.x(); + double frac_radius = ( radius * 0.001 ); + + double correct_y = ( delta.x() / delta.y() ); + + for( int i = 0 ; i <= 1000 ; i++ ){ + double y_offset = sqrt( ( radius * radius ) - ( (frac_radius * i) * (frac_radius * i) ) ); + + wp_intermediate(0)= wp1.x() + frac_radius * i; + wp_intermediate(1)= wp1.y() - ( y_offset * correct_y ) ; + + polygon.push_back( wp_intermediate ); + } + + for( int i = 1000 ; i >= 0 ; i-- ){ + + double y_offset = sqrt( ( radius * radius ) - ( (frac_radius * i) * (frac_radius * i) ) ) ; + + wp_intermediate(0)= wp1.x() + frac_radius * i; + wp_intermediate(1)= wp1.y() + ( y_offset * correct_y ); + + polygon.push_back( wp_intermediate ); + } + + for( int i = 0 ; i <= 1000 ; i++ ){ + double y_offset = sqrt( ( radius * radius ) - ( (frac_radius * i) * (frac_radius * i) ) ); + + wp_intermediate(0)= wp1.x() - frac_radius * i; + wp_intermediate(1)= wp1.y() + y_offset * correct_y ; + + polygon.push_back( wp_intermediate ); + } + + for( int i = 1000 ; i >= 0 ; i-- ){ + double y_offset = sqrt( ( radius * radius ) - ( (frac_radius * i) * (frac_radius * i) ) ); + + wp_intermediate(0)= wp1.x() - frac_radius * i ; + wp_intermediate(1)= wp1.y() - ( y_offset * correct_y ); + + polygon.push_back( wp_intermediate ); + } + } + std::cout << std::endl; + return polygon; + } + + PolygonType CreateTestStructure::createStructureSeveralSectionsInsideOneVoxelA(std::vector aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + + if( aVoxelVector.size() > 0 ) + { + int i=0; + + VoxelGridIndex3D voxelIndex; + + voxelIndex(0)=(aVoxelVector.at(i)).x(); + voxelIndex(1)=(aVoxelVector.at(i)).y(); + voxelIndex(2)=sliceNumber; + + WorldCoordinate3D p1; + _geoInfo.indexToWorldCoordinate(voxelIndex,p1); + SpacingVectorType3D delta=_geoInfo.getSpacing(); + + WorldCoordinate3D wp1; + wp1(0)=p1.x(); + wp1(1)=p1.y(); + wp1(2)=p1.z(); + + WorldCoordinate3D wp_intermediate; + wp_intermediate(0)= 0; + wp_intermediate(1)= 0; + wp_intermediate(2)= p1.z(); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.25 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.25 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 2.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 2.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.75 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.75 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 2.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.0 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 2.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.0 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.25 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.25 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 2.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 2.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.75 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.75 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 3.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() ; + wp_intermediate(1)= wp1.y() + ( delta.y() * 3.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() ; + wp_intermediate(1)= wp1.y() + ( delta.y() * 3.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() ; + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.75 ); + + polygon.push_back( wp_intermediate ); + } + std::cout << std::endl; + return polygon; + } + + PolygonType CreateTestStructure::createStructureSelfTouchingA(std::vector aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + + if( aVoxelVector.size() > 0 ) + { + int i=0; + + VoxelGridIndex3D voxelIndex; + + voxelIndex(0)=(aVoxelVector.at(i)).x(); + voxelIndex(1)=(aVoxelVector.at(i)).y(); + voxelIndex(2)=sliceNumber; + + WorldCoordinate3D p1; + _geoInfo.indexToWorldCoordinate(voxelIndex,p1); + SpacingVectorType3D delta=_geoInfo.getSpacing(); + + WorldCoordinate3D wp1; + wp1(0)=p1.x(); + wp1(1)=p1.y(); + wp1(2)=p1.z(); + + + WorldCoordinate3D wp_intermediate; + wp_intermediate(0)= 0; + wp_intermediate(1)= 0; + wp_intermediate(2)= p1.z(); + + wp_intermediate(0)= wp1.x(); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y(); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.0 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.0 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 1.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 1.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 1.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() ; + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + } + std::cout << std::endl; + return polygon; + } + + PolygonType CreateTestStructure::createStructureSelfTouchingB(std::vector aVoxelVector, GridIndexType sliceNumber){ + + PolygonType polygon; + + if( aVoxelVector.size() > 0 ) + { + int i=0; + + VoxelGridIndex3D voxelIndex; + + voxelIndex(0)=(aVoxelVector.at(i)).x(); + voxelIndex(1)=(aVoxelVector.at(i)).y(); + voxelIndex(2)=sliceNumber; + + WorldCoordinate3D p1; + _geoInfo.indexToWorldCoordinate(voxelIndex,p1); + SpacingVectorType3D delta=_geoInfo.getSpacing(); + + WorldCoordinate3D wp1; + wp1(0)=p1.x(); + wp1(1)=p1.y(); + wp1(2)=p1.z(); + + WorldCoordinate3D wp_intermediate; + wp_intermediate(0)= 0; + wp_intermediate(1)= 0; + wp_intermediate(2)= p1.z(); + + wp_intermediate(0)= wp1.x(); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y(); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.0 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 1.0 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 1.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 1.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x(); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() + ( delta.x() * 0.5 ); + wp_intermediate(1)= wp1.y() + ( delta.y() * 1.0 ); + + polygon.push_back( wp_intermediate ); + + wp_intermediate(0)= wp1.x() ; + wp_intermediate(1)= wp1.y() + ( delta.y() * 0.5 ); + + polygon.push_back( wp_intermediate ); + } + std::cout << std::endl; + return polygon; + } + + }//testing + }//rttb + diff --git a/testing/core/CreateTestStructure.h b/testing/core/CreateTestStructure.h new file mode 100644 index 0000000..01daec3 --- /dev/null +++ b/testing/core/CreateTestStructure.h @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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 "rttbStructure.h" +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" + +namespace rttb{ + namespace testing{ + /*!@class CreateTestStructure + @brief Create dummy structures for testing. + */ + class CreateTestStructure{ + + private: + core::GeometricInfo _geoInfo; + CreateTestStructure(){}; + + public: + ~CreateTestStructure(); + + CreateTestStructure(const core::GeometricInfo& aGeoInfo); + + PolygonType createPolygonLeftUpper(std::vector aVoxelVector, GridIndexType sliceNumber); + PolygonType createPolygonCenter(std::vector aVoxelVector, GridIndexType sliceNumber); + PolygonType createPolygonBetweenUpperLeftAndCenter(std::vector aVoxelVector, GridIndexType sliceNumber); + PolygonType createPolygonBetweenLowerRightAndCenter(std::vector aVoxelVector, GridIndexType sliceNumber); + PolygonType createPolygonIntermediatePoints(std::vector aVoxelVector, GridIndexType sliceNumber); + PolygonType createPolygonUpperCenter(std::vector aVoxelVector, GridIndexType sliceNumber); + PolygonType createPolygonLeftEdgeMiddle(std::vector aVoxelVector, GridIndexType sliceNumber); + + PolygonType createPolygonCircle(std::vector aVoxelVector, GridIndexType sliceNumber); + + PolygonType createStructureSeveralSectionsInsideOneVoxelA(std::vector aVoxelVector, GridIndexType sliceNumber); + + PolygonType createStructureSelfTouchingA(std::vector aVoxelVector, GridIndexType sliceNumber); + + PolygonType createStructureSelfTouchingB(std::vector aVoxelVector, GridIndexType sliceNumber); + }; + }//testing + }//rttb + diff --git a/testing/core/DVHCalculatorTest.cpp b/testing/core/DVHCalculatorTest.cpp new file mode 100644 index 0000000..d60ed41 --- /dev/null +++ b/testing/core/DVHCalculatorTest.cpp @@ -0,0 +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) +*/ + +// 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->getDoseUID(); + + 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++; + } + 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/DVHSetTest.cpp b/testing/core/DVHSetTest.cpp new file mode 100644 index 0000000..beda9de --- /dev/null +++ b/testing/core/DVHSetTest.cpp @@ -0,0 +1,168 @@ +// ----------------------------------------------------------------------- +// 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 "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbDVHSet.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" + +#include "DummyDVHGenerator.h" + +namespace rttb{ + namespace testing{ + + typedef core::DVHSet::DVHSetType DVHSetType; + + /*! @brief DVHTest - test the API of DVH + 1) test constructors (values as expected?) + 2) test size + 3) test set/getIDs + 4) test insert/retrieve individual DVHs + 5) test getDVHSet + 6) test getVolume + */ + int DVHSetTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + const IDType structureSetID = "myStructureSet"; + const IDType structureIDPrefix = "myStructure"; + const IDType doseID = "myDose"; + const IDType voxelizationID = "myVoxelization"; + + DummyDVHGenerator dvhGenerator; + DVHSetType tvSet; + IDType structureID = structureIDPrefix+"_TV_"; + for(int i = 0; i < 3; i++){ + tvSet.push_back(dvhGenerator.generateDVH(structureID+ boost::lexical_cast(i),doseID)); + } + DVHSetType htSet; + structureID = structureIDPrefix+"_HT_"; + for(int i = 0; i < 5; i++){ + htSet.push_back(dvhGenerator.generateDVH(structureID+ boost::lexical_cast(i),doseID)); + } + DVHSetType wvSet; + structureID = structureIDPrefix+"_WV_"; + for(int i = 0; i < 1; i++){ + wvSet.push_back(dvhGenerator.generateDVH(structureID+ boost::lexical_cast(i),doseID)); + } + + //1) test constructors (values as expected?) + CHECK_NO_THROW(core::DVHSet(structureSetID, doseID)); + CHECK_NO_THROW(core::DVHSet(tvSet, htSet, structureSetID, doseID)); + CHECK_NO_THROW(core::DVHSet(tvSet, htSet, wvSet, structureSetID, doseID)); + + //2) test size + core::DVHSet myDvhSet1(structureSetID, doseID); + CHECK_EQUAL(myDvhSet1.size(),0); + core::DVHSet myDvhSet2(tvSet, htSet, structureSetID, doseID); + CHECK_EQUAL(myDvhSet2.size(),tvSet.size()+htSet.size()); + core::DVHSet myDvhSet3(tvSet, htSet, wvSet, structureSetID, doseID); + CHECK_EQUAL(myDvhSet3.size(),tvSet.size()+htSet.size()+wvSet.size()); + + //3) test set/getIDs + const IDType newStructureSetID = "myNewStructureSet"; + const IDType newDoseID = "myNewDose"; + CHECK_EQUAL(myDvhSet1.getStrSetID(),structureSetID); + CHECK_EQUAL(myDvhSet1.getDoseID(),doseID); + CHECK_NO_THROW(myDvhSet1.setStrSetID(newStructureSetID)); + CHECK_NO_THROW(myDvhSet1.setDoseID(newDoseID)); + CHECK_EQUAL(myDvhSet1.getStrSetID(),newStructureSetID); + CHECK_EQUAL(myDvhSet1.getDoseID(),newDoseID); + + CHECK_EQUAL(myDvhSet3.getStrSetID(),structureSetID); + CHECK_EQUAL(myDvhSet3.getDoseID(),doseID); + CHECK_NO_THROW(myDvhSet3.setStrSetID(newStructureSetID)); + CHECK_NO_THROW(myDvhSet3.setDoseID(newDoseID)); + CHECK_EQUAL(myDvhSet3.getStrSetID(),newStructureSetID); + CHECK_EQUAL(myDvhSet3.getDoseID(),newDoseID); + + //4) test insert/retrieve individual DVHs + DVHRole roleTV = {DVHRole::TargetVolume}; + structureID = structureIDPrefix+"_TV_"; + core::DVH tv = dvhGenerator.generateDVH(structureID+ boost::lexical_cast(tvSet.size()),doseID); + CHECK_EQUAL(myDvhSet1.size(),0); + CHECK_NO_THROW(myDvhSet1.insert(tv, roleTV)); + CHECK_EQUAL(myDvhSet1.size(),1); + std::size_t currentSize = myDvhSet2.size(); + CHECK_NO_THROW(myDvhSet2.insert(tv, roleTV)); + CHECK_EQUAL(myDvhSet2.size(),currentSize+1); + + DVHRole roleHT = {DVHRole::HealthyTissue}; + structureID = structureIDPrefix+"_HT_"; + core::DVH ht = dvhGenerator.generateDVH(structureID+ boost::lexical_cast(htSet.size()),doseID); + CHECK_EQUAL(myDvhSet1.size(),1); + CHECK_NO_THROW(myDvhSet1.insert(ht, roleHT)); + CHECK_EQUAL(myDvhSet1.size(),2); + currentSize = myDvhSet2.size(); + CHECK_NO_THROW(myDvhSet2.insert(ht, roleHT)); + CHECK_EQUAL(myDvhSet2.size(),currentSize+1); + + DVHRole roleWV = {DVHRole::WholeVolume}; + structureID = structureIDPrefix+"_wv_"; + IDType testID = structureID+ boost::lexical_cast(wvSet.size()); + core::DVH wv = dvhGenerator.generateDVH(structureID+ boost::lexical_cast(wvSet.size()),doseID); + CHECK_EQUAL(myDvhSet1.size(),2); + CHECK_NO_THROW(myDvhSet1.insert(wv, roleWV)); + CHECK_EQUAL(myDvhSet1.size(),3); + currentSize = myDvhSet2.size(); + CHECK_NO_THROW(myDvhSet2.insert(wv, roleWV)); + CHECK_EQUAL(myDvhSet2.size(),currentSize+1); + + //5) test getDVHSet + core::DVH* dvhPtr = myDvhSet1.getDVH(testID); + CHECK_EQUAL(*dvhPtr,wv); + + dvhPtr = myDvhSet3.getDVH(structureIDPrefix+"_TV_0"); + CHECK_EQUAL(*dvhPtr,tvSet.at(0)); + + dvhPtr = myDvhSet3.getDVH(structureIDPrefix+"_TV_2"); + CHECK_EQUAL(*dvhPtr,tvSet.at(2)); + + DVHSetType tvTest = myDvhSet3.getDVHTVSet(); + CHECK_EQUAL(tvTest,tvSet); + + DVHSetType htTest = myDvhSet3.getDVHHTSet(); + CHECK_EQUAL(htTest,htSet); + + DVHSetType wvTest = myDvhSet3.getDVHWVSet(); + CHECK_EQUAL(wvTest,wvSet); + + //6) test getVolume + DoseTypeGy aDoseAbsolute = 10; + CHECK_EQUAL(0,myDvhSet3.getHTVolume(aDoseAbsolute)); + CHECK_EQUAL(0,myDvhSet3.getTVVolume(aDoseAbsolute)); + CHECK_EQUAL(0,myDvhSet3.getWholeVolume(aDoseAbsolute)); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing + }//end namespace rttb diff --git a/testing/core/DVHTest.cpp b/testing/core/DVHTest.cpp new file mode 100644 index 0000000..0ed832f --- /dev/null +++ b/testing/core/DVHTest.cpp @@ -0,0 +1,179 @@ +// ----------------------------------------------------------------------- +// 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++) + { + 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); + /*! @todo Should you be able to set the DoseID?*/ + myDVH.setDoseID("somethingElse"); + CHECK_EQUAL(myDVH.getDoseID(), "somethingElse"); + CHECK_EQUAL(myDVH.getVoxelizationID(), voxelizationID); + CHECK_EQUAL(myDVH.getStructureID(), structureID); + /*! @todo Should you be able to set the DoseID?*/ + 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/DummyDVHGenerator.cpp b/testing/core/DummyDVHGenerator.cpp new file mode 100644 index 0000000..7abd3c4 --- /dev/null +++ b/testing/core/DummyDVHGenerator.cpp @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// 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 "DummyDVHGenerator.h" +namespace rttb{ + namespace testing{ + + DummyDVHGenerator::DummyDVHGenerator(): _binSize(DoseTypeGy(0.1)), _voxelVolume(8), _value(0) + { + /* initialize random seed: */ + srand (time(NULL)); + }; + + core::DVH DummyDVHGenerator::generateDVH(IDType structureID,IDType doseID){ + core::DVH::DataDifferentialType aDataDifferential; + for(int i = 0; i < 100; i++){ + _value = DoseCalcType((double(rand())/RAND_MAX)*1000); + //cut off values to avoid problems on comparisson with reimported values after + //writing to file. + _value = floor(_value *1000000)/1000000; + aDataDifferential.push_back( _value ); + } + return core::DVH(aDataDifferential, _binSize, _voxelVolume, structureID, doseID); + } + + core::DVH DummyDVHGenerator::generateDVH(IDType structureID,IDType doseID, DoseCalcType value){ + _value = value; + + core::DVH::DataDifferentialType aDataDifferential; + for(int i = 0; i < 100; i++){ + aDataDifferential.push_back( _value ); + } + return core::DVH(aDataDifferential, _binSize, _voxelVolume, structureID, doseID); + } + + core::DVH DummyDVHGenerator::generateDVH(IDType structureID,IDType doseID, DoseCalcType minValue, DoseCalcType maxValue){ + _voxelVolume = 0.2*0.2*0.4; + bool decrease = false; + + core::DVH::DataDifferentialType aDataDifferential; + for(int i = 0; i <= 200; i++){ + if ((i > 20) && (i < 180)){ + if ((_value == 0) && (!decrease)) { + _value = DoseCalcType((double(rand())/RAND_MAX)*10)+minValue; + } + else if (!decrease){ + _value = _value + DoseCalcType((double(rand())/RAND_MAX)*(maxValue/10)); + } + if ( (_value > maxValue) || (decrease) ){ + decrease = true; + _value = _value - DoseCalcType((double(rand())/RAND_MAX)*(maxValue/3)); + } + if (_value < 0) { + _value = 0; + } + } + else{ + _value = 0; + } + aDataDifferential.push_back( _value ); + } + return core::DVH(aDataDifferential, _binSize, _voxelVolume, structureID, doseID); + } + } +} \ No newline at end of file diff --git a/testing/core/DummyDVHGenerator.h b/testing/core/DummyDVHGenerator.h new file mode 100644 index 0000000..f95ba43 --- /dev/null +++ b/testing/core/DummyDVHGenerator.h @@ -0,0 +1,68 @@ +// ----------------------------------------------------------------------- +// 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 __DUMMY_DVH_GENERATOR_H +#define __DUMMY_DVH_GENERATOR_H + +#include + +#include "rttbDVH.h" +#include "rttbBaseType.h" + + +namespace rttb{ + + namespace testing{ + + /*! @class DummyDVHGenerator + @brief generate DVHs for testing based on a randomly generated dose data vector. + */ + class DummyDVHGenerator + { + private: + DoseTypeGy _binSize; + DoseVoxelVolumeType _voxelVolume; + DoseCalcType _value; + + public: + ~DummyDVHGenerator(){}; + + DummyDVHGenerator(); + + /*! generate a dummy DVH with bin size = 0.1, voxel volume = 8 and 100 entries. + The values of the 100 bins are randomly generated [0,1000]. + */ + core::DVH generateDVH(IDType structureID, IDType doseID); + + /*! generate a dummy DVH with bin size = 0.1, voxel volume = 8 and 100 entries. + The values of the 100 bins all contain the given value. + */ + core::DVH generateDVH(IDType structureID, IDType doseID, DoseCalcType value); + + /*! generate a dummy DVH with bin size = 0.1, voxel volume = 0.016 and 200 entries. + The values of the 200 bins are random values inside the interval defined by minValue and maxValue. + */ + core::DVH generateDVH(IDType structureID,IDType doseID, DoseCalcType minValue, DoseCalcType maxValue); + + }; + } + } + +#endif diff --git a/testing/core/DummyDoseAccessor.cpp b/testing/core/DummyDoseAccessor.cpp new file mode 100644 index 0000000..b74adba --- /dev/null +++ b/testing/core/DummyDoseAccessor.cpp @@ -0,0 +1,96 @@ +// ----------------------------------------------------------------------- +// 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 "DummyDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbIndexOutOfBoundsException.h" + +namespace rttb +{ + namespace testing + { + + DummyDoseAccessor::~DummyDoseAccessor() {} + + DummyDoseAccessor::DummyDoseAccessor() + { + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _doseUID = "DummyDoseAccessor_" + ss.str(); + + SpacingVectorType3D aVector(2.5); + _geoInfo.setSpacing(aVector); + WorldCoordinate3D anOtherVector(-25, -2, 35); + _geoInfo.setImagePositionPatient(anOtherVector); + _geoInfo.setNumRows(11); + _geoInfo.setNumColumns(10); + _geoInfo.setNumSlices(5); + + OrientationMatrix unit = OrientationMatrix(); + _geoInfo.setOrientationMatrix(unit); + + for (int i = 0; i < _geoInfo.getNumberOfVoxels(); i++) + { + doseData.push_back((double(rand()) / RAND_MAX) * 1000); + } + } + + DummyDoseAccessor::DummyDoseAccessor(const std::vector& aDoseVector, + const core::GeometricInfo& geoInfo) + { + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _doseUID = "DummyDoseAccessor_" + ss.str(); + + doseData = aDoseVector; + _geoInfo = geoInfo; + } + + DoseTypeGy DummyDoseAccessor::getDoseAt(const VoxelGridID aID) const + { + if (!_geoInfo.validID(aID)) + { + throw core::IndexOutOfBoundsException("Not a valid Position!"); + } + + return doseData.at(aID); + } + + DoseTypeGy DummyDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + VoxelGridID gridID = 0; + _geoInfo.convert(aIndex, gridID); + return getDoseAt(gridID); + } + + }//end namespace testing +}//end namespace rttb \ No newline at end of file diff --git a/testing/core/DummyDoseAccessor.h b/testing/core/DummyDoseAccessor.h new file mode 100644 index 0000000..e1eaed0 --- /dev/null +++ b/testing/core/DummyDoseAccessor.h @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// 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 __DUMMY_DOSE_ACCESSOR_H +#define __DUMMY_DOSE_ACCESSOR_H + +#include + +#include "rttbDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + + +namespace rttb +{ + namespace testing + { + + /*! @class DummyDoseAccessor + @brief A dummy DoseAccessor for testing filled with random dose values between 0 and 100. + The default grid size is [11,10,5] + */ + class DummyDoseAccessor: public core::DoseAccessorInterface + { + + private: + /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ + std::vector doseData; + + IDType _doseUID; + + + public: + ~DummyDoseAccessor(); + + /*! @brief A dummy DoseAccessor for testing filled with random dose values between 0 and 100. + The default grid size is [11,10,5] + */ + DummyDoseAccessor(); + + /*! @brief Constructor. + Initialisation of dose with a given vector. + */ + DummyDoseAccessor(const std::vector& aDoseVector, const core::GeometricInfo& geoInfo); + + const std::vector* getDoseVector() const + { + return &doseData; + }; + + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + const IDType getDoseUID() const + { + return _doseUID; + }; + }; + } +} + +#endif diff --git a/testing/core/DummyMaskAccessor.cpp b/testing/core/DummyMaskAccessor.cpp new file mode 100644 index 0000000..623ee1f --- /dev/null +++ b/testing/core/DummyMaskAccessor.cpp @@ -0,0 +1,162 @@ +// ----------------------------------------------------------------------- +// 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 "DummyMaskAccessor.h" +#include "rttbNullPointerException.h" + +namespace rttb +{ + namespace testing + { + + DummyMaskAccessor::DummyMaskAccessor(const core::GeometricInfo& aGeometricInfo) + { + _spRelevantVoxelVector = MaskVoxelListPointer(); + _geoInfo = aGeometricInfo; + + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _maskUID = "DummyMask_" + ss.str(); + } + + DummyMaskAccessor::DummyMaskAccessor(const core::GeometricInfo& aGeometricInfo, + MaskVoxelListPointer voxelListPtr) + { + _spRelevantVoxelVector = voxelListPtr; + _geoInfo = aGeometricInfo; + + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _maskUID = "DummyMask_" + ss.str(); + } + + void DummyMaskAccessor::updateMask() + { + MaskVoxelList newRelevantVoxelVector; + + if (_spRelevantVoxelVector) + { + return; + } + + for (int i = 0; i < _geoInfo.getNumberOfVoxels(); i++) + { + + if ((double(rand()) / RAND_MAX) > 0.5) + { + core::MaskVoxel newMaskVoxel(i); + newMaskVoxel.setRelevantVolumeFraction((double(rand()) / RAND_MAX)); + + newRelevantVoxelVector.push_back(newMaskVoxel); + } + } + + _spRelevantVoxelVector = boost::make_shared(newRelevantVoxelVector); + return; + } + + DummyMaskAccessor::MaskVoxelListPointer DummyMaskAccessor::getRelevantVoxelVector() + { + updateMask(); + return _spRelevantVoxelVector; + } + + DummyMaskAccessor::MaskVoxelListPointer DummyMaskAccessor::getRelevantVoxelVector( + float lowerThreshold) + { + MaskVoxelListPointer filteredVoxelVectorPointer(new MaskVoxelList); + updateMask(); + DummyMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); + + while (it != _spRelevantVoxelVector->end()) + { + if ((*it).getRelevantVolumeFraction() > lowerThreshold) + { + filteredVoxelVectorPointer->push_back(*it); + } + + ++it; + } + + return filteredVoxelVectorPointer; + } + + bool DummyMaskAccessor::getMaskAt(VoxelGridID aID, core::MaskVoxel& voxel) const + { + voxel.setRelevantVolumeFraction(0); + + if (!_geoInfo.validID(aID)) + { + return false; + } + + if (_spRelevantVoxelVector) + { + DummyMaskAccessor::MaskVoxelList::iterator it = _spRelevantVoxelVector->begin(); + + while (it != _spRelevantVoxelVector->end()) + { + if ((*it).getVoxelGridID() == aID) + { + voxel = (*it); + return true; + } + + ++it; + } + } + else + { + return false; + } + + return false; + + } + + bool DummyMaskAccessor::getMaskAt(const VoxelGridIndex3D& aIndex, core::MaskVoxel& voxel) const + { + VoxelGridID aVoxelGridID; + + if (_geoInfo.convert(aIndex, aVoxelGridID)) + { + return getMaskAt(aVoxelGridID, voxel); + } + else + { + return false; + } + } + + } +} \ No newline at end of file diff --git a/testing/core/DummyMaskAccessor.h b/testing/core/DummyMaskAccessor.h new file mode 100644 index 0000000..44bb02e --- /dev/null +++ b/testing/core/DummyMaskAccessor.h @@ -0,0 +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) +*/ +#ifndef __DUMMY_MASK_ACCESSOR_H +#define __DUMMY_MASK_ACCESSOR_H + +#include + +#include "rttbMaskAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + + +namespace rttb +{ + namespace testing + { + + /*! @class DumyMaskAccessor + @brief Create a dummy MaskAccessor for testing. + */ + class DummyMaskAccessor: public core::MaskAccessorInterface + { + public: + typedef core::MaskAccessorInterface::MaskVoxelList MaskVoxelList; + typedef core::MaskAccessorInterface::MaskVoxelListPointer MaskVoxelListPointer; + + private: + /*! vector containing list of mask voxels*/ + MaskVoxelListPointer _spRelevantVoxelVector; + + core::GeometricInfo _geoInfo; + + IDType _maskUID; + + public: + DummyMaskAccessor(const core::GeometricInfo& aGeometricInfo); + DummyMaskAccessor(const core::GeometricInfo& aGeometricInfo, MaskVoxelListPointer voxelListPtr); + ~DummyMaskAccessor() {}; + + void updateMask(); + + const core::GeometricInfo& getGeometricInfo() const + { + return _geoInfo; + }; + + MaskVoxelListPointer getRelevantVoxelVector(); + + MaskVoxelListPointer getRelevantVoxelVector(float lowerThreshold); + + bool getMaskAt(const VoxelGridID aID, core::MaskVoxel& voxel) const; + + bool getMaskAt(const VoxelGridIndex3D& gridIndex, core::MaskVoxel& voxel) const; + + bool isGridHomogeneous() const + { + return true; + } + + IDType getMaskUID() const + { + return _maskUID; + }; + }; + } +} + +#endif diff --git a/testing/core/DummyMutableDoseAccessor.cpp b/testing/core/DummyMutableDoseAccessor.cpp new file mode 100644 index 0000000..a6acc72 --- /dev/null +++ b/testing/core/DummyMutableDoseAccessor.cpp @@ -0,0 +1,113 @@ +// ----------------------------------------------------------------------- +// 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 "DummyMutableDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbIndexOutOfBoundsException.h" + +namespace rttb +{ + namespace testing + { + + DummyMutableDoseAccessor::~DummyMutableDoseAccessor() {} + + DummyMutableDoseAccessor::DummyMutableDoseAccessor() + { + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _doseUID = "DummyMutableDoseAccessor_" + ss.str(); + + SpacingVectorType3D aVector(2.5); + _geoInfo.setSpacing(aVector); + WorldCoordinate3D anOtherVector(-25, -2, 35); + _geoInfo.setImagePositionPatient(anOtherVector); + _geoInfo.setNumRows(11); + _geoInfo.setNumColumns(10); + _geoInfo.setNumSlices(5); + + OrientationMatrix unit = OrientationMatrix(); + _geoInfo.setOrientationMatrix(unit); + + for (int i = 0; i < _geoInfo.getNumberOfVoxels(); i++) + { + doseData.push_back((double(rand()) / RAND_MAX) * 1000); + } + } + + DummyMutableDoseAccessor::DummyMutableDoseAccessor(const std::vector& aDoseVector, + const core::GeometricInfo& geoInfo) + { + boost::uuids::uuid id; + boost::uuids::random_generator generator; + id = generator(); + std::stringstream ss; + ss << id; + _doseUID = "DummyMutableDoseAccessor_" + ss.str(); + + doseData = aDoseVector; + _geoInfo = geoInfo; + } + + DoseTypeGy DummyMutableDoseAccessor::getDoseAt(const VoxelGridID aID) const + { + if (!_geoInfo.validID(aID)) + { + throw core::IndexOutOfBoundsException("Not a valid Position!"); + } + + return doseData.at(aID); + } + + DoseTypeGy DummyMutableDoseAccessor::getDoseAt(const VoxelGridIndex3D& aIndex) const + { + VoxelGridID gridID = 0; + _geoInfo.convert(aIndex, gridID); + return getDoseAt(gridID); + } + + void DummyMutableDoseAccessor::setDoseAt(const VoxelGridID aID, DoseTypeGy value) + { + if (!_geoInfo.validID(aID)) + { + throw core::IndexOutOfBoundsException("Not a valid Position!"); + } + + doseData.at(aID) = value; + } + + void DummyMutableDoseAccessor::setDoseAt(const VoxelGridIndex3D& aIndex, DoseTypeGy value) + { + VoxelGridID gridID = 0; + _geoInfo.convert(aIndex, gridID); + setDoseAt(gridID, value); + } + + }//end namespace testing +}//end namespace rttb \ No newline at end of file diff --git a/testing/core/DummyMutableDoseAccessor.h b/testing/core/DummyMutableDoseAccessor.h new file mode 100644 index 0000000..99bb819 --- /dev/null +++ b/testing/core/DummyMutableDoseAccessor.h @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// 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 __DUMMY_MUTABLE_DOSE_ACCESSOR_H +#define __DUMMY_MUTABLE_DOSE_ACCESSOR_H + +#include + +#include "rttbMutableDoseAccessorInterface.h" +#include "rttbGeometricInfo.h" +#include "rttbBaseType.h" + + +namespace rttb +{ + namespace testing + { + + /*! @class DummyMutableDoseAccessor + @brief A dummy MutableDoseAccessor for testing filled with random dose values between 0 and 100. + The default grid size is [11,10,5] + */ + class DummyMutableDoseAccessor: public core::MutableDoseAccessorInterface + { + + private: + /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ + std::vector doseData; + + IDType _doseUID; + + + public: + ~DummyMutableDoseAccessor(); + + /*! @brief A dummy DummyMutableDoseAccessor for testing filled with random dose values between 0 and 100. + The default grid size is [11,10,5] + */ + DummyMutableDoseAccessor(); + + /*! @brief Constructor. + Initialisation of dose with a given vector. + */ + DummyMutableDoseAccessor(const std::vector& aDoseVector, + const core::GeometricInfo& geoInfo); + + const std::vector* getDoseVector() const + { + return &doseData; + }; + + DoseTypeGy getDoseAt(const VoxelGridID aID) const; + + DoseTypeGy getDoseAt(const VoxelGridIndex3D& aIndex) const; + + void setDoseAt(const VoxelGridID aID, DoseTypeGy value); + + void setDoseAt(const VoxelGridIndex3D& aIndex, DoseTypeGy value); + + const IDType getDoseUID() const + { + return _doseUID; + }; + }; + } +} + +#endif diff --git a/testing/core/DummyStructure.cpp b/testing/core/DummyStructure.cpp new file mode 100644 index 0000000..74c5eee --- /dev/null +++ b/testing/core/DummyStructure.cpp @@ -0,0 +1,603 @@ +// ----------------------------------------------------------------------- +// 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 "DummyStructure.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbInvalidDoseException.h" + +namespace rttb{ + namespace testing{ + + DummyStructure::~DummyStructure(){} + + DummyStructure::DummyStructure(const core::GeometricInfo& aGeoInfo){ + _geoInfo = aGeoInfo; + } + + core::Structure DummyStructure::CreateRectangularStructureCentered(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + VoxelGridIndex2D another_i1(2,1); + VoxelGridIndex2D another_i2(5,1); + VoxelGridIndex2D another_i3(5,5); + VoxelGridIndex2D another_i4(2,5); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure_rectangular_centered = core::Structure( another_polySeq ); + + return test_structure_rectangular_centered; + } + + core::Structure DummyStructure::CreateRectangularStructureCenteredRotatedIntermediatePlacement(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + VoxelGridIndex2D another_i1(5,1); + VoxelGridIndex2D another_i2(8,4); + VoxelGridIndex2D another_i3(5,7); + VoxelGridIndex2D another_i4(2,4); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonBetweenUpperLeftAndCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateRectangularStructureCenteredRotatedIntermediatePlacementLowerRight(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + VoxelGridIndex2D another_i1(5,1); + VoxelGridIndex2D another_i2(8,4); + VoxelGridIndex2D another_i3(5,7); + VoxelGridIndex2D another_i4(2,4); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonBetweenLowerRightAndCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateRectangularStructureCenteredRotatedIntermediatePlacementLowerRightCounterClock(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,4); + VoxelGridIndex2D another_i2(5,7); + VoxelGridIndex2D another_i3(8,4); + VoxelGridIndex2D another_i4(5,1); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonBetweenLowerRightAndCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateRectangularStructureCenteredRotatedIntermediatePlacementLowerRightCounterClockIntermediatePoints(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,4); + VoxelGridIndex2D another_i2(5,7); + VoxelGridIndex2D another_i3(8,4); + VoxelGridIndex2D another_i4(5,1); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonIntermediatePoints( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureSeveralSeperateSectionsInsideOneVoxel(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,2); + + another_voxelVector.push_back(another_i1); + + PolygonType another_polygon1 = another_cts.createStructureSeveralSectionsInsideOneVoxelA( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureSelfTouchingA(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,2); + + another_voxelVector.push_back(another_i1); + + PolygonType another_polygon1 = another_cts.createStructureSelfTouchingA( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureIntersectingTwoPolygonsInDifferentSlices(GridIndexType zPlane1, GridIndexType zPlane2){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + CreateTestStructure one_more_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,4); + VoxelGridIndex2D another_i2(8,4); + VoxelGridIndex2D another_i3(8,6); + VoxelGridIndex2D another_i4(2,6); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonCenter( another_voxelVector , zPlane1 ); + + std::vector one_more_voxelVector; + + VoxelGridIndex2D one_more_i1(3,5); + VoxelGridIndex2D one_more_i2(9,5); + VoxelGridIndex2D one_more_i3(9,7); + VoxelGridIndex2D one_more_i4(3,7); + + one_more_voxelVector.push_back(one_more_i1); + one_more_voxelVector.push_back(one_more_i2); + one_more_voxelVector.push_back(one_more_i3); + one_more_voxelVector.push_back(one_more_i4); + PolygonType another_polygon2 = one_more_cts.createPolygonCenter( one_more_voxelVector , zPlane2 ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + another_polySeq.push_back( another_polygon2 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureIntersectingTwoPolygons(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + CreateTestStructure one_more_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,4); + VoxelGridIndex2D another_i2(8,4); + VoxelGridIndex2D another_i3(8,6); + VoxelGridIndex2D another_i4(2,6); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonCenter( another_voxelVector , zPlane ); + + std::vector one_more_voxelVector; + + VoxelGridIndex2D one_more_i1(3,5); + VoxelGridIndex2D one_more_i2(9,5); + VoxelGridIndex2D one_more_i3(9,7); + VoxelGridIndex2D one_more_i4(3,7); + + one_more_voxelVector.push_back(one_more_i1); + one_more_voxelVector.push_back(one_more_i2); + one_more_voxelVector.push_back(one_more_i3); + one_more_voxelVector.push_back(one_more_i4); + PolygonType another_polygon2 = one_more_cts.createPolygonCenter( one_more_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + another_polySeq.push_back( another_polygon2 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureIntersecting(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,4); + VoxelGridIndex2D another_i2(8,4); + VoxelGridIndex2D another_i3(2,6); + VoxelGridIndex2D another_i4(8,6); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouches(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(3,4); + VoxelGridIndex2D another_i2(2,8); + VoxelGridIndex2D another_i3(4,8); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + + PolygonType another_polygon1 = another_cts.createPolygonUpperCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesRotatedPointDoubeled(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,4); + VoxelGridIndex2D another_i4(2,4); + VoxelGridIndex2D another_i2(4,4); + VoxelGridIndex2D another_i3(3,8); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + + PolygonType another_polygon1 = another_cts.createPolygonUpperCenter( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesCounterClockRotatedOnePointFivePi(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(3,5); + VoxelGridIndex2D another_i2(3,5); + VoxelGridIndex2D another_i3(7,6); + VoxelGridIndex2D another_i4(7,6); + VoxelGridIndex2D another_i5(7,4); + VoxelGridIndex2D another_i6(7,4); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i5); + another_voxelVector.push_back(another_i6); + + PolygonType another_polygon1 = another_cts.createPolygonLeftEdgeMiddle( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesCounterClockRotatedQuaterPi(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(7,5); + VoxelGridIndex2D another_i2(7,5); + VoxelGridIndex2D another_i3(3,6); + VoxelGridIndex2D another_i4(3,6); + VoxelGridIndex2D another_i5(3,4); + VoxelGridIndex2D another_i6(3,4); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i5); + another_voxelVector.push_back(another_i6); + + PolygonType another_polygon1 = another_cts.createPolygonLeftEdgeMiddle( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesRotatedQuaterPi(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(3,4); + VoxelGridIndex2D another_i2(3,4); + VoxelGridIndex2D another_i3(3,6); + VoxelGridIndex2D another_i4(3,6); + VoxelGridIndex2D another_i5(7,5); + VoxelGridIndex2D another_i6(7,5); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i5); + another_voxelVector.push_back(another_i6); + + PolygonType another_polygon1 = another_cts.createPolygonLeftEdgeMiddle( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureCircle(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(4,4); + + another_voxelVector.push_back(another_i1); + + PolygonType another_polygon1 = another_cts.createPolygonCircle( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesUpperRight(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + + VoxelGridIndex2D another_i1(3,4); + VoxelGridIndex2D another_i3(4,5); + VoxelGridIndex2D another_i5(5,3); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i5); + + PolygonType another_polygon1 = another_cts.createPolygonLeftUpper( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesLowerRight(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(2,2); + VoxelGridIndex2D another_i2(2,2); + VoxelGridIndex2D another_i3(3,1); + VoxelGridIndex2D another_i4(3,1); + VoxelGridIndex2D another_i5(4,3); + VoxelGridIndex2D another_i6(4,3); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i5); + another_voxelVector.push_back(another_i6); + + PolygonType another_polygon1 = another_cts.createPolygonLeftUpper( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesLowerLeft(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(3,3); + VoxelGridIndex2D another_i2(3,3); + VoxelGridIndex2D another_i3(4,4); + VoxelGridIndex2D another_i4(4,4); + VoxelGridIndex2D another_i5(2,5); + VoxelGridIndex2D another_i6(2,5); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i5); + another_voxelVector.push_back(another_i6); + + PolygonType another_polygon1 = another_cts.createPolygonLeftUpper( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + core::Structure DummyStructure::CreateTestStructureInsideInsideTouchesUpperLeft(GridIndexType zPlane){ + CreateTestStructure another_cts = CreateTestStructure(_geoInfo); + + std::vector another_voxelVector; + + VoxelGridIndex2D another_i1(5,5); + VoxelGridIndex2D another_i2(5,5); + VoxelGridIndex2D another_i3(4,6); + VoxelGridIndex2D another_i4(4,6); + VoxelGridIndex2D another_i5(3,4); + VoxelGridIndex2D another_i6(3,4); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + another_voxelVector.push_back(another_i5); + another_voxelVector.push_back(another_i6); + + PolygonType another_polygon1 = another_cts.createPolygonLeftUpper( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + void DummyStructure::ShowTestStructure( core::Structure aStructure ){ + WorldCoordinate3D aPoint(0); + + PolygonSequenceType strVector =aStructure.getStructureVector(); + + for( unsigned int struct_index = 0 ; struct_index < strVector.size() ; struct_index++ ){ + + for( int point_index = 0 ; point_index < strVector.at(struct_index).size() ; point_index++ ){ + + aPoint = strVector.at(struct_index).at( point_index ); + + std::cout<< " aPoint.x " << aPoint.x() < another_voxelVector; + VoxelGridIndex2D another_i1(5,1); + VoxelGridIndex2D another_i2(8,4); + VoxelGridIndex2D another_i3(5,7); + VoxelGridIndex2D another_i4(2,4); + + another_voxelVector.push_back(another_i1); + another_voxelVector.push_back(another_i2); + another_voxelVector.push_back(another_i3); + another_voxelVector.push_back(another_i4); + PolygonType another_polygon1 = another_cts.createPolygonLeftUpper( another_voxelVector , zPlane ); + + PolygonSequenceType another_polySeq; + another_polySeq.push_back( another_polygon1 ); + + core::Structure test_structure = core::Structure( another_polySeq ); + + return test_structure; + } + + }//testing + }//rttb + diff --git a/testing/core/DummyStructure.h b/testing/core/DummyStructure.h new file mode 100644 index 0000000..b142aa8 --- /dev/null +++ b/testing/core/DummyStructure.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$ (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 "rttbStructure.h" +#include "CreateTestStructure.h" +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" + +namespace rttb{ + namespace testing{ + + /*! @class DummyStructure + @brief generate simple geometric testing structures. + The maximal x coordinate used is 9 and the maximal y coordinate is 8. + Make sure the geometricInfo corresponds to a sufficiently large data grid. + @see CreateTestStructures + */ + class DummyStructure{ + + private: + core::GeometricInfo _geoInfo; + + public: + ~DummyStructure(); + + DummyStructure(const core::GeometricInfo& aGeoInfo); + + const core::GeometricInfo& getGeometricInfo(){ + return _geoInfo; + }; + + core::Structure CreateRectangularStructureCentered(GridIndexType zPlane); + + core::Structure CreateTestStructureCircle(GridIndexType zPlane); + + core::Structure CreateRectangularStructureUpperLeftRotated(GridIndexType zPlane); + + core::Structure CreateTestStructureSeveralSeperateSectionsInsideOneVoxel(GridIndexType zPlane); + + core::Structure CreateRectangularStructureCenteredRotatedIntermediatePlacement(GridIndexType zPlane); + core::Structure CreateRectangularStructureCenteredRotatedIntermediatePlacementLowerRight(GridIndexType zPlane); + core::Structure CreateRectangularStructureCenteredRotatedIntermediatePlacementLowerRightCounterClock(GridIndexType zPlane); + core::Structure CreateRectangularStructureCenteredRotatedIntermediatePlacementLowerRightCounterClockIntermediatePoints(GridIndexType zPlane); + + core::Structure CreateTestStructureSelfTouchingA(GridIndexType zPlane); + + core::Structure CreateTestStructureIntersecting(GridIndexType zPlane); + core::Structure CreateTestStructureIntersectingTwoPolygons(GridIndexType zPlane); + core::Structure CreateTestStructureIntersectingTwoPolygonsInDifferentSlices(GridIndexType zPlane1, GridIndexType zPlane2); + + core::Structure CreateTestStructureInsideInsideTouches(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesRotatedQuaterPi(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesCounterClockRotatedQuaterPi(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesCounterClockRotatedOnePointFivePi(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesRotatedPointDoubeled(GridIndexType zPlane); + + core::Structure CreateTestStructureInsideInsideTouchesUpperLeft(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesLowerLeft(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesLowerRight(GridIndexType zPlane); + core::Structure CreateTestStructureInsideInsideTouchesUpperRight(GridIndexType zPlane); + + void ShowTestStructure( core::Structure aStructure ); + }; + }//testing + }//rttb + diff --git a/testing/core/GenericDoseIteratorTest.cpp b/testing/core/GenericDoseIteratorTest.cpp new file mode 100644 index 0000000..92b7ef2 --- /dev/null +++ b/testing/core/GenericDoseIteratorTest.cpp @@ -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$ (last changed revision) +// @date $Date$ (last change date) +// @author $Author$ (last changed by) +*/ + +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGenericDoseIterator.h" +#include "DummyDoseAccessor.h" + +namespace rttb{ + namespace testing{ + + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + + /*! @brief GenericDoseIteratorTest - test the API of GenericDoseIterator + 1) test constructor (values as expected?) + 2) test reset/next/get current values/isPositionValid + */ + int GenericDoseIteratorTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + //create dummy DoseAccessor + boost::shared_ptr spTestDoseAccessor = boost::make_shared(); + DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); + + //1) test constructor (values as expected?) + CHECK_NO_THROW(core::GenericDoseIterator genDoseIterator(spDoseAccessor)); + core::GenericDoseIterator genDoseIterator(spDoseAccessor); + const VoxelGridID defaultDoseVoxelGridID = 0; + const DoseVoxelVolumeType defaultVoxelVolume = 0; + CHECK_EQUAL(defaultDoseVoxelGridID, genDoseIterator.getCurrentVoxelGridID()); + CHECK_EQUAL(defaultVoxelVolume, genDoseIterator.getCurrentVoxelVolume()); + + //2) test reset/next + genDoseIterator.reset(); + const DoseVoxelVolumeType homogeneousVoxelVolume = genDoseIterator.getCurrentVoxelVolume(); + CHECK_EQUAL(defaultDoseVoxelGridID, genDoseIterator.getCurrentVoxelGridID()); + CHECK(!(defaultVoxelVolume == genDoseIterator.getCurrentVoxelVolume())); + core::GeometricInfo geoInfo = spTestDoseAccessor->getGeometricInfo(); + SpacingVectorType3D spacing = geoInfo.getSpacing(); + CHECK_EQUAL(spacing(0)*spacing(1)*spacing(2)/1000, genDoseIterator.getCurrentVoxelVolume()); + + //check if the correct voxels are accessed + const std::vector* doseVals = spTestDoseAccessor->getDoseVector(); + genDoseIterator.reset(); + VoxelGridID position = 0; + while (genDoseIterator.isPositionValid()){ + CHECK_EQUAL(genDoseIterator.getCurrentDoseValue(), doseVals->at(position)); + CHECK_EQUAL(homogeneousVoxelVolume, genDoseIterator.getCurrentVoxelVolume()); + CHECK_EQUAL(1, genDoseIterator.getCurrentRelevantVolumeFraction()); + CHECK_EQUAL(position, genDoseIterator.getCurrentVoxelGridID()); + + genDoseIterator.next(); + position++; + } + //check isPositionValid() in invalid positions + CHECK(!(genDoseIterator.isPositionValid())); //after end of dose + genDoseIterator.reset(); + CHECK_EQUAL(defaultDoseVoxelGridID, genDoseIterator.getCurrentVoxelGridID()); + CHECK(genDoseIterator.isPositionValid());//before start of dose + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing + }//end namespace rttb + diff --git a/testing/core/GenericMaskedDoseIteratorTest.cpp b/testing/core/GenericMaskedDoseIteratorTest.cpp new file mode 100644 index 0000000..9326d1a --- /dev/null +++ b/testing/core/GenericMaskedDoseIteratorTest.cpp @@ -0,0 +1,115 @@ +// ----------------------------------------------------------------------- +// 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 "rttbGenericMaskedDoseIterator.h" +#include "rttbNullPointerException.h" +#include "rttbException.h" +#include "DummyDoseAccessor.h" +#include "DummyMaskAccessor.h" + +namespace rttb{ + namespace testing{ + + typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; + typedef core::GenericMaskedDoseIterator::DoseAccessorPointer DoseAccessorPointer; + + /*! @brief GenericMaskedDoseIteratorTest. + 1) test constructor (values as expected?) + 2) test reset/next/get current values/isPositionValid + */ + int GenericMaskedDoseIteratorTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + boost::shared_ptr spTestDoseAccessor = boost::make_shared(); + DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); + const std::vector* doseVals = spTestDoseAccessor->getDoseVector(); + + core::GeometricInfo geoInfo; + boost::shared_ptr spTestMaskAccessor = boost::make_shared(geoInfo); + MaskAccessorPointer spMaskAccessor(spTestMaskAccessor); + + //1) test constructor (values as expected?) + //not NULL pointer + MaskAccessorPointer spNullMaskAccessor; + DoseAccessorPointer spNullDoseAccessor; + CHECK_THROW_EXPLICIT(core::GenericMaskedDoseIterator genMaskedDoseIterator(spNullMaskAccessor, spDoseAccessor), + core::NullPointerException); + CHECK_THROW_EXPLICIT(core::GenericMaskedDoseIterator genMaskedDoseIterator(spMaskAccessor, spNullDoseAccessor), + core::NullPointerException); + //not same core::GeometricInfo + CHECK_THROW_EXPLICIT(core::GenericMaskedDoseIterator genMaskedDoseIterator(spMaskAccessor, spDoseAccessor), core::Exception); + //set correct GeometricInfo + geoInfo = spDoseAccessor->getGeometricInfo(); + boost::shared_ptr spTestMaskAccessor1 = boost::make_shared(geoInfo); + MaskAccessorPointer spMaskAccessorTemp(spTestMaskAccessor1); + spMaskAccessor.swap(spMaskAccessorTemp); + CHECK_NO_THROW(core::GenericMaskedDoseIterator genMaskedDoseIterator(spMaskAccessor, spDoseAccessor)); + core::GenericMaskedDoseIterator genMaskedDoseIterator(spMaskAccessor, spDoseAccessor); + + //2) test reset/next + const DummyMaskAccessor::MaskVoxelListPointer maskedVoxelListPtr = spTestMaskAccessor1->getRelevantVoxelVector(); + genMaskedDoseIterator.reset(); + const DoseVoxelVolumeType homogeneousVoxelVolume = genMaskedDoseIterator.getCurrentVoxelVolume(); + CHECK_EQUAL((maskedVoxelListPtr->begin())->getVoxelGridID(), genMaskedDoseIterator.getCurrentVoxelGridID()); + geoInfo = spDoseAccessor->getGeometricInfo(); + SpacingVectorType3D spacing = geoInfo.getSpacing(); + CHECK_EQUAL(spacing(0)*spacing(1)*spacing(2)/1000, genMaskedDoseIterator.getCurrentVoxelVolume()); + + //check if the correct voxels are accessed + genMaskedDoseIterator.reset(); + VoxelGridID defaultDoseVoxelGridID = genMaskedDoseIterator.getCurrentVoxelGridID(); + DoseTypeGy controlValue = 0; + VoxelGridID position = 0; + while (genMaskedDoseIterator.isPositionValid()){ + controlValue = doseVals->at((maskedVoxelListPtr->at(position)).getVoxelGridID()); + CHECK_EQUAL(controlValue, genMaskedDoseIterator.getCurrentDoseValue()); + controlValue = controlValue*(maskedVoxelListPtr->at(position)).getRelevantVolumeFraction(); + CHECK_EQUAL(controlValue, genMaskedDoseIterator.getCurrentMaskedDoseValue()); + CHECK_EQUAL(homogeneousVoxelVolume, genMaskedDoseIterator.getCurrentVoxelVolume()); + CHECK_EQUAL((maskedVoxelListPtr->at(position)).getRelevantVolumeFraction(), + genMaskedDoseIterator.getCurrentRelevantVolumeFraction()); + CHECK_EQUAL((maskedVoxelListPtr->at(position)).getVoxelGridID(), genMaskedDoseIterator.getCurrentVoxelGridID()); + CHECK(genMaskedDoseIterator.isPositionValid()); + + genMaskedDoseIterator.next(); + position++; + } + //check isPositionValid() in invalid positions + CHECK(!(genMaskedDoseIterator.isPositionValid())); //after end of dose + genMaskedDoseIterator.reset(); + CHECK_EQUAL(defaultDoseVoxelGridID, genMaskedDoseIterator.getCurrentVoxelGridID()); + CHECK(genMaskedDoseIterator.isPositionValid());//at start of dose + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + + + }//testing +}//rttb + diff --git a/testing/core/GeometricInfoTest.cpp b/testing/core/GeometricInfoTest.cpp new file mode 100644 index 0000000..6ffd656 --- /dev/null +++ b/testing/core/GeometricInfoTest.cpp @@ -0,0 +1,366 @@ +// ----------------------------------------------------------------------- +// 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 "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" + +namespace rttb +{ + namespace testing + { + + /*!@brief GeometricInfoTest - test the API of GeometricInfo + @note ITK pixel indexing: Index[0] = col, Index[1] = row, Index[2] = slice. + 1) test default constructor (values as expected?) + 2) test set/getImagePositionPatient + 3) test set/getPixelSpacingColumn/Row/SliceThickness + 4) test set/getSpacing + 5) test set/getNumColumns/Rows/Slices + 6) test get/setOrientationMatrix + 7) test getImageOrientationRow/Column + 8) test operators "==" + 9) test world to index coordinate conversion + 10) test isInside and index to world coordinate conversion + 11) test getNumberOfVoxels + */ + + int GeometricInfoTest(int argc, char* argv[]) + { + PREPARE_DEFAULT_TEST_REPORTING; + + //1) test default constructor (values as expected?) + CHECK_NO_THROW(core::GeometricInfo()); + core::GeometricInfo geoInfo; + SpacingVectorType3D testNullSV(0); + WorldCoordinate3D testNullWC(0); + OrientationMatrix testNullOM(0); + CHECK_EQUAL(testNullSV, geoInfo.getSpacing()); + CHECK_EQUAL(testNullWC, geoInfo.getImagePositionPatient()); + CHECK_EQUAL(testNullOM, geoInfo.getOrientationMatrix()); + + //2) test set/getImagePositionPatient + WorldCoordinate3D testIPP(1.2, 3.4, 5.6); + CHECK_NO_THROW(geoInfo.setImagePositionPatient(testIPP)); + geoInfo.setImagePositionPatient(testIPP); + CHECK_EQUAL(testIPP, geoInfo.getImagePositionPatient()); + + //3) test set/getPixelSpacingColumn/Row/SliceThickness + SpacingVectorType3D expectedSpacing(1, 2.2, 3.3); + CHECK_NO_THROW(geoInfo.setPixelSpacingRow(expectedSpacing(0))); + CHECK_EQUAL(expectedSpacing(0), geoInfo.getPixelSpacingRow()); + CHECK_EQUAL(SpacingVectorType3D(expectedSpacing(0), 0, 0), geoInfo.getSpacing()); + CHECK_EQUAL(geoInfo.getPixelSpacingRow(), geoInfo.getSpacing()(0)); + + CHECK_NO_THROW(geoInfo.setPixelSpacingColumn(expectedSpacing(1))); + CHECK_EQUAL(expectedSpacing(1), geoInfo.getPixelSpacingColumn()); + CHECK_EQUAL(SpacingVectorType3D(expectedSpacing(0), expectedSpacing(1), 0), geoInfo.getSpacing()); + CHECK_EQUAL(geoInfo.getPixelSpacingColumn(), geoInfo.getSpacing()(1)); + + CHECK_NO_THROW(geoInfo.setSliceThickness(expectedSpacing(2))); + CHECK_EQUAL(expectedSpacing(2), geoInfo.getSliceThickness()); + CHECK_EQUAL(expectedSpacing, geoInfo.getSpacing()); + CHECK_EQUAL(geoInfo.getSliceThickness(), geoInfo.getSpacing()(2)); + + //4) test set/getSpacing + //negative spacing does not make sense! + /*!@todo: Should negative values be prohibited/set to zero?*/ + expectedSpacing(0) = 4.15; + expectedSpacing(1) = 2.35; + expectedSpacing(2) = 100; + CHECK_NO_THROW(geoInfo.setSpacing(expectedSpacing)); + geoInfo.setSpacing(expectedSpacing); + CHECK_EQUAL(expectedSpacing, geoInfo.getSpacing()); + + //5) test set/getNumColumns/Rows/Slices + const VoxelGridIndex3D expectedVoxelDims(10, 5, 3); + //CHECK_THROW(geoInfo.setNumColumns(1.2)); -> implicit conversion will prevent exception + CHECK_NO_THROW(geoInfo.setNumColumns(expectedVoxelDims(0))); + geoInfo.setNumColumns(expectedVoxelDims(0)); + + CHECK_NO_THROW(geoInfo.setNumRows(expectedVoxelDims(1))); + geoInfo.setNumRows(expectedVoxelDims(1)); + + //CHECK_THROW(geoInfo.setNumSlices(4.2)); -> implicit conversion will prevent exception + CHECK_NO_THROW(geoInfo.setNumSlices(expectedVoxelDims(2))); + geoInfo.setNumSlices(expectedVoxelDims(2)); + + ImageSize rttbSize = geoInfo.getImageSize(); + CHECK_EQUAL(rttbSize(0), geoInfo.getNumColumns()); + CHECK_EQUAL(rttbSize(1), geoInfo.getNumRows()); + CHECK_EQUAL(rttbSize(2), geoInfo.getNumSlices()); + + rttbSize = ImageSize(11, 99, 6); + + core::GeometricInfo geoInfo3; + geoInfo3.setImageSize(rttbSize); + CHECK_EQUAL(rttbSize(0), geoInfo3.getNumColumns()); + CHECK_EQUAL(rttbSize(1), geoInfo3.getNumRows()); + CHECK_EQUAL(rttbSize(2), geoInfo3.getNumSlices()); + + + //6) test get/setOrientationMatrix + CHECK_EQUAL(testNullOM, geoInfo.getOrientationMatrix()); + OrientationMatrix testOM(0); + const WorldCoordinate3D testIORow(5.5, 4.7, 3.2); + const WorldCoordinate3D testIOColumn(2.5, 1.8, 9.1); + WorldCoordinate3D ortho = testIORow.cross(testIOColumn); + + testOM(0, 0) = testIORow(0); + testOM(1, 0) = testIORow(1); + testOM(2, 0) = testIORow(2); + CHECK_NO_THROW(geoInfo.setOrientationMatrix(testOM)); + geoInfo.setOrientationMatrix(testOM); + CHECK_EQUAL(testOM, geoInfo.getOrientationMatrix()); + + testOM(0, 1) = testIOColumn(0); + testOM(1, 1) = testIOColumn(1); + testOM(2, 1) = testIOColumn(2); + CHECK_NO_THROW(geoInfo.setOrientationMatrix(testOM)); + geoInfo.setOrientationMatrix(testOM); + CHECK_EQUAL(testOM, geoInfo.getOrientationMatrix()); + + testOM(0, 2) = ortho(0); + testOM(1, 2) = ortho(1); + testOM(2, 2) = ortho(2); + CHECK_NO_THROW(geoInfo.setOrientationMatrix(testOM)); + geoInfo.setOrientationMatrix(testOM); + CHECK_EQUAL(testOM, geoInfo.getOrientationMatrix()); + + //7) test getImageOrientationRow/Column + CHECK_EQUAL(testIORow, geoInfo.getImageOrientationRow()); + CHECK_EQUAL(testIOColumn, geoInfo.getImageOrientationColumn()); + + //8) test operators "==" + core::GeometricInfo geoInfo2; + CHECK_EQUAL(geoInfo, geoInfo); + CHECK(!(geoInfo == geoInfo2)); + + CHECK_EQUAL(geoInfo.getOrientationMatrix(), testOM); + CHECK(!(geoInfo.getOrientationMatrix() == testNullOM)); + + //9) test world to index coordinate converion + //use unit matrix as orientation matrix + CHECK_NO_THROW(geoInfo.setOrientationMatrix(OrientationMatrix())); + + //origin (inside) + WorldCoordinate3D insideTestWC1 = geoInfo.getImagePositionPatient(); + //inside + const VoxelGridIndex3D expectedIndex(8, 3, 2); + WorldCoordinate3D insideTestWC2(expectedIndex(0)*expectedSpacing(0) + testIPP(0), + expectedIndex(1)*expectedSpacing(1) + testIPP(1), expectedIndex(2)*expectedSpacing(2) + testIPP(2)); + //outside + WorldCoordinate3D insideTestWC3(-33.12, 0, 14); + // outside (dimension of grid) + WorldCoordinate3D insideTestWC4(expectedVoxelDims(0)*expectedSpacing(0) + testIPP(0), + expectedVoxelDims(1)*expectedSpacing(1) + testIPP(1), + expectedVoxelDims(2)*expectedSpacing(2) + testIPP(2)); + CHECK(geoInfo.isInside(insideTestWC1)); + CHECK(geoInfo.isInside(insideTestWC2)); + CHECK(!(geoInfo.isInside(insideTestWC3))); + CHECK(!(geoInfo.isInside(insideTestWC4))); + + VoxelGridIndex3D testConvert(0); + CHECK(geoInfo.worldCoordinateToIndex(insideTestWC1, testConvert)); + CHECK(geoInfo.isInside(testConvert)); + CHECK_EQUAL(VoxelGridIndex3D(0), testConvert); + CHECK(geoInfo.worldCoordinateToIndex(insideTestWC2, testConvert)); + CHECK(geoInfo.isInside(testConvert)); + CHECK_EQUAL(expectedIndex, testConvert); + CHECK(!(geoInfo.worldCoordinateToIndex(insideTestWC3, testConvert))); + //CHECK_EQUAL(VoxelGridIndex3D(0),testConvert); //if value is in a negative grid position it will be converted + //to a very large unrelated number. + /*! @todo Should VoxelGridIndex3D assign a special value to negative indices? */ + CHECK(!(geoInfo.isInside(testConvert))); + CHECK(!(geoInfo.worldCoordinateToIndex(insideTestWC4, testConvert))); + CHECK_EQUAL(expectedVoxelDims, testConvert); + CHECK(!(geoInfo.isInside(testConvert))); + + //use a more complicated orientation matrix + OrientationMatrix newOrientation(0); + newOrientation(0, 0) = 0.5; + newOrientation(1, 2) = -3; + newOrientation(2, 1) = 1; + CHECK_NO_THROW(geoInfo.setOrientationMatrix(newOrientation)); + + testIPP = WorldCoordinate3D(20, 100, -1000); + CHECK_NO_THROW(geoInfo.setImagePositionPatient(testIPP)); + + CHECK_NO_THROW(geoInfo.setSpacing(SpacingVectorType3D(1))); + + //values for testing were generated with a dedicated MeVisLab routine + insideTestWC1 = geoInfo.getImagePositionPatient(); //origin (inside) + const VoxelGridIndex3D expectedIndexWC1(0, 0, 0); + insideTestWC2 = WorldCoordinate3D(22.5, 97, -998); //inside + const VoxelGridIndex3D expectedIndexWC2(5, 2, 1); + insideTestWC3 = WorldCoordinate3D(26, 88, -996); //outside + const VoxelGridIndex3D expectedIndexWC3(12, 4, 4); + insideTestWC4 = WorldCoordinate3D(25, 91, -995); // outside: Grid dimension = [10,5,3] + const VoxelGridIndex3D expectedIndexWC4(10, 5, 3); + + CHECK(geoInfo.isInside(insideTestWC1)); + CHECK_EQUAL(geoInfo.isInside(insideTestWC1), geoInfo.isInside(expectedIndexWC1)); + CHECK(geoInfo.isInside(insideTestWC2)); + CHECK_EQUAL(geoInfo.isInside(insideTestWC2), geoInfo.isInside(expectedIndexWC2)); + CHECK(!(geoInfo.isInside(insideTestWC3))); + CHECK_EQUAL(geoInfo.isInside(insideTestWC3), geoInfo.isInside(expectedIndexWC3)); + CHECK(!(geoInfo.isInside(insideTestWC4))); + CHECK_EQUAL(geoInfo.isInside(insideTestWC4), geoInfo.isInside(expectedIndexWC4)); + + CHECK(geoInfo.worldCoordinateToIndex(insideTestWC1, testConvert)); + CHECK(geoInfo.isInside(testConvert)); + CHECK_EQUAL(expectedIndexWC1, testConvert); + + CHECK(geoInfo.worldCoordinateToIndex(insideTestWC2, testConvert)); + CHECK(geoInfo.isInside(testConvert)); + CHECK_EQUAL(expectedIndexWC2, testConvert); + + CHECK(!(geoInfo.worldCoordinateToIndex(insideTestWC3, testConvert))); + CHECK(!(geoInfo.isInside(testConvert))); + CHECK_EQUAL(expectedIndexWC3, testConvert); + + CHECK(!(geoInfo.worldCoordinateToIndex(insideTestWC4, testConvert))); + CHECK(!(geoInfo.isInside(testConvert))); + CHECK_EQUAL(expectedIndexWC4, testConvert); + + //10) test isInside and index to world coordinate converion + //use unit matrix as orientation matrix + CHECK_NO_THROW(geoInfo.setOrientationMatrix(OrientationMatrix())); + + VoxelGridIndex3D insideTest1(0, 0, 0); //origin (inside) + VoxelGridIndex3D insideTest2(2, 3, 1); //inside + VoxelGridIndex3D insideTest3(0, 6, 14); //outside + VoxelGridIndex3D insideTest4 = expectedVoxelDims; // outside + CHECK(geoInfo.isInside(insideTest1)); + CHECK(geoInfo.isInside(insideTest2)); + CHECK(!(geoInfo.isInside(insideTest3))); + CHECK(!(geoInfo.isInside(insideTest4))); + + WorldCoordinate3D testInside(0); + CHECK(geoInfo.indexToWorldCoordinate(insideTest1, testInside)); + CHECK(geoInfo.isInside(testInside)); + //CHECK_EQUAL(geoInfo.getImagePositionPatient(),testInside); //half voxel shift prevents equality! + CHECK(geoInfo.indexToWorldCoordinate(insideTest2, testInside)); + CHECK(geoInfo.isInside(testInside)); + CHECK(!(geoInfo.indexToWorldCoordinate(insideTest3, testInside))); + CHECK(!(geoInfo.isInside(testInside))); + CHECK(!(geoInfo.indexToWorldCoordinate(insideTest4, testInside))); + CHECK(!(geoInfo.isInside(testInside))); + + //use a more complicated orientation matrix + newOrientation = OrientationMatrix(0); + newOrientation(0, 0) = 0.5; + newOrientation(1, 2) = -3; + newOrientation(2, 1) = 1; + CHECK_NO_THROW(geoInfo.setOrientationMatrix(newOrientation)); + + testIPP = WorldCoordinate3D(20, 100, -1000); + CHECK_NO_THROW(geoInfo.setImagePositionPatient(testIPP)); + + CHECK_NO_THROW(geoInfo.setSpacing(SpacingVectorType3D(1))); + + //values for testing were generated with a dedicated MeVisLab routine + //expected worldcoordinates need to accomodate the half voxel shift + //caused by the use of the central position in the voxel for conversion. + /*!@todo Should the conversion always use the centroid of the current voxel?*/ + /*!@todo Should it be prohibited to define negative VoxelGridIndices, because conversion + generates strange values?*/ + insideTest1 = VoxelGridIndex3D(0, 0, 0); //origin (inside) + const WorldCoordinate3D expectedIndex1(19.75, 101.5, -1000.5); + insideTest2 = VoxelGridIndex3D(6, 0, 2); //inside + const WorldCoordinate3D expectedIndex2(22.75, 95.5, -1000.5); + insideTest3 = VoxelGridIndex3D(11, 6, 15); //outside + const WorldCoordinate3D expectedIndex3(25.25, 56.5, -994.5); + insideTest4 = VoxelGridIndex3D(10, 5, 3); // outside: Grid dimension = [10,5,3] + const WorldCoordinate3D expectedIndex4(24.75, 92.5, -995.5); + + CHECK(geoInfo.isInside(insideTest1)); + CHECK_EQUAL(geoInfo.isInside(insideTest1), geoInfo.isInside(expectedIndex1)); + CHECK(geoInfo.isInside(insideTest2)); + CHECK_EQUAL(geoInfo.isInside(insideTest2), geoInfo.isInside(expectedIndex2)); + CHECK(!(geoInfo.isInside(insideTest3))); + CHECK_EQUAL(geoInfo.isInside(insideTest3), geoInfo.isInside(expectedIndex3)); + CHECK(!(geoInfo.isInside(insideTest4))); + CHECK_EQUAL(geoInfo.isInside(insideTest4), geoInfo.isInside(expectedIndex4)); + + CHECK(geoInfo.indexToWorldCoordinate(insideTest1, testInside)); + CHECK(geoInfo.isInside(testInside)); + CHECK_EQUAL(expectedIndex1, testInside); + + CHECK(geoInfo.indexToWorldCoordinate(insideTest2, testInside)); + CHECK(geoInfo.isInside(testInside)); + CHECK_EQUAL(expectedIndex2, testInside); + + CHECK(!(geoInfo.indexToWorldCoordinate(insideTest3, testInside))); + CHECK(!(geoInfo.isInside(testInside))); + CHECK_EQUAL(expectedIndex3, testInside); + + CHECK(!(geoInfo.indexToWorldCoordinate(insideTest4, testInside))); + CHECK(!(geoInfo.isInside(testInside))); + CHECK_EQUAL(expectedIndex4, testInside); + + //11) test getNumberOfVoxels + CHECK_EQUAL(expectedVoxelDims(0)*expectedVoxelDims(1)*expectedVoxelDims(2), + geoInfo.getNumberOfVoxels()); + + //12) Test convert, validID and validIndex + geoInfo.setNumColumns(50); + geoInfo.setNumRows(30); + geoInfo.setNumSlices(40); + + VoxelGridIndex3D startIndex(0, 0, 0); + VoxelGridID startId(0); + VoxelGridIndex3D endIndex(geoInfo.getNumColumns() - 1, geoInfo.getNumRows() - 1, + geoInfo.getNumSlices() - 1); + VoxelGridID endId((geoInfo.getNumColumns()*geoInfo.getNumRows()*geoInfo.getNumSlices()) - 1); + VoxelGridIndex3D indexInvalid(geoInfo.getNumColumns(), geoInfo.getNumRows(), + geoInfo.getNumSlices()); + VoxelGridID idInvalid(geoInfo.getNumColumns()*geoInfo.getNumRows()*geoInfo.getNumSlices()); + + CHECK(geoInfo.validID(startId)); + CHECK(geoInfo.validIndex(startIndex)); + VoxelGridIndex3D aIndex; + VoxelGridID aId; + + CHECK(geoInfo.convert(startIndex, aId)); + CHECK(geoInfo.convert(startId, aIndex)); + CHECK_EQUAL(aId, startId); + CHECK_EQUAL(aIndex, startIndex); + + CHECK(geoInfo.validID(endId)); + CHECK(geoInfo.validIndex(endIndex)); + + CHECK(geoInfo.convert(endIndex, aId)); + CHECK(geoInfo.convert(endId, aIndex)); + CHECK_EQUAL(aId, endId); + CHECK_EQUAL(aIndex, endIndex); + + CHECK(!geoInfo.validID(idInvalid)); + CHECK(!geoInfo.validIndex(indexInvalid)); + CHECK(!geoInfo.convert(idInvalid, aIndex)); + CHECK(!geoInfo.convert(indexInvalid, aId)); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing +}//end namespace rttb diff --git a/testing/core/MaskVoxelTest.cpp b/testing/core/MaskVoxelTest.cpp new file mode 100644 index 0000000..7d35df4 --- /dev/null +++ b/testing/core/MaskVoxelTest.cpp @@ -0,0 +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) +*/ + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbMaskVoxel.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace testing{ + + /*! @brief MaskVoxelTest - test the API of MaskVoxel + 1) test constructors (values as expected?) + 2) test set/getRelevantVolumeFraction + 3) test legacy access + 4) test operators "==" + */ + + int MaskVoxelTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + //1) test constructors (values as expected?) + //MaskVoxel(const VoxelGridID& aVoxelGridID); + VoxelGridID anID = 5; + const FractionType defaultFraction = 1; + CHECK_NO_THROW(core::MaskVoxel MaskVoxel(anID)); + CHECK_THROW_EXPLICIT(core::MaskVoxel MaskVoxel(-anID), core::InvalidParameterException); + core::MaskVoxel aMaskVoxel2(anID); + CHECK_EQUAL(anID, aMaskVoxel2.getVoxelGridID()); + CHECK_EQUAL(defaultFraction, aMaskVoxel2.getRelevantVolumeFraction()); + + //MaskVoxel(const VoxelGridID& aVoxelGridID, FractionType aVolumeFraction) + anID = 15; + FractionType aFraction = 0.73; + CHECK_NO_THROW(core::MaskVoxel MaskVoxel(anID, aFraction)); + CHECK_THROW_EXPLICIT(core::MaskVoxel MaskVoxel(-anID, aFraction), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(core::MaskVoxel MaskVoxel(anID, -aFraction), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(core::MaskVoxel MaskVoxel(-anID, -aFraction), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(core::MaskVoxel MaskVoxel(anID, aFraction+2), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(core::MaskVoxel MaskVoxel(-anID, aFraction+2), core::InvalidParameterException); + core::MaskVoxel aMaskVoxel3(anID, aFraction); + CHECK_EQUAL(anID, aMaskVoxel3.getVoxelGridID()); + CHECK_EQUAL(aFraction, aMaskVoxel3.getRelevantVolumeFraction()); + + //2) test set/getRelevantVolumeFraction + aFraction = 0.42; + anID = aMaskVoxel3.getVoxelGridID(); + CHECK_NO_THROW(aMaskVoxel3.setRelevantVolumeFraction(aFraction)); + CHECK_THROW_EXPLICIT(aMaskVoxel3.setRelevantVolumeFraction(-aFraction), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(aMaskVoxel3.setRelevantVolumeFraction(aFraction+2), core::InvalidParameterException); + aMaskVoxel3.setRelevantVolumeFraction(aFraction); + CHECK_EQUAL(anID, aMaskVoxel3.getVoxelGridID()); + CHECK_EQUAL(aFraction, aMaskVoxel3.getRelevantVolumeFraction()); + + //3) test legacy access + aFraction = aMaskVoxel3.getRelevantVolumeFraction(); + anID = aMaskVoxel3.getVoxelGridID(); + CHECK_EQUAL(aFraction, aMaskVoxel3.getProportionInStr()); + CHECK_EQUAL(aMaskVoxel3.getProportionInStr(), aMaskVoxel3.getRelevantVolumeFraction()); + aFraction = 0.84; + CHECK_NO_THROW(aMaskVoxel3.setProportionInStr(aFraction)); + CHECK_THROW_EXPLICIT(aMaskVoxel3.setProportionInStr(-aFraction), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(aMaskVoxel3.setProportionInStr(aFraction+2), core::InvalidParameterException); + aMaskVoxel3.setProportionInStr(aFraction); + CHECK_EQUAL(anID, aMaskVoxel3.getVoxelGridID()); + CHECK_EQUAL(aFraction, aMaskVoxel3.getRelevantVolumeFraction()); + + //4) test operators "==" + CHECK(!(aMaskVoxel2 == aMaskVoxel3)); //not equal + core::MaskVoxel aMaskVoxel4(aMaskVoxel3.getVoxelGridID()); + CHECK(!(aMaskVoxel4 == aMaskVoxel3)); //equal ID, but unequal volume fraction -> not equal + aMaskVoxel4.setRelevantVolumeFraction(aMaskVoxel3.getRelevantVolumeFraction()); + CHECK_EQUAL(aMaskVoxel4, aMaskVoxel3); //equal + aMaskVoxel2.setRelevantVolumeFraction(aMaskVoxel3.getRelevantVolumeFraction()); + CHECK(!(aMaskVoxel2 == aMaskVoxel3)); //no equal ID -> not equal + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing + }//end namespace rttb + diff --git a/testing/core/StrVectorStructureSetGeneratorTest.cpp b/testing/core/StrVectorStructureSetGeneratorTest.cpp new file mode 100644 index 0000000..765acf8 --- /dev/null +++ b/testing/core/StrVectorStructureSetGeneratorTest.cpp @@ -0,0 +1,80 @@ +// ----------------------------------------------------------------------- +// 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 "rttbStrVectorStructureSetGenerator.h" +#include "DummyStructure.h" +#include "DummyDoseAccessor.h" +#include "rttbInvalidParameterException.h" +#include "rttbStructure.h" +#include "rttbStructureSet.h" + +namespace rttb{ + namespace testing{ + + /*! @brief StructureTest - tests the API for Structure + 1) constructors + 2) get/setXX + */ + int StrVectorStructureSetGeneratorTest(int argc, char* argv[] ) + { + typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + + typedef core::StructureSet::StructTypePointer StructTypePointer; + + PREPARE_DEFAULT_TEST_REPORTING; + + //1) empty structure vector + std::vector strVector; + + CHECK_NO_THROW(core::StrVectorStructureSetGenerator generator1(strVector)); + CHECK_NO_THROW(core::StrVectorStructureSetGenerator(strVector).generateStructureSet()); + CHECK_EQUAL(core::StrVectorStructureSetGenerator(strVector).generateStructureSet()->getNumberOfStructures(),0); + CHECK_THROW_EXPLICIT(core::StrVectorStructureSetGenerator(strVector).generateStructureSet()->getStructure(0),core::InvalidParameterException); + + + //1) dummy structure + boost::shared_ptr spTestDoseAccessor = + boost::make_shared(); + + DummyStructure myStructGenerator(spTestDoseAccessor->getGeometricInfo()); + GridIndexType zPlane = 4; + core::Structure rect = myStructGenerator.CreateRectangularStructureCentered(zPlane); + StructTypePointer rectStrPtr=boost::make_shared(rect); + + strVector.push_back(rectStrPtr); + + CHECK_NO_THROW(core::StrVectorStructureSetGenerator generator2(strVector)); + CHECK_NO_THROW(core::StrVectorStructureSetGenerator(strVector).generateStructureSet()); + CHECK_EQUAL(core::StrVectorStructureSetGenerator(strVector).generateStructureSet()->getNumberOfStructures(),1); + CHECK_NO_THROW(core::StrVectorStructureSetGenerator(strVector).generateStructureSet()->getStructure(0)); + + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb \ No newline at end of file diff --git a/testing/core/StructureTest.cpp b/testing/core/StructureTest.cpp new file mode 100644 index 0000000..b46361e --- /dev/null +++ b/testing/core/StructureTest.cpp @@ -0,0 +1,129 @@ +// ----------------------------------------------------------------------- +// 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++) + { + CHECK_EQUAL(it->size(),it2->size()); + PolygonType::iterator pit = it->begin(); + PolygonType::iterator pit2 = it2->begin(); + for(;pit!=it->end();pit++) + { + CHECK_EQUAL(*(pit),*(pit2)); + pit2++; + } + 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++) + { + CHECK_EQUAL(it->size(),it3->size()); + PolygonType::iterator pit = it->begin(); + PolygonType::iterator pit3 = it3->begin(); + for(;pit!=it->end();pit++) + { + CHECK_EQUAL(*(pit),*(pit3)); + pit3++; + } + 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 \ No newline at end of file diff --git a/testing/core/files.cmake b/testing/core/files.cmake new file mode 100644 index 0000000..5951c5e --- /dev/null +++ b/testing/core/files.cmake @@ -0,0 +1,27 @@ +SET(CPP_FILES + rttbCoreTests.cpp + DummyDoseAccessor.cpp + DummyMaskAccessor.cpp + DummyMutableDoseAccessor.cpp + DummyDVHGenerator.cpp + DummyStructure.cpp + CreateTestStructure.cpp + StructureTest.cpp + GeometricInfoTest.cpp + MaskVoxelTest.cpp + GenericDoseIteratorTest.cpp + GenericMaskedDoseIteratorTest.cpp + DVHCalculatorTest.cpp + DVHTest.cpp + DVHSetTest.cpp + StrVectorStructureSetGeneratorTest.cpp + ) + +SET(H_FILES + DummyStructure.h + CreateTestStructure.h + DummyDoseAccessor.h + DummyMaskAccessor.h + DummyMutableDoseAccessor.h + DummyDVHGenerator.h +) diff --git a/testing/core/rttbCoreHeaderTest.cpp b/testing/core/rttbCoreHeaderTest.cpp new file mode 100644 index 0000000..5bfdd22 --- /dev/null +++ b/testing/core/rttbCoreHeaderTest.cpp @@ -0,0 +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) +*/ +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include + +#include "../../code/core/rttbBaseType_NEW.h" +#include "../../code/core/rttbClinicalTrial.h" +#include "../../code/core/rttbDoseAccessorInterface_NEW.h" +#include "../../code/core/rttbDVH.h" +#include "../../code/core/rttbDVHCalculator.h" +#include "../../code/core/rttbDVHGeneratorInterface.h" +#include "../../code/core/rttbDVHSet.h" +#include "../../code/core/rttbException.h" +#include "../../code/core/rttbField.h" +#include "../../code/core/rttbFraction.h" +#include "../../code/core/rttbFractionManager.h" +#include "../../code/core/rttbGenericDoseIterator_NEW.h" +#include "../../code/core/rttbGenericMaskedDoseIterator_NEW.h" +#include "../../code/core/rttbGeometricInfo_NEW.h" +#include "../../code/core/rttbImage.h" +#include "../../code/core/rttbIndexConversionInterface_NEW.h" +#include "../../code/core/rttbIndexOutOfBoundsException.h" +#include "../../code/core/rttbInvalidDoseException.h" +#include "../../code/core/rttbInvalidParameterException.h" +#include "../../code/core/rttbMaskAccessorInterface_NEW.h" +#include "../../code/core/rttbMaskedDoseIteratorInterface_NEW.h" +#include "../../code/core/rttbMaskVoxel_NEW.h" +#include "../../code/core/rttbNullPointerException.h" +#include "../../code/core/rttbPatient.h" +#include "../../code/core/rttbPhysicalInfo.h" +#include "../../code/core/rttbPlan.h" +#include "../../code/core/rttbSelfIntersectingStructureException.h" +#include "../../code/core/rttbStructure.h" +#include "../../code/core/rttbStructureSetInterface.h" +#include "../../code/core/rttbTherapy.h" +#include "../../code/core/rttbTreatmentCourse.h" + + +int main ( int , char* ) +{ + + return EXIT_SUCCESS; +} + diff --git a/testing/core/rttbCoreTests.cpp b/testing/core/rttbCoreTests.cpp new file mode 100644 index 0000000..78d20e6 --- /dev/null +++ b/testing/core/rttbCoreTests.cpp @@ -0,0 +1,69 @@ +// ----------------------------------------------------------------------- +// 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 + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include "litMultiTestsMain.h" + +namespace rttb{ + namespace testing{ + + void registerTests() + { + LIT_REGISTER_TEST(GeometricInfoTest); + LIT_REGISTER_TEST(MaskVoxelTest); + LIT_REGISTER_TEST(GenericDoseIteratorTest); + LIT_REGISTER_TEST(GenericMaskedDoseIteratorTest); + LIT_REGISTER_TEST(DVHCalculatorTest); + LIT_REGISTER_TEST(DVHTest); + LIT_REGISTER_TEST(DVHSetTest); + LIT_REGISTER_TEST(GeometricInfoTest); + LIT_REGISTER_TEST(MaskVoxelTest); + LIT_REGISTER_TEST(GenericDoseIteratorTest); + LIT_REGISTER_TEST(StructureTest); + LIT_REGISTER_TEST(StrVectorStructureSetGeneratorTest); + } + } + } + +int main(int argc, char* argv[]) + { + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + + catch(...) + { + result = -1; + } + + return result; + } diff --git a/testing/examples/CMakeLists.txt b/testing/examples/CMakeLists.txt new file mode 100644 index 0000000..fda6b71 --- /dev/null +++ b/testing/examples/CMakeLists.txt @@ -0,0 +1,37 @@ +#----------------------------------------------------------------------------- +# 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}/Virtuos/MPM_LR_ah/dvh_diff_trunk6.txt" +"${TEST_DATA_ROOT}/Virtuos/MPM_LR_ah/dvh_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}/DICOM/TestDose/LinearIncrease3D.dcm") +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") + +RTTB_CREATE_TEST_MODULE(rttbExamples DEPENDS RTTBCore RTTBAlgorithms RTTBMasks RTTBIndices RTTBDicomIO RTTBOtherIO RTTBModels PACKAGE_DEPENDS Boost Litmus DCMTK) + diff --git a/testing/examples/DVHCalculatorComparisonTest.cpp b/testing/examples/DVHCalculatorComparisonTest.cpp new file mode 100644 index 0000000..5ce3784 --- /dev/null +++ b/testing/examples/DVHCalculatorComparisonTest.cpp @@ -0,0 +1,315 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include + +#include "litCheckMacros.h" + + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmrt/drtdose.h" +#include "dcmtk/dcmrt/drtstrct.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" + + +namespace rttb{ + + namespace testing{ + + /*! @brief DVHCalculatorTest. + 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! + + This test can be used to get more detailed information, but it will always fail, because differences in voxelization accuracy, + especially the ones caused by the change from double to float precission will not cause considerable deviations in the structure + sizes, which correspond to considerable differences in the calculated DVHs. + + Even in double precission differences up to 0.005 between values from old and new implementation can occure. + */ + + int DVHCalculatorComparisonTest(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: dose2 file name + // 4: dose3 file name + + std::string RTSTRUCT_FILENAME; + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + std::string RTDOSE3_FILENAME; + std::string COMPARISON_DVH_FOLDER; + + + if (argc>1) + { + RTSTRUCT_FILENAME = argv[1]; + } + if (argc>2) + { + RTDOSE_FILENAME = argv[2]; + } + if (argc>3) + { + RTDOSE2_FILENAME = argv[3]; + } + if (argc>4) + { + RTDOSE3_FILENAME = argv[4]; + } + if (argc>5) + { + COMPARISON_DVH_FOLDER = argv[5]; + } + + + OFCondition status; + DcmFileFormat fileformat; + + /* read dicom-rt dose */ + ::DRTDoseIOD rtdose1; + 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; + + ::DRTDoseIOD rtdose2; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator2(RTDOSE2_FILENAME.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + + ::DRTDoseIOD rtdose3; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator3(RTDOSE3_FILENAME.c_str()); + DoseAccessorPointer doseAccessor3(doseAccessorGenerator3.generateDoseAccessor()); + + + double maxDifference = 0; + double difference = 0; + double minDifference = 1000; + clock_t start(clock()); + if(rtStructureSet->getNumberOfStructures()>0){ + for(int j=0;jgetNumberOfStructures();j++){ + + //create MaskAccessor + boost::shared_ptr spOTBMaskAccessor = + boost::make_shared(rtStructureSet->getStructure(j), doseAccessor1->getGeometricInfo()); + spOTBMaskAccessor->updateMask(); + MaskAccessorPointer spMaskAccessor(spOTBMaskAccessor); + //create corresponding MaskedDoseIterator + boost::shared_ptr spMaskedDoseIteratorTmp = + boost::make_shared(spMaskAccessor, doseAccessor1); + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + //store MaskAccessor + rtStructSetMaskAccessorVec.push_back(spMaskAccessor); + + rttb::core::DVHCalculator calc(spMaskedDoseIterator,(rtStructureSet->getStructure(j))->getUID(),doseAccessor1->getDoseUID()); + std::string dvhFileName = "dvh1"; + std::string label = (rtStructureSet->getStructure(j))->getLabel(); + dvhFileName.append(label); + if (dvhFileName.find("/")!=std::string::npos){ + dvhFileName.replace(dvhFileName.find("/"),1,""); + } + std::cout << "=== Dose 1: "<maxDifference){ + maxDifference = difference; + } + } + } + } + clock_t finish(clock()); + std::cout << std::setprecision (20) <<"max(difference): "<< maxDifference < spMaskedDoseIteratorTmp = + boost::make_shared(rtStructSetMaskAccessorVec.at(j), doseAccessor2); + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + + rttb::core::DVHCalculator calc(spMaskedDoseIterator,(rtStructureSet->getStructure(j))->getUID(),doseAccessor2->getDoseUID()); + std::string dvhFileName = "dvh2"; + std::string label = (rtStructureSet->getStructure(j))->getLabel(); + dvhFileName.append(label); + if (dvhFileName.find("/")!=std::string::npos){ + dvhFileName.replace(dvhFileName.find("/"),1,""); + } + std::cout << "=== Dose 2: "< 10){ + std::cout <<"large difference: "<< abs(*(itOrig)-*(it))<<" = "<<*(itOrig)<<" - "<<*(it)<maxDifference){ + maxDifference = difference; + } + } + } + clock_t finish2(clock()); + + std::cout << std::setprecision (20) <<"max(difference): "<< maxDifference < spMaskedDoseIteratorTmp = + boost::make_shared(rtStructSetMaskAccessorVec.at(j), doseAccessor3); + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + + rttb::core::DVHCalculator calc(spMaskedDoseIterator,(rtStructureSet->getStructure(j))->getUID(),doseAccessor3->getDoseUID()); + std::string dvhFileName = "dvh3"; + std::string label = (rtStructureSet->getStructure(j))->getLabel(); + dvhFileName.append(label); + if (dvhFileName.find("/")!=std::string::npos){ + dvhFileName.replace(dvhFileName.find("/"),1,""); + } + std::cout << "=== Dose 3: "< 10){ + std::cout <<"large difference: "<< abs(*(itOrig)-*(it))<<" = "<<*(itOrig)<<" - "<<*(it)<maxDifference){ + maxDifference = difference; + } + } + } + clock_t finish3(clock()); + + std::cout << std::setprecision (20) <<"max(difference): "<< maxDifference < +#include + +#include + +#include "litCheckMacros.h" + + +#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ + +#include "dcmtk/dcmrt/drtdose.h" +#include "dcmtk/dcmrt/drtstrct.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" + + +namespace rttb{ + + namespace testing{ + + /*! @brief DVHCalculatorTest. + 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 DVHCalculatorExampleTest(int argc, char* argv[] ) + { + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + typedef core::GenericMaskedDoseIterator::MaskAccessorPointer MaskAccessorPointer; + typedef core::DVHCalculator::DoseIteratorPointer DoseIteratorPointer; + typedef core::DVHCalculator::MaskedDoseIteratorPointer MaskedDoseIteratorPointer; + typedef masks::OTBMaskAccessor::StructTypePointer StructTypePointer; + typedef core::DVH::DVHPointer DVHPointer; + typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: 1: structure file name + // 2: dose1 file name + // 3: dose2 file name + // 4: dose3 file name + + std::string RTSTRUCT_FILENAME; + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + std::string RTDOSE3_FILENAME; + + + if (argc>1) + { + RTSTRUCT_FILENAME = argv[1]; + } + if (argc>2) + { + RTDOSE_FILENAME = argv[2]; + } + if (argc>3) + { + RTDOSE2_FILENAME = argv[3]; + } + if (argc>4) + { + RTDOSE3_FILENAME = argv[4]; + } + + + OFCondition status; + DcmFileFormat fileformat; + + // read dicom-rt dose + ::DRTDoseIOD rtdose1; + 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(); + + //storeage for mask accessors to reduce time spent on voxelization (perform only once) + std::vector rtStructSetMaskAccessorVec; + + ::DRTDoseIOD rtdose2; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator2(RTDOSE2_FILENAME.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + + ::DRTDoseIOD rtdose3; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator3(RTDOSE3_FILENAME.c_str()); + DoseAccessorPointer doseAccessor3(doseAccessorGenerator3.generateDoseAccessor()); + + + //start evaluation + clock_t start(clock()); + if(rtStructureSet->getNumberOfStructures()>0){ + for(int j=0;jgetNumberOfStructures();j++){ + + //create MaskAccessor for each structure + boost::shared_ptr spOTBMaskAccessor = + boost::make_shared(rtStructureSet->getStructure(j), doseAccessor1->getGeometricInfo()); + spOTBMaskAccessor->updateMask(); + MaskAccessorPointer spMaskAccessor(spOTBMaskAccessor); + //create corresponding MaskedDoseIterator + boost::shared_ptr spMaskedDoseIteratorTmp = + boost::make_shared(spMaskAccessor, doseAccessor1); + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + //store MaskAccessor for each structure (later reuse) + rtStructSetMaskAccessorVec.push_back(spMaskAccessor); + + //calculate DVH + rttb::core::DVHCalculator calc(spMaskedDoseIterator,(rtStructureSet->getStructure(j))->getUID(),doseAccessor1->getDoseUID()); + rttb::core::DVH dvh=*(calc.generateDVH()); + + + /*//DEBUG OUTPUT + std::cout << "=== Dose 1 Structure "< spMaskedDoseIteratorTmp = + boost::make_shared(rtStructSetMaskAccessorVec.at(j), doseAccessor2); + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + + //calculate DVH + rttb::core::DVHCalculator calc(spMaskedDoseIterator,(rtStructureSet->getStructure(j))->getUID(),doseAccessor2->getDoseUID()); + rttb::core::DVH dvh=*(calc.generateDVH()); + + + /*//DEBUG OUTPUT + std::cout << "=== Dose 2 Structure "< spMaskedDoseIteratorTmp = + boost::make_shared(rtStructSetMaskAccessorVec.at(j), doseAccessor3); + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + + //calculate DVH + rttb::core::DVHCalculator calc(spMaskedDoseIterator,(rtStructureSet->getStructure(j))->getUID(),doseAccessor3->getDoseUID()); + rttb::core::DVH dvh=*(calc.generateDVH()); + + + /*//DEBUG OUTPUT + std::cout << "=== Dose 3 Structure "< + +#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 = 1; + + 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.getAlpahBeta()); + 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++){ + if ((*it).first < 62){ + CHECK_EQUAL(0,(*it).second); + } + else if ((*it).first >114){ + 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.getAlpahBeta()); + CHECK_EQUAL(roh,tcplq.getRho()); + if(tcplq.init()){ + CHECK_CLOSE(1.58632626255825960000e-002,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.getAlpahBeta()); + CHECK_EQUAL(roh,tcplq.getRho()); + if(tcplq.init()){ + CHECK_CLOSE(9.99338781766346610000e-001,tcplq.getValue(),tolerance); + } + + alpha = 0.9; + alphaBeta = 1; + tcplq.setAlpha(alpha); + tcplq.setAlphaBeta(alphaBeta); + tcplq.setRho(roh); + CHECK_EQUAL(alpha,tcplq.getAlphaMean()); + CHECK_EQUAL(alphaBeta,tcplq.getAlpahBeta()); + 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.getAlpahBeta()); + 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 d50Varianz=2; + 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 \ No newline at end of file diff --git a/testing/examples/RTBioModelScatterPlotExampleTest.cpp b/testing/examples/RTBioModelScatterPlotExampleTest.cpp new file mode 100644 index 0000000..f196097 --- /dev/null +++ b/testing/examples/RTBioModelScatterPlotExampleTest.cpp @@ -0,0 +1,392 @@ +// ----------------------------------------------------------------------- +// 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/models/RTBioModelTest.cpp $ +*/ + +// 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 "../models/rttbScatterTester.h" +#include "rttbInvalidParameterException.h" + +namespace rttb{ + namespace testing{ + + + /*! @brief RTBioModelScatterPlotExampleTest. + calculating Curves and Scatterplots for TCP and NTCP models. + The values on curve and scatterplot need to be similar for similar dose values. + The range of difference is given by the variance used to generate the scatter. + + WARNING: The values for comparison need to be adjusted if the input files are changed! + */ + int RTBioModelScatterPlotExampleTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + typedef rttb::models::CurveDataType CurveDataType; + typedef rttb::models::ScatterPlotType 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: TV dvh file name + + std::string DVH_FILENAME_PTV; + std::string DVH_FILENAME_NT1; + std::string DVH_FILENAME_TV_TEST; + + if (argc>1) + { + DVH_FILENAME_PTV = argv[1]; + } + if (argc>2) + { + DVH_FILENAME_NT1 = argv[2]; + } + if (argc>3) + { + DVH_FILENAME_TV_TEST = argv[3]; + } + + //DVH PTV + rttb::io::other::DVHTxtFileReader dvhReader=rttb::io::other::DVHTxtFileReader(DVH_FILENAME_PTV); + DVHPointer dvhPtr=dvhReader.generateDVH(); + + 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 = 1; + + DoseTypeGy normalizationDose = 68; + + rttb::models::TCPLQModel tcplq=rttb::models::TCPLQModel(dvhPtr,alpha, beta, roh, numFractions); + + CHECK_NO_THROW(tcplq.init()); + + CurveDataType curve=models::getCurveDoseVSBioModel(tcplq,normalizationDose); + + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, 0, normalizationDose,100)); + ScatterPlotType tcpScatter = models::getScatterPlotVary1Parameter(tcplq, 0, alpha, 0, normalizationDose,100); + CHECK_EQUAL(100,tcpScatter.size()); + + ScatterTester scatterCompare(curve, tcpScatter); + CHECK_TESTER(scatterCompare); + + //test also with other parameter + tcpScatter = models::getScatterPlotVary1Parameter(tcplq, 3, roh, 0, normalizationDose,100); + + scatterCompare.setCompareScatter(tcpScatter); + CHECK_TESTER(scatterCompare); + + std::vector paramIdVec; + models::BioModel::ParamVectorType meanVec; + models::BioModel::ParamVectorType meanVecTest; meanVecTest.push_back(alpha); + models::BioModel::ParamVectorType varianceVec; + //"alphaMean":0,"alphaVariance":1,"alpha_beta":2, "rho":3 + paramIdVec.push_back(0); meanVec.push_back(tcplq.getAlphaMean()); varianceVec.push_back(0); + //setting parameter 1 will change the resulting scatter plot dramatically - is it meant to? + //this is unexpected since the value was taken from the original calculation + //paramIdVec.push_back(1); meanVec.push_back(tcplq.getAlphaVariance()); varianceVec.push_back(0); + paramIdVec.push_back(2); meanVec.push_back(tcplq.getAlpahBeta()); varianceVec.push_back(0); + paramIdVec.push_back(3); meanVec.push_back(tcplq.getRho()); varianceVec.push_back(0); + + CHECK_THROW_EXPLICIT(models::getScatterPlotVaryParameters(tcplq, paramIdVec, meanVecTest, varianceVec, normalizationDose,50),core::InvalidParameterException); + ScatterPlotType scatterVary = models::getScatterPlotVaryParameters(tcplq, paramIdVec, meanVec, varianceVec, normalizationDose,50); + CHECK_EQUAL(50,scatterVary.size()); + + scatterCompare.setCompareScatter(scatterVary); + CHECK_TESTER(scatterCompare); + + models::BioModelParamType variance = 0.00015; + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, variance, normalizationDose,100)); + tcpScatter = models::getScatterPlotVary1Parameter(tcplq, 0, alpha, variance, normalizationDose,100); + + scatterCompare.setVariance(variance); + scatterCompare.setCompareScatter(tcpScatter); + //allow 5% of the points to deviate more + scatterCompare.setAllowExceptions(true); + CHECK_TESTER(scatterCompare); + + //test also with other parameter + tcpScatter = models::getScatterPlotVary1Parameter(tcplq, 3, roh, variance, normalizationDose,100); + scatterCompare.setCompareScatter(tcpScatter); + //allow 5% of the points to deviate more + scatterCompare.setAllowExceptions(true); + CHECK_TESTER(scatterCompare); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(tcplq.getAlphaMean()); varianceVec.push_back(variance); + //paramIdVec.push_back(1); meanVec.push_back(tcplq.getAlphaVariance()); varianceVec.push_back(variance); + paramIdVec.push_back(2); meanVec.push_back(tcplq.getAlpahBeta()); varianceVec.push_back(variance); + paramIdVec.push_back(3); meanVec.push_back(tcplq.getRho()); varianceVec.push_back(variance); + + scatterVary = models::getScatterPlotVaryParameters(tcplq, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + //allow 5% of the points to deviate more + scatterCompare.setAllowExceptions(true); + CHECK_TESTER(scatterCompare); + + models::BioModelParamType alphaBeta = 10; + tcplq.setParameters(alpha,alphaBeta,roh,0.08); + tcplq.init(); + + normalizationDose = 40; + curve=models::getCurveDoseVSBioModel(tcplq,normalizationDose); + + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, 0, normalizationDose,100)); + tcpScatter = models::getScatterPlotVary1Parameter(tcplq, 0, alpha, 0, normalizationDose,100); + + scatterCompare.setReferenceCurve(curve); + scatterCompare.setVariance(0); + //do not allow larger deviations + scatterCompare.setAllowExceptions(false); + scatterCompare.setCompareScatter(tcpScatter); + CHECK_TESTER(scatterCompare); + + variance = 0.25; + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq, 0, alpha, variance, normalizationDose,100)); + tcpScatter = models::getScatterPlotVary1Parameter(tcplq, 0, alpha, variance, normalizationDose,100); + + scatterCompare.setCompareScatter(tcpScatter); + scatterCompare.setVariance(variance); + //allow 5% of the points to deviate more + scatterCompare.setAllowExceptions(true); + CHECK_TESTER(scatterCompare); + + + /*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_NO_THROW(tcplq_test.init()); + normalizationDose = 60; + curve=models::getCurveDoseVSBioModel(tcplq_test,normalizationDose); + + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq_test, 0, alpha, 0, normalizationDose,100)); + tcpScatter = models::getScatterPlotVary1Parameter(tcplq_test, 0, alpha, 0, normalizationDose,100); + + scatterCompare.setReferenceCurve(curve); + scatterCompare.setVariance(0); + scatterCompare.setCompareScatter(tcpScatter); + CHECK_TESTER(scatterCompare); + + //test also with other parameter + tcpScatter = models::getScatterPlotVary1Parameter(tcplq_test, 3, roh, 0, normalizationDose,100); + scatterCompare.setCompareScatter(tcpScatter); + CHECK_TESTER(scatterCompare); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(tcplq_test.getAlphaMean()); varianceVec.push_back(0); + //paramIdVec.push_back(1); meanVec.push_back(tcplq_test.getAlphaVariance()); varianceVec.push_back(0); + paramIdVec.push_back(2); meanVec.push_back(tcplq_test.getAlpahBeta()); varianceVec.push_back(0); + paramIdVec.push_back(3); meanVec.push_back(tcplq_test.getRho()); varianceVec.push_back(0); + + scatterVary = models::getScatterPlotVaryParameters(tcplq_test, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + CHECK_TESTER(scatterCompare); + + variance = 0.00025; + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq_test, 0, alpha, variance, normalizationDose,100)); + tcpScatter = models::getScatterPlotVary1Parameter(tcplq_test, 0, alpha, variance, normalizationDose,100); + + scatterCompare.setCompareScatter(tcpScatter); + scatterCompare.setVariance(variance); + CHECK_TESTER(scatterCompare); + + //test also with other parameter + tcpScatter = models::getScatterPlotVary1Parameter(tcplq_test, 3, roh, variance, normalizationDose,100); + scatterCompare.setCompareScatter(tcpScatter); + scatterCompare.setAllowExceptions(true); + CHECK_TESTER(scatterCompare); + scatterCompare.setAllowExceptions(false); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(tcplq_test.getAlphaMean()); varianceVec.push_back(variance); + //paramIdVec.push_back(1); meanVec.push_back(tcplq_test.getAlphaVariance()); varianceVec.push_back(variance); + paramIdVec.push_back(2); meanVec.push_back(tcplq_test.getAlpahBeta()); varianceVec.push_back(variance); + paramIdVec.push_back(3); meanVec.push_back(tcplq_test.getRho()); varianceVec.push_back(variance); + + scatterVary = models::getScatterPlotVaryParameters(tcplq_test, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + //allow 5% of the points to deviate more + scatterCompare.setAllowExceptions(true); + CHECK_TESTER(scatterCompare); + + //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; + lkb.setDVH(dvhPtr2); + lkb.setA(aVal); + lkb.setM(mVal); + lkb.setD50(d50Val); + CHECK_NO_THROW(lkb.init()); + + normalizationDose = 60; + curve=models::getCurveDoseVSBioModel(lkb,normalizationDose); + + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(lkb, 2, aVal, 0, normalizationDose,100)); + ScatterPlotType scatter = models::getScatterPlotVary1Parameter(lkb, 2, aVal, 0, normalizationDose,100); + + scatterCompare.setReferenceCurve(curve); + scatterCompare.setVariance(0); + scatterCompare.setCompareScatter(scatter); + CHECK_TESTER(scatterCompare); + + //"d50":0,"m":1,"a":2 + //test also with other parameter + scatter = models::getScatterPlotVary1Parameter(lkb, 0, d50Val, 0, normalizationDose,100); + scatterCompare.setCompareScatter(scatter); + CHECK_TESTER(scatterCompare); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(lkb.getD50()); varianceVec.push_back(0); + paramIdVec.push_back(1); meanVec.push_back(lkb.getM()); varianceVec.push_back(0); + paramIdVec.push_back(2); meanVec.push_back(lkb.getA()); varianceVec.push_back(0); + + scatterVary = models::getScatterPlotVaryParameters(lkb, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + CHECK_TESTER(scatterCompare); + + variance = 0.00025; + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(lkb, 2, aVal, variance, normalizationDose,100)); + scatter = models::getScatterPlotVary1Parameter(lkb, 2, aVal, variance, normalizationDose,100); + + scatterCompare.setCompareScatter(scatter); + scatterCompare.setVariance(variance); + CHECK_TESTER(scatterCompare); + + //test also with other parameter + scatter = models::getScatterPlotVary1Parameter(lkb, 0, d50Val, variance, normalizationDose,100); + scatterCompare.setCompareScatter(scatter); + CHECK_TESTER(scatterCompare); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(lkb.getD50()); varianceVec.push_back(variance); + paramIdVec.push_back(1); meanVec.push_back(lkb.getM()); varianceVec.push_back(variance); + paramIdVec.push_back(2); meanVec.push_back(lkb.getA()); varianceVec.push_back(variance); + + scatterVary = models::getScatterPlotVaryParameters(lkb, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + CHECK_TESTER(scatterCompare); + + //test RTNTCPRSModel + rttb::models::NTCPRSModel rs=rttb::models::NTCPRSModel(); + models::BioModelParamType gammaVal = 1.7; + models::BioModelParamType sVal = 1; + rs.setDVH(dvhPtr2); + rs.setD50(d50Val); + rs.setGamma(gammaVal); + rs.setS(sVal); + CHECK_NO_THROW(rs.init()); + + normalizationDose = 60; + curve=models::getCurveDoseVSBioModel(rs,normalizationDose); + + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(rs, 0, d50Val, 0, normalizationDose,100)); + scatter = models::getScatterPlotVary1Parameter(rs, 0, d50Val, 0, normalizationDose,100); + + scatterCompare.setReferenceCurve(curve); + scatterCompare.setVariance(0); + scatterCompare.setCompareScatter(scatter); + CHECK_TESTER(scatterCompare); + + //"d50":0,"gamma":1,"s":2 + //test also with other parameter + scatter = models::getScatterPlotVary1Parameter(rs, 1, gammaVal, 0, normalizationDose,100); + scatterCompare.setCompareScatter(scatter); + CHECK_TESTER(scatterCompare); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(rs.getD50()); varianceVec.push_back(0); + paramIdVec.push_back(1); meanVec.push_back(rs.getGamma()); varianceVec.push_back(0); + paramIdVec.push_back(2); meanVec.push_back(rs.getS()); varianceVec.push_back(0); + + scatterVary = models::getScatterPlotVaryParameters(rs, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + CHECK_TESTER(scatterCompare); + + variance = 0.0075; + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(rs, 0, d50Val, variance, normalizationDose,100)); + scatter = models::getScatterPlotVary1Parameter(rs, 0, d50Val, variance, normalizationDose,100); + + scatterCompare.setCompareScatter(scatter); + scatterCompare.setVariance(variance); + CHECK_TESTER(scatterCompare); + + //test also with other parameter + scatter = models::getScatterPlotVary1Parameter(rs, 2, sVal, variance, normalizationDose,100); + scatterCompare.setCompareScatter(scatter); + CHECK_TESTER(scatterCompare); + + paramIdVec.clear(); meanVec.clear(); varianceVec.clear(); + paramIdVec.push_back(0); meanVec.push_back(rs.getD50()); varianceVec.push_back(variance); + paramIdVec.push_back(1); meanVec.push_back(rs.getGamma()); varianceVec.push_back(variance); + paramIdVec.push_back(2); meanVec.push_back(rs.getS()); varianceVec.push_back(variance); + + scatterVary = models::getScatterPlotVaryParameters(rs, paramIdVec, meanVec, varianceVec, normalizationDose,50); + + scatterCompare.setCompareScatter(scatterVary); + CHECK_TESTER(scatterCompare); + + RETURN_AND_REPORT_TEST_SUCCESS; + + } + + }//testing +}//rttb \ No newline at end of file diff --git a/testing/examples/RTDVHCalculatorTest.cpp b/testing/examples/RTDVHCalculatorTest.cpp new file mode 100644 index 0000000..59ddd27 --- /dev/null +++ b/testing/examples/RTDVHCalculatorTest.cpp @@ -0,0 +1,252 @@ +// ----------------------------------------------------------------------- +// 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()< mditVector; + ResultListPointer resultListMax; + ResultListPointer resultListMin; + if(rtstrset.getNumberOfStructures()>0){ + for(int j=0;j(*(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"<getUID(),mddit->getDoseUID()); + rttb::core::DVH dvh=*(calc.getDVH()); + std::cout <<"DVH(mask) max: "<< dvh.getMaximum()<< "in "<getVolume()<<" cm^3"<0){ + for(int j=0;j(*(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"<getUID(),mddit->getDoseUID()); + rttb::core::DVH dvh2=*(calc2.getDVH()); + std::cout <<"DVH(mask) max: "<< dvh2.getMaximum()<< "in "<getVolume()<<" cm^3"<getUID(),ddit2->getDoseUID()); + rttb::core::DVH dvh=*(calc.getDVH()); + std::cout <<"DVH(dose) max: "<< dvh.getMaximum()< ../algorithms + clock_t startStats(clock()); + std::cout <<"Statistics: "<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)< 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 comulative 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)); + rttb::DoseTypeGy dTest = dvh->getDx(100); + CHECK_CLOSE(0.0131,dvh->getDx(100),errorConstant+errorConstant*10); + CHECK_CLOSE(0.013,dvh->getDx(249),errorConstant); + dTest = dvh->getDx(249); + CHECK_EQUAL(0,dvh->getDx(251)); + dTest = dvh->getDx(251); + + //test statistics (absolute comulative data) + CHECK_EQUAL(2000,dvh->calcCumulativeDVH(false).at(0)); + CHECK_EQUAL(0,dvh->getVx(0.014)); + CHECK_EQUAL(250,dvh->getVx(0.01)); + dTest = dvh->getDx(100); + CHECK_CLOSE(0.0131,dvh->getDx(100),errorConstant+errorConstant*10); + CHECK_CLOSE(0.013,dvh->getDx(249),errorConstant); + dTest = dvh->getDx(249); + CHECK_EQUAL(0,dvh->getDx(251)); + dTest = dvh->getDx(251); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/examples/RTDoseIndexTest.cpp b/testing/examples/RTDoseIndexTest.cpp new file mode 100644 index 0000000..514679f --- /dev/null +++ b/testing/examples/RTDoseIndexTest.cpp @@ -0,0 +1,241 @@ +// ----------------------------------------------------------------------- +// 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/algorithms/RTDoseIndexTest.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 "rttbDoseIndex.h" +#include "rttbDVHSet.h" +#include "rttbDVHTxtFileReader.h" +#include "rttbBaseType.h" +#include "rttbNullPointerException.h" +#include "rttbConformalIndex.h" +#include "rttbConformationNumber.h" +#include "rttbConformityIndex.h" +#include "rttbCoverageIndex.h" +#include "rttbHomogeneityIndex.h" +#include "rttbException.h" +#include "rttbInvalidParameterException.h" + +#include + + +namespace rttb{ + namespace testing{ + /*! @brief DoseIndexTest. ConformationNumber ConformalIndex ConformityIndex CoverageIndex HomogeneityIndex are tested. + test dvh: deltaV 0.125, deltaD 0.5 + 1. dvh TV: number of voxels 2900, maximum dose bin 133, dose bin 127~133 + 2. dvh HT1: number of voxels 5410, maximum dose bin 40, dose bin 0~2,40 + 3. dvh HT2: number of voxels 10210, maximum dose bin 50, dose bin 0~2,50 + 4. dvh HT3: number of voxels 1210, maximum dose bin 70, dose bin 0~2,70 + + 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 RTDoseIndexTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + //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 + + std::string DVH_FILENAME_PTV; + std::string DVH_FILENAME_NT1; + std::string DVH_FILENAME_NT2; + std::string DVH_FILENAME_NT3; + + + 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]; + } + + + /*test dvh: deltaV 0.125, deltaD 0.5*/ + /*dvh TV: number of voxels 2900, maximum dose bin 133, dose bin 127~133*/ + rttb::io::other::DVHTxtFileReader dvhReader=rttb::io::other::DVHTxtFileReader(DVH_FILENAME_PTV); + rttb::core::DVH dvhPTV=*(dvhReader.generateDVH()); + /*dvh HT1: number of voxels 5410, maximum dose bin 40, dose bin 0~2,40*/ + rttb::io::other::DVHTxtFileReader dvhReader1=rttb::io::other::DVHTxtFileReader(DVH_FILENAME_NT1); + core::DVH dvhNT1=*(dvhReader1.generateDVH()); + /*dvh HT2: number of voxels 10210, maximum dose bin 50, dose bin 0~2,50*/ + rttb::io::other::DVHTxtFileReader dvhReader2=rttb::io::other::DVHTxtFileReader(DVH_FILENAME_NT2); + core::DVH dvhNT2=*(dvhReader2.generateDVH()); + /*dvh HT3: number of voxels 1210, maximum dose bin 70, dose bin 0~2,70*/ + rttb::io::other::DVHTxtFileReader dvhReader3=rttb::io::other::DVHTxtFileReader(DVH_FILENAME_NT3); + core::DVH dvhNT3=*(dvhReader3.generateDVH()); + + std::vector dvhTVSet; + std::vector dvhHTSet; + dvhTVSet.push_back(dvhPTV); + dvhHTSet.push_back(dvhNT1); + dvhHTSet.push_back(dvhNT2); + dvhHTSet.push_back(dvhNT3); + + core::DVHSet dvhSet=core::DVHSet(dvhTVSet,dvhHTSet,"testStrSet",dvhPTV.getDoseID()); + + /*test exception*/ + indices::ConformalIndex test1=indices::ConformalIndex(NULL,0); + indices::ConformationNumber test2=indices::ConformationNumber(NULL,0); + indices::ConformityIndex test3=indices::ConformityIndex(NULL,0); + indices::CoverageIndex test4=indices::CoverageIndex(NULL,0); + indices::HomogeneityIndex test5=indices::HomogeneityIndex(NULL,0); + + CHECK_THROW_EXPLICIT(test1.init(),core::NullPointerException); + CHECK_THROW_EXPLICIT(test2.init(),core::NullPointerException); + CHECK_THROW_EXPLICIT(test3.init(),core::NullPointerException); + CHECK_THROW_EXPLICIT(test4.init(),core::NullPointerException); + CHECK_THROW_EXPLICIT(test5.init(),core::NullPointerException); + + + /*test exception for invalid reference dose*/ + test1=indices::ConformalIndex(&dvhSet,100); + test2=indices::ConformationNumber(&dvhSet,100); + test3=indices::ConformityIndex(&dvhSet,100); + test5=indices::HomogeneityIndex(&dvhSet,0); + + CHECK_THROW_EXPLICIT(test1.init(),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(test2.init(),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(test3.init(),core::InvalidParameterException); + CHECK_THROW_EXPLICIT(test5.init(),core::InvalidParameterException); + + /*test index calculation*/ + + /*RTConformationNumber */ + //PTV covered by reference dose 30 = the whole PTV =362.5; Volume of the referece 30=362.5+1.25 + indices::ConformationNumber cn=indices::ConformationNumber(&dvhSet, 30); + cn.init(); + //check if values are close. Equality is only achieved with double precission. + CHECK_CLOSE(362.5/363.75,cn.getValue(),errorConstant); + + //cn==1*TV0/V0=362.5/2466.25 + cn.setDoseReference(0); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(cn.getValue(),core::Exception); + cn.init(); + CHECK_CLOSE(362.5/2466.25,cn.getValue(),errorConstant); + + cn.setDoseReference(65); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(cn.getValue(),core::Exception); + cn.init(); + CHECK_CLOSE(2300/2900.0,cn.getValue(),errorConstant);//ref dose: 65 -> TVref=Vref -> cn=TVref/TV=2300/2900 + + CHECK_EQUAL(cn.getValue(),cn.getDoseIndexAt(0)); + CHECK_THROW_EXPLICIT(cn.getDoseIndexAt(1),core::InvalidParameterException); + + /*ConformalIndex */ + //HT 1 covered by ref=HT2 covered by ref=0, HT3 covered by ref=1.25 + indices::ConformalIndex coin=indices::ConformalIndex(&dvhSet, 30); + coin.init(); + CHECK_CLOSE((362.5/363.75)*(1-1.25/151.25),coin.getValue(),errorConstant); + + coin.setDoseReference(0); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(coin.getValue(),core::Exception); + coin.init(); + CHECK_EQUAL(0,coin.getValue()); + + coin.setDoseReference(65); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(coin.getValue(),core::Exception); + coin.init(); + CHECK_CLOSE(2300/2900.0,coin.getValue(),errorConstant);//ref dose: 65 -> Vref=0 for all HT -> cn=cn*(1-0)=cn + + CHECK_EQUAL(coin.getValue(),coin.getDoseIndexAt(0)); + CHECK_THROW_EXPLICIT(coin.getDoseIndexAt(1),core::InvalidParameterException); + + /*ConformityIndex */ + indices::ConformityIndex ci=indices::ConformityIndex(&dvhSet, 30); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(ci.getValue(),core::Exception); + ci.init(); + CHECK_CLOSE(362.5/363.75,ci.getValue(),errorConstant); + + ci.setDoseReference(65); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(ci.getValue(),core::Exception); + ci.init(); + CHECK_CLOSE(2300/2900.0,ci.getValue(),errorConstant);//ref dose: 65->ci=2300/2900*1*1*1 + + CHECK_EQUAL(ci.getValue(),ci.getDoseIndexAt(0)); + CHECK_THROW_EXPLICIT(ci.getDoseIndexAt(1),core::InvalidParameterException); + + /*CoverageIndex*/ + indices::CoverageIndex coverageI=indices::CoverageIndex(&dvhSet, 30); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(coverageI.getValue(),core::Exception); + coverageI.init(); + CHECK_CLOSE(362.5/362.5,coverageI.getValue(),errorConstant);//ref dose: 30 -> coverage index=1 + + coverageI.setDoseReference(65); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(coverageI.getValue(),core::Exception); + coverageI.init(); + CHECK_CLOSE(2300/2900.0,coverageI.getValue(),errorConstant);//ref dose: 65->coverage index=2300/2900 + + CHECK_EQUAL(coverageI.getValue(),coverageI.getDoseIndexAt(0)); + CHECK_THROW_EXPLICIT(coverageI.getDoseIndexAt(1),core::InvalidParameterException); + + + /*HomogeneityIndex TV max: 133*0.5=66.5, TV min: 127*0.5=63.5 -> hi=(66.5-63.5)/30*/ + indices::HomogeneityIndex hi=indices::HomogeneityIndex(&dvhSet, 30); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(hi.getValue(),core::Exception); + hi.init(); + CHECK_CLOSE(3/30.0,hi.getValue(),errorConstant); + + hi.setDoseReference(65); + //if not init, throw Exception + CHECK_THROW_EXPLICIT(hi.getValue(),core::Exception); + hi.init(); + CHECK_CLOSE(3/65.0,hi.getValue(),errorConstant); + + CHECK_EQUAL(hi.getValue(),hi.getDoseIndexAt(0)); + CHECK_THROW_EXPLICIT(hi.getDoseIndexAt(1),core::InvalidParameterException); + + + RETURN_AND_REPORT_TEST_SUCCESS; + + + } + + }//testing +}//rttb \ No newline at end of file diff --git a/testing/examples/RTDoseStatisticsTest.cpp b/testing/examples/RTDoseStatisticsTest.cpp new file mode 100644 index 0000000..ed0359b --- /dev/null +++ b/testing/examples/RTDoseStatisticsTest.cpp @@ -0,0 +1,131 @@ +// ----------------------------------------------------------------------- +// 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/algorithms/RTDoseStatisticsTest.cpp $ +*/ + +// 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 "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ +#include "dcmtk/dcmrt/drtdose.h" +#include "dcmtk/dcmdata/dcfilefo.h" + +#include "litCheckMacros.h" +#include "rttbDoseStatistics.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbGenericDoseIterator.h" +#include "rttbNullPointerException.h" +#include "rttbBaseType.h" + +namespace rttb{ + namespace testing{ + + /*! @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!*/ + int RTDoseStatisticsTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + typedef core::GenericDoseIterator::DoseIteratorPointer DoseIteratorPointer; + typedef algorithms::DoseStatistics::ResultListPointer ResultListPointer; + + //ARGUMENTS: 1: dose1 file name + // 2: dose2 file name + + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + + if (argc>1) + { + RTDOSE_FILENAME = argv[1]; + } + if (argc>2) + { + RTDOSE2_FILENAME= argv[2]; + } + + + OFCondition status; + DcmFileFormat fileformat; + double max, min; + + const double expectedVal = 5.64477e-005; + const double reducedErrorConstant = 0.0001; + + /*Test NullPointerException*/ + + rttb::algorithms::DoseStatistics doseStatistics; + typedef boost::shared_ptr > > ResultsVectorPointer; + + ResultsVectorPointer spResults = + boost::make_shared > >(); + + ResultListPointer minListPtr(spResults); + ResultListPointer maxListPtr(spResults); + + ::DRTDoseIOD rtdoseDKFZ; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_FILENAME.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + //create corresponding DoseIterator + boost::shared_ptr spDoseIteratorTmp = + boost::make_shared(doseAccessor1); + DoseIteratorPointer spDoseIterator (spDoseIteratorTmp); + doseStatistics.setDoseIterator(spDoseIterator); + + CHECK_CLOSE(doseStatistics.getMean(),expectedVal,errorConstant); + CHECK_CLOSE(doseStatistics.getStdDeviation(),0,errorConstant); + CHECK_CLOSE(doseStatistics.getVariance(),0,errorConstant); + + DoseTypeGy vx=doseStatistics.getVx(expectedVal); + CHECK_EQUAL(vx,doseStatistics.getVx(0)); + CHECK_CLOSE(expectedVal,doseStatistics.getDx(vx),reducedErrorConstant); + + max=doseStatistics.getMaximum(maxListPtr); + CHECK_CLOSE(doseStatistics.getMaximum(maxListPtr),expectedVal,errorConstant); + CHECK_EQUAL(doseStatistics.getVx(max+reducedErrorConstant),0); + + min=doseStatistics.getMinimum(minListPtr); + CHECK_CLOSE(min,expectedVal,errorConstant); + CHECK_EQUAL(minListPtr->size(),100); + min=doseStatistics.getMinimum(minListPtr,50); + CHECK_EQUAL(minListPtr->size(),50); + + DoseTypeGy wholeVolume=doseStatistics.getVx(min); + CHECK_CLOSE(doseStatistics.getDx(wholeVolume),min,0.001); + CHECK_CLOSE(doseStatistics.getMOHx(vx),doseStatistics.getMean(),reducedErrorConstant); + CHECK_CLOSE(doseStatistics.getMOCx(20000),doseStatistics.getMean(),reducedErrorConstant); + CHECK_CLOSE(doseStatistics.getMinOCx(20000),doseStatistics.getMean(),reducedErrorConstant); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing + }//rttb \ No newline at end of file diff --git a/testing/examples/files.cmake b/testing/examples/files.cmake new file mode 100644 index 0000000..313afcf --- /dev/null +++ b/testing/examples/files.cmake @@ -0,0 +1,15 @@ +SET(CPP_FILES + RTBioModelExampleTest.cpp + RTBioModelScatterPlotExampleTest.cpp + RTDoseIndexTest.cpp + RTDoseStatisticsTest.cpp + RTDVHTest.cpp + DVHCalculatorExampleTest.cpp + DVHCalculatorComparisonTest.cpp + ../models/rttbScatterTester.cpp + rttbTestExamples.cpp + ) + +SET(H_FILES + ../models/rttbScatterTester.h +) diff --git a/testing/examples/rttbTestExamples.cpp b/testing/examples/rttbTestExamples.cpp new file mode 100644 index 0000000..cf5fd83 --- /dev/null +++ b/testing/examples/rttbTestExamples.cpp @@ -0,0 +1,73 @@ +// ----------------------------------------------------------------------- +// 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 smang (last changed by) +// @author *none* (Reviewer) +// @author zhangl (Programmer) +// +// Subversion HeadURL: $HeadURL: http://sidt-hpc1/dkfz_repository/NotMeVisLab/SIDT/RTToolbox/branch/restructure/testing/core/rttbCoreTests.cpp $ +*/ + +// this file defines the rttbCoreTests 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(RTBioModelExampleTest); + LIT_REGISTER_TEST(DVHCalculatorExampleTest); + LIT_REGISTER_TEST(RTDVHTest); + LIT_REGISTER_TEST(RTDoseIndexTest); + LIT_REGISTER_TEST(RTDoseStatisticsTest); + LIT_REGISTER_TEST(RTBioModelScatterPlotExampleTest); + + } + } +} + +int main(int argc, char* argv[]) +{ + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + catch(const std::exception& e) + { +// std::cerr << "RTToolbox test driver caught an exception:\n"; + // std::cerr << e.what() << "\n"; + result = -1; + } + catch(...) + { + //std::cerr << "RTToolbox test driver caught an unknown exception!!!\n"; + result = -1; + } + + return result; +} diff --git a/testing/interpolation/CMakeLists.txt b/testing/interpolation/CMakeLists.txt new file mode 100644 index 0000000..d74fd91 --- /dev/null +++ b/testing/interpolation/CMakeLists.txt @@ -0,0 +1,19 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(INTERPOLATION_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbInterpolationTests) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(SimpleMappableDoseAccessorTest ${INTERPOLATION_TESTS} SimpleMappableDoseAccessorTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncreaseX.dcm") +ADD_TEST(RosuMappableDoseAccessorTest ${INTERPOLATION_TESTS} RosuMappableDoseAccessorTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncreaseX.dcm") +ADD_TEST(InterpolationTest ${INTERPOLATION_TESTS} InterpolationTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncreaseX.dcm") + +RTTB_CREATE_TEST_MODULE(rttbInterpolation DEPENDS RTTBInterpolation RTTBMatchPointBinding RTTBDicomIO RTTBITKIO RTTBHelaxIO PACKAGE_DEPENDS Boost Litmus MatchPoint DCMTK) diff --git a/testing/interpolation/InterpolationTest.cpp b/testing/interpolation/InterpolationTest.cpp new file mode 100644 index 0000000..112f795 --- /dev/null +++ b/testing/interpolation/InterpolationTest.cpp @@ -0,0 +1,224 @@ +// ----------------------------------------------------------------------- +// 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 "rttbNearestNeighborInterpolation.h" +#include "rttbLinearInterpolation.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbGenericDoseIterator.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" + +namespace rttb +{ + namespace testing + { + + typedef rttb::interpolation::NearestNeighborInterpolation NearestNeighborInterpolation; + typedef rttb::interpolation::LinearInterpolation LinearInterpolation; + typedef rttb::core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + + /*! @brief InterpolationTest - tests only interpolation + 1) test both interpolation types with simple image (Dose = 2) + 2) test both interpolation types with increasing x image values image (Dose = y value) + 3) test exception handling + */ + + int InterpolationTest(int argc, char* argv[]) + { + PREPARE_DEFAULT_TEST_REPORTING; + + std::string RTDOSE_FILENAME_CONSTANT_TWO; + std::string RTDOSE_FILENAME_INCREASE_X; + + if (argc > 2) + { + RTDOSE_FILENAME_CONSTANT_TWO = argv[1]; + RTDOSE_FILENAME_INCREASE_X = argv[2]; + } + else + { + std::cout << "at least two parameters for InterpolationTest expected" << std::endl; + return -1; + } + + rttb::io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1( + RTDOSE_FILENAME_CONSTANT_TWO.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + DoseAccessorPointer doseAccessorNull; + + rttb::io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator2( + RTDOSE_FILENAME_INCREASE_X.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + //doseAccessor1 is used as dose image + boost::shared_ptr interpolationNN = + boost::shared_ptr(new NearestNeighborInterpolation()); + interpolationNN->setDoseAccessorPointer(doseAccessor1); + boost::shared_ptr interpolationLinear = boost::shared_ptr + (new LinearInterpolation()); + interpolationLinear->setDoseAccessorPointer(doseAccessor1); + + //doseAccessor2 is used as dose image. + //RTDOSE_FILENAME_INCREASE_X and RTDOSE_FILENAME_CONSTANT_TWO have the same GeometricInfo + boost::shared_ptr interpolationNN2 = + boost::shared_ptr(new NearestNeighborInterpolation()); + interpolationNN2->setDoseAccessorPointer(doseAccessor2); + boost::shared_ptr interpolationLinear2 = + boost::shared_ptr(new LinearInterpolation()); + interpolationLinear2->setDoseAccessorPointer(doseAccessor2); + + boost::shared_ptr interpolationNullNN = + boost::shared_ptr(new NearestNeighborInterpolation()); + boost::shared_ptr interpolationNullLinear = + boost::shared_ptr(new LinearInterpolation()); + + rttb::WorldCoordinate3D imagePositionPatient = + doseAccessor1->getGeometricInfo().getImagePositionPatient(); + rttb::SpacingVectorType3D pixelSpacing = doseAccessor1->getGeometricInfo().getSpacing(); + int size[] = {doseAccessor1->getGeometricInfo().getNumColumns(), doseAccessor1->getGeometricInfo().getNumRows(), doseAccessor1->getGeometricInfo().getNumSlices()}; + + //check position [0 0 0], [max/2, max/2, max/2], [max max max] and combinations (as pixel positions are given at middle, correct with PixelSpacing/2) + //outermost position of the image max_x = (coordinatesZeroAndMaxX.at(0) + (size[0]*pixelSpacing.x()) can't be used, because this is already outside of the image, thus test max-0.001. + std::vector coordinatesZeroAndMaxX; + coordinatesZeroAndMaxX.push_back(imagePositionPatient.x() - (pixelSpacing.x() * 0.5)); + coordinatesZeroAndMaxX.push_back(coordinatesZeroAndMaxX.at(0) + (size[0] + *pixelSpacing.x() - 0.001)); + coordinatesZeroAndMaxX.push_back(coordinatesZeroAndMaxX.at(0) + ((size[0] - 1)*pixelSpacing.x() * + 0.5)); + std::vector coordinatesZeroAndMaxY; + coordinatesZeroAndMaxY.push_back(imagePositionPatient.y() - (pixelSpacing.y() * 0.5)); + coordinatesZeroAndMaxY.push_back(coordinatesZeroAndMaxY.at(0) + (size[1] + *pixelSpacing.y() - 0.001)); + coordinatesZeroAndMaxY.push_back(coordinatesZeroAndMaxY.at(0) + ((size[1] - 1)*pixelSpacing.y() * + 0.5)); + std::vector coordinatesZeroAndMaxZ; + coordinatesZeroAndMaxZ.push_back(imagePositionPatient.z() - (pixelSpacing.z() * 0.5)); + coordinatesZeroAndMaxZ.push_back(coordinatesZeroAndMaxZ.at(0) + (size[2] + *pixelSpacing.z() - 0.001)); + coordinatesZeroAndMaxZ.push_back(coordinatesZeroAndMaxZ.at(0) + ((size[2] - 1)*pixelSpacing.z() * + 0.5)); + + std::vector coordinatesToCheck; + + for (int x = 0; x < coordinatesZeroAndMaxX.size(); x++) + { + for (int y = 0; y < coordinatesZeroAndMaxY.size(); y++) + { + for (int z = 0; z < coordinatesZeroAndMaxZ.size(); z++) + { + coordinatesToCheck.push_back(rttb::WorldCoordinate3D(coordinatesZeroAndMaxX.at(x), + coordinatesZeroAndMaxY.at(y), coordinatesZeroAndMaxZ.at(z))); + } + } + } + + rttb::WorldCoordinate3D positionOutsideOfImageLeft = imagePositionPatient - rttb::WorldCoordinate3D( + pixelSpacing.x() * 2.0, pixelSpacing.y() * 2.0, pixelSpacing.z() * 2.0); + rttb::WorldCoordinate3D positionOutsideOfImageRight = imagePositionPatient + + rttb::WorldCoordinate3D(size[0] * pixelSpacing.x(), size[1] * pixelSpacing.y(), + size[2] * pixelSpacing.z()) + rttb::WorldCoordinate3D(pixelSpacing.x() * 2.0, + pixelSpacing.y() * 2.0, pixelSpacing.z() * 2.0); + + //precomputed values for Nearest neighbor + Linear interpolator + double expectedDoseIncreaseXNearest[27]; + double expectedDoseIncreaseXLinear[27]; + + for (int i = 0; i < 27; i++) + { + if (i < 9) + { + expectedDoseIncreaseXNearest[i] = 0.0; + expectedDoseIncreaseXLinear[i] = 0.0; + } + else if (i < 18) + { + expectedDoseIncreaseXNearest[i] = 0.00186277; + expectedDoseIncreaseXLinear[i] = 0.00186277; + } + else + { + expectedDoseIncreaseXNearest[i] = 0.000931387; + expectedDoseIncreaseXLinear[i] = 0.000931387; + } + } + + //TEST 1) RTDOSE_FILENAME_CONSTANT_TWO contains Dose 2.0 Gy for each pixel, so for every interpolation, 2.0 has to be the result + std::vector::const_iterator iterCoordinates = coordinatesToCheck.cbegin(); + + while (iterCoordinates != coordinatesToCheck.cend()) + { + CHECK_EQUAL(interpolationNN->getValue(*iterCoordinates), 2.0); + CHECK_EQUAL(interpolationLinear->getValue(*iterCoordinates), 2.0); + ++iterCoordinates; + } + + //TEST 2) RTDOSE_FILENAME_INCREASE_X contains a Dose gradient file, correct interpolation values have been computed manually. To avoid floating point inaccuracy, CHECK_CLOSE is used + iterCoordinates = coordinatesToCheck.cbegin(); + unsigned int index = 0; + + while (iterCoordinates != coordinatesToCheck.cend() && index < 27) + { + CHECK_CLOSE(interpolationNN2->getValue(*iterCoordinates), expectedDoseIncreaseXNearest[index], + errorConstant); + CHECK_CLOSE(interpolationLinear2->getValue(*iterCoordinates), expectedDoseIncreaseXLinear[index], + errorConstant); + ++iterCoordinates; + ++index; + } + + //TEST 3) Exception handling + //Check that core::MappingOutOfImageException is thrown if requested position is outside image + CHECK_THROW_EXPLICIT(interpolationNN->getValue(positionOutsideOfImageLeft), + core::MappingOutsideOfImageException); + CHECK_THROW_EXPLICIT(interpolationNN->getValue(positionOutsideOfImageRight), + core::MappingOutsideOfImageException); + CHECK_THROW_EXPLICIT(interpolationLinear->getValue(positionOutsideOfImageLeft), + core::MappingOutsideOfImageException); + CHECK_THROW_EXPLICIT(interpolationLinear->getValue(positionOutsideOfImageRight), + core::MappingOutsideOfImageException); + + //Check that core::NullPointerException is thrown if Null Pointers are given to interpolator + CHECK_THROW_EXPLICIT(interpolationNullLinear->setDoseAccessorPointer(doseAccessorNull), + core::NullPointerException); + CHECK_THROW_EXPLICIT(interpolationNullNN->setDoseAccessorPointer(doseAccessorNull), + core::NullPointerException); + CHECK_THROW_EXPLICIT(interpolationNullLinear->getValue(coordinatesToCheck.front()), + core::NullPointerException); + CHECK_THROW_EXPLICIT(interpolationNullNN->getValue(coordinatesToCheck.front()), + core::NullPointerException); + + //Check that no exception is thrown otherwise + CHECK_NO_THROW(NearestNeighborInterpolation::NearestNeighborInterpolation()); + CHECK_NO_THROW(LinearInterpolation::LinearInterpolation()); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + } +} \ No newline at end of file diff --git a/testing/interpolation/RosuMappableDoseAccessorTest.cpp b/testing/interpolation/RosuMappableDoseAccessorTest.cpp new file mode 100644 index 0000000..79a32eb --- /dev/null +++ b/testing/interpolation/RosuMappableDoseAccessorTest.cpp @@ -0,0 +1,162 @@ +// ----------------------------------------------------------------------- +// 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 "rttbGenericDoseIterator.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbMatchPointTransformation.h" +#include "rttbRosuMappableDoseAccessor.h" +#include "rttbNearestNeighborInterpolation.h" +#include "rttbLinearInterpolation.h" +#include "rttbITKImageDoseAccessor.h" + +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" + +#include "itkImage.h" + +#include "registrationTest.h" +#include "registrationAlgorithm.h" + +namespace rttb +{ + namespace testing + { + static const unsigned int TargetDimension3D = 3; + static const unsigned int MovingDimension3D = 3; + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; + typedef rttb::interpolation::RosuMappableDoseAccessor RosuMappableDoseAccessor; + typedef map::core::RegistrationTest Registration3D3DTypeTest; + typedef Registration3D3DTypeTest::Pointer Registration3D3DTypeTestPointer; + typedef map::core::Registration Registration3D3DType; + typedef Registration3D3DType::Pointer Registration3D3DPointer; + typedef rttb::interpolation::TransformationInterface TransformationInterface; + typedef rttb::interpolation::MatchPointTransformation MatchPointTransformation; + typedef RosuMappableDoseAccessor::Pointer RosuMappableDoseAccessorPointer; + typedef rttb::interpolation::MappableDoseAccessorBase MappableDoseAccessorBase; + + /*! @brief RosuMappableDoseAccessorTest - test the API of RosuMappableDoseAccessor + 1) test constructor + 2) test with registration stub + */ + + int RosuMappableDoseAccessorTest(int argc, char* argv[]) + { + PREPARE_DEFAULT_TEST_REPORTING; + + std::string RTDOSE_FILENAME_CONSTANT_TWO; + std::string RTDOSE_FILENAME_INCREASE_X; + + if (argc > 2) + { + RTDOSE_FILENAME_CONSTANT_TWO = argv[1]; + RTDOSE_FILENAME_INCREASE_X = argv[2]; + } + else + { + std::cout << "at least two parameters for MappableDoseAccessorTest are expected" << std::endl; + return -1; + } + + rttb::io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1( + RTDOSE_FILENAME_CONSTANT_TWO.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + DoseAccessorPointer doseAccessorNull; + + rttb::io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator2( + RTDOSE_FILENAME_INCREASE_X.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + const double doseGridScalingDose2 = 2.822386e-005; + + Registration3D3DTypeTestPointer registration = Registration3D3DTypeTest::New(); + double translation[] = {0.0, 0.0, 0.0}; + registration->translation = translation; + registration->_limitedTarget = false; + + TransformationInterface::Pointer transformMP = TransformationInterface::Pointer( + new MatchPointTransformation(registration)); + boost::shared_ptr transformNull; + + MappableDoseAccessorBase::Pointer aMappableDoseAccessorDefault = + MappableDoseAccessorBase::Pointer(new RosuMappableDoseAccessor( + doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP)); + + //1) test constructors + CHECK_NO_THROW(RosuMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP)); + + + //2) test with registration stub + //First: Identity Transformation (nothing changes). Should lead to the same value although the subvoxel values are different (but symmetric with respect to the voxel center) + + //outside: should have default value 0 + CHECK_EQUAL(aMappableDoseAccessorDefault->getDoseAt( + aMappableDoseAccessorDefault->getGridSize() + 1), 0.0); + + const core::GeometricInfo geoInfoTarget = doseAccessor1->getGeometricInfo(); + + const VoxelGridIndex3D indexValid(geoInfoTarget.getNumColumns() - 1, geoInfoTarget.getNumRows() - 1, + geoInfoTarget.getNumSlices() - 1); + + const VoxelGridID idValid(indexValid[2] * geoInfoTarget.getNumColumns() * geoInfoTarget.getNumRows() + + indexValid[1] * geoInfoTarget.getNumColumns() + + indexValid[0]); + + //check if getDoseAt(VoxelGridID) and getDoseAt(VoxelGridIndex3D) lead to same result + CHECK_NO_THROW(aMappableDoseAccessorDefault->getDoseAt(indexValid)); + CHECK_EQUAL(aMappableDoseAccessorDefault->getDoseAt(indexValid), + aMappableDoseAccessorDefault->getDoseAt(idValid)); + CHECK_CLOSE(aMappableDoseAccessorDefault->getDoseAt(indexValid), + doseGridScalingDose2 * indexValid.x(), + errorConstant); + //dose value equals x-position. 0.5*(0.25+0.75)=0.5 (compute interpolation value at x-pos 0.25 and 0.75) + CHECK_CLOSE(aMappableDoseAccessorDefault->getDoseAt(VoxelGridID(0)), 0.5 * doseGridScalingDose2, + errorConstant); + + //Second: Translation (2.5mm/2.5mm/2.5mm) --> in voxel: (0.5/0.5/0.5) as pixelspacing = 5 mm + translation[0] = translation[1] = translation[2] = 2.5; + registration->translation = translation; + rttb::VoxelGridIndex3D aIndex(23, 11, 9); + + //value equals x-position. 0.5*((indexValid.x()+0.25+translation_x)+(indexValid.x()+0.75+translation_x))=1 + CHECK_CLOSE(aMappableDoseAccessorDefault->getDoseAt(aIndex), + (aIndex.x() + 1.0)*doseGridScalingDose2, errorConstant); + + rttb::VoxelGridIndex3D aIndex2(49, 32, 29); + + CHECK_CLOSE(aMappableDoseAccessorDefault->getDoseAt(aIndex2), + (aIndex2.x() + 1.0)*doseGridScalingDose2, errorConstant); + + //@todo: more tests needed? + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing +}//end namespace rttb diff --git a/testing/interpolation/SimpleMappableDoseAccessorTest.cpp b/testing/interpolation/SimpleMappableDoseAccessorTest.cpp new file mode 100644 index 0000000..8486748 --- /dev/null +++ b/testing/interpolation/SimpleMappableDoseAccessorTest.cpp @@ -0,0 +1,347 @@ +// ----------------------------------------------------------------------- +// 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 "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomHelaxDoseAccessor.h" +#include "rttbDicomHelaxFileDoseAccessorGenerator.h" +#include "rttbSimpleMappableDoseAccessor.h" +#include "rttbNearestNeighborInterpolation.h" +#include "rttbLinearInterpolation.h" +#include "rttbITKImageDoseAccessor.h" +#include "rttbTransformationInterface.h" +#include "rttbMatchPointTransformation.h" + +#include "rttbNullPointerException.h" +#include "rttbMappingOutsideOfImageException.h" + +#include "itkImage.h" + +#include "registrationTest.h" +#include "registrationAlgorithm.h" + +namespace rttb +{ + namespace testing + { + static const unsigned int TargetDimension3D = 3; + static const unsigned int MovingDimension3D = 3; + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + typedef rttb::interpolation::SimpleMappableDoseAccessor SimpleMappableDoseAccessor; + typedef map::core::RegistrationTest Registration3D3DTypeTest; + typedef Registration3D3DTypeTest::Pointer Registration3D3DTypeTestPointer; + typedef map::core::Registration Registration3D3DType; + typedef Registration3D3DType::Pointer Registration3D3DPointer; + typedef rttb::interpolation::NearestNeighborInterpolation NearestNeighborInterpolation; + typedef rttb::interpolation::LinearInterpolation LinearInterpolation; + typedef rttb::interpolation::TransformationInterface TransformationInterface; + typedef rttb::interpolation::MatchPointTransformation MatchPointTransformation; + typedef rttb::interpolation::MappableDoseAccessorBase MappableDoseAccessorBase; + + /*! @brief SimpleMappableDoseAccessorTest - test the API of SimpleMappableDoseAccessor + MappableDoseAccessorBase + 1) test constructor + 2) test getGeometricInfo(), validId(), validIndex() function + 3) test with registration stub + optional (depends on input parameters): + 4) test with rigid registration + */ + + int SimpleMappableDoseAccessorTest(int argc, char* argv[]) + { + PREPARE_DEFAULT_TEST_REPORTING; + + std::string RTDOSE_FILENAME_CONSTANT_TWO; + std::string RTDOSE_FILENAME_INCREASE_X; + std::string RTDOSE_FILENAME_REALISTIC; + std::string CT_PLANNING; + std::string CT_FRACTION; + + if (argc > 2) + { + RTDOSE_FILENAME_CONSTANT_TWO = argv[1]; + RTDOSE_FILENAME_INCREASE_X = argv[2]; + RTDOSE_FILENAME_REALISTIC = ""; + CT_PLANNING = ""; + CT_FRACTION = ""; + } + else + { + std::cout << "at least two parameters for MappableDoseAccessorTest are expected" << std::endl; + return -1; + } + + if (argc > 5) + { + RTDOSE_FILENAME_CONSTANT_TWO = argv[1]; + RTDOSE_FILENAME_INCREASE_X = argv[2]; + RTDOSE_FILENAME_REALISTIC = argv[3]; + CT_PLANNING = argv[4]; + CT_FRACTION = argv[5]; + } + + rttb::io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1( + RTDOSE_FILENAME_CONSTANT_TWO.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + DoseAccessorPointer doseAccessorNull; + + rttb::io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator2( + RTDOSE_FILENAME_INCREASE_X.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + Registration3D3DTypeTestPointer registration = Registration3D3DTypeTest::New(); + double translation[] = {0.0, 0.0, 0.0}; + registration->translation = translation; + registration->_limitedTarget = false; + + auto interpolationNN = + NearestNeighborInterpolation::Pointer(new NearestNeighborInterpolation()); + auto interpolationLinear = boost::shared_ptr + (new LinearInterpolation()); + boost::shared_ptr interpolationNull; + + auto transformMP = TransformationInterface::Pointer(new MatchPointTransformation(registration)); + boost::shared_ptr transformNull; + + auto aSimpleMappableDoseAccessorMPDefaultNN = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP, + interpolationNN)); + auto aSimpleMappableDoseAccessorMPNoPaddingNN = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP, + interpolationNN, false)); + auto aSimpleMappableDoseAccessorMPPaddingNN = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP, + interpolationNN, true, 42.0)); + auto aSimpleMappableDoseAccessorMPDefaultLinear = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP, + interpolationLinear)); + auto aSimpleMappableDoseAccessorMPNoPaddingLinear = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP, + interpolationLinear, false)); + auto aSimpleMappableDoseAccessorMPPaddingLinear = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(doseAccessor1->getGeometricInfo(), doseAccessor2, transformMP, + interpolationLinear, true, 42.0)); + + const core::GeometricInfo geoInfoMoving = doseAccessor1->getGeometricInfo(); + core::GeometricInfo geoInfoTarget = geoInfoMoving; + geoInfoTarget.setNumColumns(geoInfoTarget.getNumColumns() / 2); + geoInfoTarget.setNumRows(geoInfoTarget.getNumRows() / 2); + geoInfoTarget.setNumSlices(geoInfoTarget.getNumSlices() / 2); + geoInfoTarget.setPixelSpacingRow(geoInfoTarget.getPixelSpacingRow() * 2.0); + geoInfoTarget.setPixelSpacingColumn(geoInfoTarget.getPixelSpacingColumn() * 2.0); + geoInfoTarget.setSliceThickness(geoInfoTarget.getSliceThickness() * 2.0); + + const VoxelGridIndex3D indexValid(geoInfoTarget.getNumColumns() - 1, geoInfoTarget.getNumRows() - 1, + geoInfoTarget.getNumSlices() - 1); + const VoxelGridIndex3D indexInvalid(geoInfoMoving.getNumColumns() - 1, + geoInfoMoving.getNumRows() - 1, geoInfoMoving.getNumSlices() - 1); + + const VoxelGridID idValid(indexValid[2] * geoInfoTarget.getNumColumns() * geoInfoTarget.getNumRows() + + + indexValid[1] * geoInfoTarget.getNumColumns() + + indexValid[0]); + const VoxelGridID idInvalid(indexInvalid[2] * geoInfoTarget.getNumColumns() * + geoInfoTarget.getNumRows() + + indexInvalid[1] * geoInfoTarget.getNumColumns() + + indexInvalid[0]); + + auto aMappableDoseAccessorMPChangedGeometricInfo = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(geoInfoTarget, doseAccessor1, transformMP, + interpolationNN)); + + //1) test constructors + CHECK_NO_THROW(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP, interpolationNN)); + CHECK_NO_THROW(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP, interpolationNN, false)); + CHECK_NO_THROW(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP, interpolationNN, true, 5.0)); + CHECK_NO_THROW(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP, interpolationLinear)); + CHECK_NO_THROW(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP, interpolationLinear, false)); + CHECK_NO_THROW(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), doseAccessor2, + transformMP, interpolationLinear, true, 5.0)); + + CHECK_THROW_EXPLICIT(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), + doseAccessorNull, transformMP, interpolationNN), core::NullPointerException); + CHECK_THROW_EXPLICIT(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), + doseAccessor2, transformNull, interpolationNN), core::NullPointerException); + CHECK_THROW_EXPLICIT(SimpleMappableDoseAccessor test(doseAccessor1->getGeometricInfo(), + doseAccessor2, + transformMP, interpolationNull), core::NullPointerException); + + //2) test getGeometricInfo(), validId(), validIndex() function + + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->getGeometricInfo(), geoInfoTarget); + + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->validID(idValid), true); + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->validID(idInvalid), false); + + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->validIndex(indexValid), true); + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->validIndex(indexInvalid), false); + + + //3) test with registration stub + //First: Identity Transformation (nothing changes) + double vectorDoseAccessorStartEnd = 0.0; + + while (vectorDoseAccessorStartEnd <= 1.0) + { + VoxelGridID id = (VoxelGridID)(vectorDoseAccessorStartEnd * + (double)aSimpleMappableDoseAccessorMPDefaultNN->getGridSize()); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultNN->getDoseAt(id), doseAccessor2->getDoseAt(id)); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultLinear->getDoseAt(id), + doseAccessor2->getDoseAt(id)); + vectorDoseAccessorStartEnd += 0.1; + } + + CHECK_THROW_EXPLICIT(aSimpleMappableDoseAccessorMPNoPaddingNN->getDoseAt( + aSimpleMappableDoseAccessorMPNoPaddingNN->getGridSize() + 1), core::MappingOutsideOfImageException); + CHECK_NO_THROW(aSimpleMappableDoseAccessorMPDefaultNN->getDoseAt( + aSimpleMappableDoseAccessorMPDefaultNN->getGridSize() + 1)); + CHECK_NO_THROW(aSimpleMappableDoseAccessorMPPaddingNN->getDoseAt( + aSimpleMappableDoseAccessorMPPaddingNN->getGridSize() + 1)); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultNN->getDoseAt( + aSimpleMappableDoseAccessorMPDefaultNN->getGridSize() + 1), 0.0); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPPaddingNN->getDoseAt( + aSimpleMappableDoseAccessorMPPaddingNN->getGridSize() + 1), 42.0); + + CHECK_THROW_EXPLICIT(aSimpleMappableDoseAccessorMPNoPaddingLinear->getDoseAt( + aSimpleMappableDoseAccessorMPNoPaddingLinear->getGridSize() + 1), + core::MappingOutsideOfImageException); + CHECK_NO_THROW(aSimpleMappableDoseAccessorMPDefaultLinear->getDoseAt( + aSimpleMappableDoseAccessorMPDefaultLinear->getGridSize() + 1)); + CHECK_NO_THROW(aSimpleMappableDoseAccessorMPPaddingLinear->getDoseAt( + aSimpleMappableDoseAccessorMPPaddingLinear->getGridSize() + 1)); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultLinear->getDoseAt( + aSimpleMappableDoseAccessorMPDefaultLinear->getGridSize() + 1), 0.0); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPPaddingLinear->getDoseAt( + aSimpleMappableDoseAccessorMPPaddingLinear->getGridSize() + 1), 42.0); + + //check if getDoseAt(VoxelGridID) and getDoseAt(VoxelGridIndex3D) lead to same result + CHECK_NO_THROW(aMappableDoseAccessorMPChangedGeometricInfo->getDoseAt(indexValid)); + CHECK_NO_THROW(aMappableDoseAccessorMPChangedGeometricInfo->getDoseAt(indexInvalid)); + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->getDoseAt(indexValid), + aMappableDoseAccessorMPChangedGeometricInfo->getDoseAt(idValid)); + CHECK_EQUAL(aMappableDoseAccessorMPChangedGeometricInfo->getDoseAt(indexInvalid), 0.0); + + //Second: Translation (5mm/5mm/5mm) --> in voxel: (1/1/1) as pixelspacing = 5 mm + translation[0] = translation[1] = translation[2] = 5.0; + registration->translation = translation; + rttb::VoxelGridIndex3D aIndexBeforeTransformation(0, 0, 0); + rttb::VoxelGridIndex3D aIndexAfterTransformation(1, 1, 1); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultNN->getDoseAt(aIndexBeforeTransformation), + doseAccessor2->getDoseAt(aIndexAfterTransformation)); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultLinear->getDoseAt(aIndexBeforeTransformation), + doseAccessor2->getDoseAt(aIndexAfterTransformation)); + + rttb::VoxelGridIndex3D aIndexBeforeTransformation2(20, 10, 10); + rttb::VoxelGridIndex3D aIndexAfterTransformation2(21, 11, 11); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultNN->getDoseAt(aIndexBeforeTransformation2), + doseAccessor2->getDoseAt(aIndexAfterTransformation2)); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultLinear->getDoseAt(aIndexBeforeTransformation2), + doseAccessor2->getDoseAt(aIndexAfterTransformation2)); + + rttb::VoxelGridIndex3D aIndexBeforeTransformation3( + aSimpleMappableDoseAccessorMPDefaultNN->getGeometricInfo().getNumColumns() - 2, + aSimpleMappableDoseAccessorMPDefaultNN->getGeometricInfo().getNumRows() - 2, + aSimpleMappableDoseAccessorMPDefaultNN->getGeometricInfo().getNumSlices() - 2); + rttb::VoxelGridIndex3D aIndexAfterTransformation3( + aSimpleMappableDoseAccessorMPDefaultNN->getGeometricInfo().getNumColumns() - 1, + aSimpleMappableDoseAccessorMPDefaultNN->getGeometricInfo().getNumRows() - 1, + aSimpleMappableDoseAccessorMPDefaultNN->getGeometricInfo().getNumSlices() - 1); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultNN->getDoseAt(aIndexBeforeTransformation3), + doseAccessor2->getDoseAt(aIndexAfterTransformation3)); + CHECK_EQUAL(aSimpleMappableDoseAccessorMPDefaultLinear->getDoseAt(aIndexBeforeTransformation3), + doseAccessor2->getDoseAt(aIndexAfterTransformation3)); + + if (RTDOSE_FILENAME_REALISTIC != "" && CT_FRACTION != "" && CT_PLANNING != "") + { + //4) test with rigid registration + //realistic background: registration from BP-CT to fraction CT, apply on planning dose that is based on BP-CT (proof of concept) + + //to prevent error if files are not in repository + if (boost::filesystem::exists(CT_FRACTION) && boost::filesystem::exists(CT_PLANNING)) + { + + //Target image: fraction CT, Moving image: planning CT + registrationAlgorithm prepareRegistrationRealisticScenario(CT_FRACTION, CT_PLANNING, true); + Registration3D3DPointer registrationRealisticScenario = + prepareRegistrationRealisticScenario.getRegistration(); + + auto transformRealistic = TransformationInterface::Pointer(new MatchPointTransformation( + registrationRealisticScenario)); + + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator3(RTDOSE_FILENAME_REALISTIC.c_str()); + DoseAccessorPointer doseAccessor3(doseAccessorGenerator3.generateDoseAccessor()); + + core::GeometricInfo geoInfoRealistic = io::itk::convertToGeometricInfo( + prepareRegistrationRealisticScenario.getTargetImage()); + + //Dose is related to BP-CT, map dose to fraction CT geometry + auto aSimpleMappableDoseAccessorRealisticScenarioLinear = MappableDoseAccessorBase::Pointer( + new SimpleMappableDoseAccessor(geoInfoRealistic, + doseAccessor3, transformRealistic, interpolationLinear)); + + //combination of 0, size/2 and size to check as coordinates + std::vector coordinatesToCheckX, coordinatesToCheckY, coordinatesToCheckZ; + coordinatesToCheckX.push_back(0); + coordinatesToCheckX.push_back(geoInfoRealistic.getNumColumns() / 2); + coordinatesToCheckX.push_back(geoInfoRealistic.getNumColumns() - 1); + coordinatesToCheckY.push_back(0); + coordinatesToCheckY.push_back(geoInfoRealistic.getNumRows() / 2); + coordinatesToCheckY.push_back(geoInfoRealistic.getNumRows() - 1); + coordinatesToCheckZ.push_back(0); + coordinatesToCheckZ.push_back(geoInfoRealistic.getNumSlices() / 2); + coordinatesToCheckZ.push_back(geoInfoRealistic.getNumSlices() - 1); + + //Pixels are inside the fraction CT image and mapping should work (even if they map outside of doseAccessor3) + for (unsigned int i = 0; i < coordinatesToCheckX.size(); ++i) + { + for (unsigned int j = 0; j < coordinatesToCheckY.size(); ++j) + { + for (unsigned int k = 0; k < coordinatesToCheckZ.size(); ++k) + { + CHECK_NO_THROW(aSimpleMappableDoseAccessorRealisticScenarioLinear->getDoseAt(VoxelGridIndex3D( + coordinatesToCheckX.at(i), coordinatesToCheckY.at(j), coordinatesToCheckZ.at(k)))); + } + } + } + } + else + { + std::cout << CT_PLANNING << " and " << CT_FRACTION << + " files are missing. Rigid registration tests are not performed." << std::endl; + } + } + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//end namespace testing +}//end namespace rttb diff --git a/testing/interpolation/files.cmake b/testing/interpolation/files.cmake new file mode 100644 index 0000000..c006bd5 --- /dev/null +++ b/testing/interpolation/files.cmake @@ -0,0 +1,14 @@ +SET(CPP_FILES + SimpleMappableDoseAccessorTest.cpp + RosuMappableDoseAccessorTest.cpp + InterpolationTest.cpp + rttbInterpolationTests.cpp + registrationAlgorithm.cpp + mapDemoHelloWorldRegistration1Helper.cpp + ) + +SET(H_FILES + registrationAlgorithm.h + mapDemoHelloWorldRegistration1Helper.h + registrationTest.h + ) diff --git a/testing/interpolation/mapDemoHelloWorldRegistration1Helper.cpp b/testing/interpolation/mapDemoHelloWorldRegistration1Helper.cpp new file mode 100644 index 0000000..8317ab9 --- /dev/null +++ b/testing/interpolation/mapDemoHelloWorldRegistration1Helper.cpp @@ -0,0 +1,93 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Examples/Algorithms/mapDemoHelloWorldRegistration1Helper.cpp $ +*/ + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include "mapDemoHelloWorldRegistration1Helper.h" + +#include "litTestImageIO.h" +#include "litCheckMacros.h" +#include "litImageTester.h" +#include "litPointSetTester.h" + +#include + +int setImageFileNames(std::string targetImage, std::string movingImage, std::string resultImage, bool isDirectory, AppGlobals& globals) +{ + globals.targetImageFileName = targetImage; + globals.movingImageFileName = movingImage; + globals.resultImageFileName = resultImage; + 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; +} + +int saveResults(AppGlobals& globals) +{ + std::cout << std::endl << "Save result data..." << std::endl; + + lit::TestImageIO::InternalImageType>::writeImage( + globals.spResultImage, globals.resultImageFileName); + + return EXIT_SUCCESS; +} + +AppGlobals::AppGlobals() +{ +}; \ No newline at end of file diff --git a/testing/interpolation/mapDemoHelloWorldRegistration1Helper.h b/testing/interpolation/mapDemoHelloWorldRegistration1Helper.h new file mode 100644 index 0000000..8444a4d --- /dev/null +++ b/testing/interpolation/mapDemoHelloWorldRegistration1Helper.h @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Examples/Algorithms/mapDemoHelloWorldRegistration1Helper.h $ +*/ + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#ifndef __MAP_DEMO_HELLO_WORLD_REGISTRATION1_HELPER_H +#define __MAP_DEMO_HELLO_WORLD_REGISTRATION1_HELPER_H + +#include "mapContinuousElements.h" +#include "mapDiscreteElements.h" +#include "mapImageReader.h" + +typedef map::core::discrete::Elements<3>::InternalImageType ImageType; +typedef map::core::continuous::Elements<3>::InternalPointSetType LandmarksType; + +struct AppGlobals +{ + std::string targetImageFileName; + std::string movingImageFileName; + std::string resultImageFileName; + + bool isDirectory; + + ImageType::Pointer spTargetImage; + ImageType::Pointer spMovingImage; + + ImageType::Pointer spResultImage; + + AppGlobals(); +}; + +int setImageFileNames(std::string targetImage, std::string movingImage, std::string resultImage, bool isDirectory, AppGlobals& globals); + +int loadData(AppGlobals& globals); + +int saveResults(AppGlobals& globals); + +#endif \ No newline at end of file diff --git a/testing/interpolation/registrationAlgorithm.cpp b/testing/interpolation/registrationAlgorithm.cpp new file mode 100644 index 0000000..ad0eaa1 --- /dev/null +++ b/testing/interpolation/registrationAlgorithm.cpp @@ -0,0 +1,128 @@ +// ----------------------------------------------------------------------- +// MatchPoint - DKFZ translational registration framework +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See mapCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/MatchPoint/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) +// Subversion HeadURL: $HeadURL: https://svn/sbr/Sources/SBR-Projects/MatchPoint/trunk/Examples/Algorithms/mapDemoHelloWorldRegistration1.cpp $ +*/ + +#undef MAP_SEAL_ALGORITHMS + +#include "registrationAlgorithm.h" +#include "mapDemoHelloWorldRegistration1Helper.h" + +registrationAlgorithm::registrationAlgorithm(std::string targetFilename, std::string movingFilename, bool isDirectory, bool writeResultsFile) +{ + _targetFilename = targetFilename; + _movingFilename = movingFilename; + AppGlobals globals; + int result = EXIT_FAILURE; + + _saveResults = writeResultsFile; + + setImageFileNames(_targetFilename, _movingFilename, "result.dcm", isDirectory, globals); + + //load image and landmark files + std::cout << "Load images and landmarks..." << std::endl; + + loadData(globals); + + std::cout << "Establish registration algorithm..." << std::endl; + + _spAlgorithmEuler = AlgorithmTypeEuler::New(); + //to ensure that the Geometric centers of both images are matched + //all other parameters are default + _spAlgorithmEuler->setProperty("PreinitTransform",map::core::MetaProperty::New(true)); + + //Finally we set moving and target image for that should be + //used by the image based registration algorithm + _spAlgorithmEuler->setMovingImage(globals.spMovingImage); + _spAlgorithmEuler->setTargetImage(globals.spTargetImage); + + //The algorithm is set up and ready to run... + std::cout << "Starting to determine the registration..." << std::endl; + 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"; + result = -1; + } + catch (const itk::ExceptionObject& e) + { + std::cerr << "caught an ITK exception:\n"; + std::cerr << e.GetFile() << ":" << e.GetLine() << ":\n" + << e.GetDescription() << "\n"; + result = -1; + } + catch (const std::exception& e) + { + std::cerr << "caught an exception:\n"; + std::cerr << e.what() << "\n"; + result = -1; + } + catch (...) + { + std::cerr << "caught an unknown exception!!!\n"; + result = -1; + } + + std::cout << std::endl << "Registration determined..." << std::endl; + //Thats all. Now we have a registration that map the moving image into the target image + //space. But the algorithm only delivers the registration as a mapping function between + //moving and target image space. So the moving image must be mapped... + + if (_saveResults){ + //define registration task + std::cout << "Define registration task..." << std::endl; + + ImageMappingTaskType::Pointer spImageTask = ImageMappingTaskType::New(); + spImageTask->setInputImage(globals.spMovingImage); + + spImageTask->setRegistration(spRegistration); + spImageTask->setResultImageDescriptor(map::core::createFieldRepresentation(*(_spAlgorithmEuler->getTargetImage().GetPointer()))); + + //Process the task + std::cout << "Process registration task..." << std::endl; + spImageTask->execute(); + + globals.spResultImage = spImageTask->getResultImage(); + + saveResults(globals); + } + +} + +vnl_vector registrationAlgorithm::getRegistrationParameters(Registration3D3DPointer reg){ + typedef map::core::ModelBasedRegistrationKernel<3, 3> ModelBasedRegistrationKernel3D3D; + + const ModelBasedRegistrationKernel3D3D* pModelBasedDirectKernel3D3D = dynamic_cast(&(reg->getDirectMapping())); + + if (pModelBasedDirectKernel3D3D) + { + ModelBasedRegistrationKernel3D3D::ParametersType params = + pModelBasedDirectKernel3D3D->getTransformModel()->getTransform()->GetParameters(); + + return params; + } + else + return vnl_vector(); +} diff --git a/testing/interpolation/registrationAlgorithm.h b/testing/interpolation/registrationAlgorithm.h new file mode 100644 index 0000000..c54bf28 --- /dev/null +++ b/testing/interpolation/registrationAlgorithm.h @@ -0,0 +1,51 @@ +#ifndef __MYREGISTRATION_H +#define __MYREGISTRATION_H + +#include +#include + +#include "mapDemoHelloWorldRegistration1Helper.h" +#include "mapImageMappingTask.h" +#include "mapITKEuler3DMattesMIRegistrationAlgorithmTemplate.h" +#include "mapExceptionObject.h" + +/*! @class registrationAlgorithm + @brief implements a concrete registration algorithm of MatchPoint +*/ +class registrationAlgorithm +{ +private: + std::string _targetFilename; + std::string _movingFilename; + std::string _targetDirectory; + std::string _movingDirectory; + + bool _saveResults; + +public: + typedef map::core::Registration<3, 3> Registration3D3DType; + typedef Registration3D3DType::Pointer Registration3D3DPointer; + typedef map::algorithm::boxed::ITKEuler3DMattesMIRegistrationAlgorithm + AlgorithmTypeEuler; + typedef map::core::ImageMappingTask + ImageMappingTaskType; + + /*! @brief Constructor + */ + registrationAlgorithm(std::string targetFilename, std::string movingFilename, + bool isDirectory = false, bool saveResult = false); + map::core::Registration<3, 3>::Pointer getRegistration() + { + return _spAlgorithmEuler->getRegistration(); + }; + const itk::Image* getTargetImage() + { + return _spAlgorithmEuler->getTargetImage(); + }; + vnl_vector getRegistrationParameters(Registration3D3DPointer reg); + +private: + AlgorithmTypeEuler::Pointer _spAlgorithmEuler; +}; + +#endif \ No newline at end of file diff --git a/testing/interpolation/registrationTest.h b/testing/interpolation/registrationTest.h new file mode 100644 index 0000000..d551e99 --- /dev/null +++ b/testing/interpolation/registrationTest.h @@ -0,0 +1,59 @@ +#ifndef __TESTREGISTRATION_H +#define __TESTREGISTRATION_H + +#include "mapRegistration.h" + +namespace map +{ + namespace core + { + /*! @class TestRegistration + @brief Simple implementation of MatchPoint Registration class with direct access to mapping. + */ + template + class RegistrationTest: public + Registration + { + public: + typedef RegistrationTest Self; + typedef RegistrationBase Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + itkTypeMacro(RegistrationTest, Registration); + itkNewMacro(Self); + + bool _limitedTarget; + double* translation; + + RegistrationTest() {}; + + ~RegistrationTest() + { + }; + + + virtual bool mapPointInverse(const TargetPointType& inPoint, MovingPointType& outPoint) const + { + for (unsigned int i = 0; i < VTargetDimensions; i++) + { + outPoint[i] = inPoint[i] + translation[i]; + } + + return true; + }; + + virtual bool hasLimitedTargetRepresentation() const + { + return _limitedTarget; + } + + + private: + RegistrationTest(const Self& source); //purposely not implemented + void operator=(const Self&); //purposely not implemented + }; + } +} + +#endif \ No newline at end of file diff --git a/testing/interpolation/rttbInterpolationTests.cpp b/testing/interpolation/rttbInterpolationTests.cpp new file mode 100644 index 0000000..c100175 --- /dev/null +++ b/testing/interpolation/rttbInterpolationTests.cpp @@ -0,0 +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) +*/ + +// 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(SimpleMappableDoseAccessorTest); + LIT_REGISTER_TEST(RosuMappableDoseAccessorTest); + LIT_REGISTER_TEST(InterpolationTest); + } + } +} + +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/interpolation/testRegistration.h b/testing/interpolation/testRegistration.h new file mode 100644 index 0000000..c23424c --- /dev/null +++ b/testing/interpolation/testRegistration.h @@ -0,0 +1,56 @@ +#ifndef __TESTREGISTRATION_H +#define __TESTREGISTRATION_H + +#include "mapRegistration.h" + +namespace map +{ + namespace core + { + template + class TestRegistration: public + Registration + { + public: + typedef TestRegistration Self; + typedef RegistrationBase Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + itkTypeMacro(TestRegistration, Registration); + itkNewMacro(Self); + + bool _limitedTarget; + double* translation; + + TestRegistration() {}; + + ~TestRegistration() + { + }; + + + virtual bool mapPointInverse(const TargetPointType& inPoint, MovingPointType& outPoint) const + { + for (unsigned int i = 0; i < VTargetDimensions; i++) + { + outPoint[i] = inPoint[i] + translation[i]; + } + + return true; + }; + + virtual bool hasLimitedTargetRepresentation() const + { + return _limitedTarget; + } + + + private: + TestRegistration(const Self& source); //purposely not implemented + void operator=(const Self&); //purposely not implemented + }; + } +} + +#endif \ No newline at end of file diff --git a/testing/io/CMakeLists.txt b/testing/io/CMakeLists.txt new file mode 100644 index 0000000..7a2ce71 --- /dev/null +++ b/testing/io/CMakeLists.txt @@ -0,0 +1,26 @@ + + MESSAGE (STATUS "Process All IO Tests...") + + #----------------------------------------------------------------------------- + # Include sub directories + #----------------------------------------------------------------------------- + + ADD_SUBDIRECTORY (other) + + IF(BUILD_IO_Dicom) + ADD_SUBDIRECTORY(dicom) + IF(BUILD_IO_HELAX) + ADD_SUBDIRECTORY(helax) + ENDIF(BUILD_IO_HELAX) + ENDIF(BUILD_IO_Dicom) + + IF(BUILD_IO_ITK) + ADD_SUBDIRECTORY(itk) + ENDIF(BUILD_IO_ITK) + + IF(BUILD_IO_Virtuos) + ADD_SUBDIRECTORY(virtuos) + ENDIF(BUILD_IO_Virtuos) + + + diff --git a/testing/io/dicom/CMakeLists.txt b/testing/io/dicom/CMakeLists.txt new file mode 100644 index 0000000..e032960 --- /dev/null +++ b/testing/io/dicom/CMakeLists.txt @@ -0,0 +1,28 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(DICOMIO_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbDicomIOTests) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(DicomDoseAccessorGeneratorTest ${DICOMIO_TEST} DicomDoseAccessorGeneratorTest +"${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" +"${TEST_DATA_ROOT}/DICOM/TestDose/ConstantFifty.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" ) + +ADD_TEST(DicomIOTest ${DICOMIO_TEST} DicomIOTest +"${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}/DICOM/TestDose/ConstantFifty.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/dicompylerTestDose.dcm" ) + +ADD_TEST(DicomStructureSetGeneratorTest ${DICOMIO_TEST} DicomStructureSetGeneratorTest +"${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" ) + + +RTTB_CREATE_TEST_MODULE(rttbDicomIO DEPENDS RTTBDicomIO PACKAGE_DEPENDS Boost Litmus DCMTK) + diff --git a/testing/io/dicom/DicomDoseAccessorGeneratorTest.cpp b/testing/io/dicom/DicomDoseAccessorGeneratorTest.cpp new file mode 100644 index 0000000..15c1f03 --- /dev/null +++ b/testing/io/dicom/DicomDoseAccessorGeneratorTest.cpp @@ -0,0 +1,106 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomIODDoseAccessorGenerator.h" +#include "rttbInvalidDoseException.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 DicomDoseAccessorGeneratorTest(int argc, char* argv[] ) + { + typedef boost::shared_ptr DRTDoseIODPtr; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: + // 1: dose1 file name + // 2: dose2 file name + // 3: dose3 file name + + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + std::string RTDOSE3_FILENAME; + + if (argc>1) + { + RTDOSE_FILENAME = argv[1]; + } + if (argc>2) + { + RTDOSE2_FILENAME = argv[2]; + } + if (argc>3) + { + RTDOSE3_FILENAME = argv[3]; + } + + + + OFCondition status; + DcmFileFormat fileformat; + + /* test DicomFileDoseAccessorGenerator generateDoseAccessor()*/ + CHECK_THROW_EXPLICIT(io::dicom::DicomFileDoseAccessorGenerator("test.test").generateDoseAccessor(), core::InvalidDoseException); + CHECK_NO_THROW(io::dicom::DicomFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str()).generateDoseAccessor()); + CHECK_NO_THROW(io::dicom::DicomFileDoseAccessorGenerator(RTDOSE2_FILENAME.c_str()).generateDoseAccessor()); + CHECK_NO_THROW(io::dicom::DicomFileDoseAccessorGenerator(RTDOSE3_FILENAME.c_str()).generateDoseAccessor()); + + /* test DicomIODDoseAccessorGenerator generateDoseAccessor()*/ + + DRTDoseIODPtr dose = boost::make_shared(); + + CHECK_THROW_EXPLICIT(io::dicom::DicomIODDoseAccessorGenerator(dose).generateDoseAccessor(), core::InvalidDoseException); + + fileformat.loadFile(RTDOSE_FILENAME.c_str()); + + dose->read(*fileformat.getDataset()); + + CHECK_NO_THROW(io::dicom::DicomIODDoseAccessorGenerator(dose).generateDoseAccessor()); + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/dicom/DicomIOTest.cpp b/testing/io/dicom/DicomIOTest.cpp new file mode 100644 index 0000000..2aee589 --- /dev/null +++ b/testing/io/dicom/DicomIOTest.cpp @@ -0,0 +1,225 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomIODDoseAccessorGenerator.h" +#include "rttbDicomFileStructureSetGenerator.h" + + +namespace rttb +{ + + namespace testing + { + + /*!@brief DicomIOTest - test the IO for dicom data + 1) test dicom dose import if geometric info was set correctly + 2) test dicom dose import accessing dose data and converting + 3) check if dicom tags are correctly read + 4) test structure import + + WARNING: The values for comparison need to be adjusted if the input files are changed! + */ + + int DicomIOTest(int argc, char* argv[]) + { + typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: 1: structure file name + // 2: dose1 file name + // 3: dose2 file name + // 4: dose3 file name + + std::string RTSTRUCT_FILENAME; + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + std::string RTDOSE3_FILENAME; + std::string RTDOSE4_FILENAME; + + if (argc > 1) + { + RTSTRUCT_FILENAME = argv[1]; + } + if (argc > 2) + { + RTDOSE_FILENAME = argv[2]; + } + if (argc > 3) + { + RTDOSE2_FILENAME = argv[3]; + } + if (argc > 4) + { + RTDOSE3_FILENAME = argv[4]; + } + if (argc>5) + { + RTDOSE4_FILENAME = argv[5]; + } + + + OFCondition status; + DcmFileFormat fileformat; + + /* read dicom-rt dose */ + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_FILENAME.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + //1) test dicom dose import if geometric info was set correctly + core::GeometricInfo geoInfo = doseAccessor1->getGeometricInfo(); + CHECK_EQUAL(45, geoInfo.getNumRows()); + CHECK_EQUAL(67, geoInfo.getNumColumns()); + CHECK_EQUAL(64, geoInfo.getNumSlices()); + CHECK_EQUAL(OrientationMatrix(), geoInfo.getOrientationMatrix()); + + SpacingVectorType3D pixelSpacing(5, 5, 5); + CHECK_EQUAL(pixelSpacing, geoInfo.getSpacing()); + + WorldCoordinate3D imagePositionPatient(-170.556642, -273.431642, -674); + CHECK_EQUAL(imagePositionPatient, geoInfo.getImagePositionPatient()); + + const VoxelGridID start = 0; + const VoxelGridIndex3D start3D(0); + + VoxelGridID end, inbetween; + VoxelGridIndex3D end3D, inbetween3D; + + //2) test dicom dose import accessing dose data and converting + + + CHECK_EQUAL(2, doseAccessor1->getDoseAt(start)); + CHECK_EQUAL(2, doseAccessor1-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor1-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor1->getGridSize() / 2.0)); + doseAccessor1->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(2, doseAccessor1->getDoseAt(inbetween)); + CHECK_EQUAL(2, doseAccessor1-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor1-> getDoseAt(inbetween3D)); + + end = doseAccessor1->getGridSize() - 1; + doseAccessor1->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(2, doseAccessor1->getDoseAt(end)); + CHECK_EQUAL(2, doseAccessor1-> getDoseAt(end3D)); + CHECK_EQUAL(doseAccessor1->getDoseAt(end), doseAccessor1-> getDoseAt(end3D)); + + ::DRTDoseIOD rtdose2; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator2(RTDOSE2_FILENAME.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + //2) test dicom dose import accessing dose data and converting + + CHECK_EQUAL(50, doseAccessor2->getDoseAt(start)); + CHECK_EQUAL(50, doseAccessor2-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(start), doseAccessor2-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor2->getGridSize() / 2.0)); + doseAccessor2->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(50, doseAccessor2->getDoseAt(inbetween)); + CHECK_EQUAL(50, doseAccessor2-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(inbetween), doseAccessor2-> getDoseAt(inbetween3D)); + + end = doseAccessor2->getGridSize() - 1; + doseAccessor2->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(50, doseAccessor2->getDoseAt(end)); + CHECK_EQUAL(50, doseAccessor2-> getDoseAt(end3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(end), doseAccessor2-> getDoseAt(end3D)); + + + ::DRTDoseIOD rtdose3; + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator3(RTDOSE3_FILENAME.c_str()); + DoseAccessorPointer doseAccessor3(doseAccessorGenerator3.generateDoseAccessor()); + + //2) test dicom dose import accessing dose data and converting + + CHECK_EQUAL(0, doseAccessor3->getDoseAt(start)); + CHECK_EQUAL(0, doseAccessor3-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor3->getDoseAt(start), doseAccessor3-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor3->getGridSize() / 2.0)); + doseAccessor3->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(0, doseAccessor3->getDoseAt(inbetween)); + CHECK_EQUAL(0, doseAccessor3-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor3->getDoseAt(inbetween), doseAccessor3-> getDoseAt(inbetween3D)); + + end = doseAccessor3->getGridSize() - 1; + doseAccessor3->getGeometricInfo().convert(end, end3D); + + CHECK_CLOSE(1.46425, doseAccessor3->getDoseAt(end), errorConstant); + CHECK_CLOSE(1.46425, doseAccessor3-> getDoseAt(end3D), errorConstant); + CHECK_EQUAL(doseAccessor3->getDoseAt(end), doseAccessor3-> getDoseAt(end3D)); + + /* structure set */ + StructureSetPointer rtStructureSet = io::dicom::DicomFileStructureSetGenerator( + RTSTRUCT_FILENAME.c_str()).generateStructureSet(); + //4) test structure import + CHECK_EQUAL(10, rtStructureSet->getNumberOfStructures()); + + CHECK_EQUAL("Aussenkontur", (rtStructureSet->getStructure(0))->getLabel()); + CHECK_EQUAL("Rueckenmark", (rtStructureSet->getStructure(1))->getLabel()); + CHECK_EQUAL("Niere re.", (rtStructureSet->getStructure(2))->getLabel()); + CHECK_EQUAL("Niere li.", (rtStructureSet->getStructure(3))->getLabel()); + CHECK_EQUAL("Magen/DD", (rtStructureSet->getStructure(4))->getLabel()); + CHECK_EQUAL("Leber", (rtStructureSet->getStructure(5))->getLabel()); + CHECK_EQUAL("Darm", (rtStructureSet->getStructure(6))->getLabel()); + CHECK_EQUAL("Ref.Pkt.", (rtStructureSet->getStructure(7))->getLabel()); + CHECK_EQUAL("PTV", (rtStructureSet->getStructure(8))->getLabel()); + CHECK_EQUAL("Boost", (rtStructureSet->getStructure(9))->getLabel()); + + + //4) Test other tags (GridFrameOffsetVector(3004,000c) and PixelSpacingBetweenSlices(0018, 0088)) to compute dose slicing + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator4(RTDOSE4_FILENAME.c_str()); + DoseAccessorPointer doseAccessor4(doseAccessorGenerator4.generateDoseAccessor()); + rttb::SpacingVectorType3D spacingVec = doseAccessor4->getGeometricInfo().getSpacing(); + CHECK_EQUAL(2.5, spacingVec.x()); + CHECK_EQUAL(2.5, spacingVec.y()); + CHECK_EQUAL(3, spacingVec.z()); + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/dicom/DicomStructureSetGeneratorTest.cpp b/testing/io/dicom/DicomStructureSetGeneratorTest.cpp new file mode 100644 index 0000000..3192dd6 --- /dev/null +++ b/testing/io/dicom/DicomStructureSetGeneratorTest.cpp @@ -0,0 +1,102 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#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(), io::dicom::DcmrtException); + 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()); + + 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/dicom/files.cmake b/testing/io/dicom/files.cmake new file mode 100644 index 0000000..f0d4be7 --- /dev/null +++ b/testing/io/dicom/files.cmake @@ -0,0 +1,9 @@ +SET(CPP_FILES + DicomDoseAccessorGeneratorTest.cpp + DicomIOTest.cpp + DicomStructureSetGeneratorTest.cpp + rttbIOTests.cpp + ) + +SET(H_FILES +) diff --git a/testing/io/dicom/rttbIOTests.cpp b/testing/io/dicom/rttbIOTests.cpp new file mode 100644 index 0000000..c27b187 --- /dev/null +++ b/testing/io/dicom/rttbIOTests.cpp @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------- +// 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 + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include "litMultiTestsMain.h" + +namespace rttb{ + namespace testing{ + + void registerTests() + { + LIT_REGISTER_TEST(DicomDoseAccessorGeneratorTest); + LIT_REGISTER_TEST(DicomIOTest); + LIT_REGISTER_TEST(DicomStructureSetGeneratorTest); + + } + } + } + +int main(int argc, char* argv[]) + { + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + + catch(...) + { + result = -1; + } + + return result; + } diff --git a/testing/io/helax/CMakeLists.txt b/testing/io/helax/CMakeLists.txt new file mode 100644 index 0000000..586bc14 --- /dev/null +++ b/testing/io/helax/CMakeLists.txt @@ -0,0 +1,25 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(HelaxIO_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbHelaxIOTests) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(DicomHelaxDoseAccessorGeneratorTest ${HelaxIO_TEST} DicomHelaxDoseAccessorGeneratorTest +"${TEST_DATA_ROOT}/DICOM/HelaxDose/" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" +"${TEST_DATA_ROOT}/DICOM/TestDose/ConstantFifty.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" + ) + +ADD_TEST(DicomHelaxIOTest ${HelaxIO_TEST} DicomHelaxIOTest +"${TEST_DATA_ROOT}/DICOM/HelaxStructureSet/____mm__1.2.276.0.28.19.142087956198378746376227895256244905653791675016.dcm" "${TEST_DATA_ROOT}/DICOM/HelaxDose/" + ) + +RTTB_CREATE_TEST_MODULE(rttbHelaxIO DEPENDS RTTBHelaxIO RTTBDicomIO PACKAGE_DEPENDS BoostBinaries Litmus DCMTK) + diff --git a/testing/io/helax/DicomHelaxDoseAccessorGeneratorTest.cpp b/testing/io/helax/DicomHelaxDoseAccessorGeneratorTest.cpp new file mode 100644 index 0000000..992fddf --- /dev/null +++ b/testing/io/helax/DicomHelaxDoseAccessorGeneratorTest.cpp @@ -0,0 +1,127 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDicomHelaxFileDoseAccessorGenerator.h" +#include "rttbDicomHelaxIODVecDoseAccessorGenerator.h" +#include "rttbDicomHelaxDoseAccessor.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" + + +namespace rttb{ + + namespace testing{ + + /*!@brief DicomHelaxDoseAccessorGeneratorTest - test the IO for dicom helax data + 1) test dicom helax file generator generateDoseAccessor() + 2) test dicom helax IOD vector generator generateDoseAccessor() + */ + + int DicomHelaxDoseAccessorGeneratorTest(int argc, char* argv[] ) + { + typedef boost::shared_ptr DRTDoseIODPtr; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: 1: dose directory name + // 2: dose1 file name + // 3: dose2 file name + // 4: dose3 file name + + std::string RTDOSE_DIRNAME; + std::string RTDOSE1_FILENAME; + std::string RTDOSE2_FILENAME; + std::string RTDOSE3_FILENAME; + + + if (argc>1) + { + RTDOSE_DIRNAME = argv[1]; + } + if (argc>2) + { + RTDOSE1_FILENAME = argv[2]; + } + if (argc>3) + { + RTDOSE2_FILENAME = argv[3]; + } + if (argc>4) + { + RTDOSE3_FILENAME = argv[4]; + } + + + OFCondition status; + DcmFileFormat fileformat; + + /* test dicom helax file generator generateDoseAccessor() */ + CHECK_THROW_EXPLICIT(io::helax::DicomHelaxFileDoseAccessorGenerator("/test").generateDoseAccessor(), core::InvalidParameterException); + CHECK_NO_THROW(io::helax::DicomHelaxFileDoseAccessorGenerator(RTDOSE_DIRNAME.c_str()).generateDoseAccessor()); + + + /* test dicom helax IOD vector generator generateDoseAccessor()*/ + DRTDoseIODPtr dose1 = boost::make_shared(); + DRTDoseIODPtr dose2 = boost::make_shared(); + DRTDoseIODPtr dose3 = boost::make_shared(); + std::vector doseVector; + + //test empty vector + CHECK_THROW_EXPLICIT(io::helax::DicomHelaxIODVecDoseAccessorGenerator(doseVector).generateDoseAccessor(), core::InvalidParameterException); + + doseVector.push_back(dose1); + doseVector.push_back(dose2); + doseVector.push_back(dose3); + + //test vector with all empty dose IODs + CHECK_THROW_EXPLICIT(io::helax::DicomHelaxIODVecDoseAccessorGenerator(doseVector).generateDoseAccessor(), core::InvalidDoseException); + + + fileformat.loadFile(RTDOSE1_FILENAME.c_str()); + dose1->read(*fileformat.getDataset()); + fileformat.loadFile(RTDOSE2_FILENAME.c_str()); + dose2->read(*fileformat.getDataset()); + //test vector with one empty dose IOD + CHECK_THROW_EXPLICIT(io::helax::DicomHelaxIODVecDoseAccessorGenerator(doseVector).generateDoseAccessor(), core::InvalidDoseException); + + + fileformat.loadFile(RTDOSE3_FILENAME.c_str()); + dose3->read(*fileformat.getDataset()); + CHECK_NO_THROW(io::helax::DicomHelaxIODVecDoseAccessorGenerator(doseVector).generateDoseAccessor()); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/helax/DicomHelaxIOTest.cpp b/testing/io/helax/DicomHelaxIOTest.cpp new file mode 100644 index 0000000..78a4943 --- /dev/null +++ b/testing/io/helax/DicomHelaxIOTest.cpp @@ -0,0 +1,138 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbDicomHelaxFileDoseAccessorGenerator.h" +#include "rttbDicomHelaxIODVecDoseAccessorGenerator.h" +#include "rttbDicomHelaxDoseAccessor.h" +#include "rttbDicomFileStructureSetGenerator.h" + + +namespace rttb +{ + + namespace testing + { + + /*!@brief DicomHelaxIOTest - test the IO for dicom helax data + 1) test dicom helax dose import if geometric info was set correctly + 2) test dicom helax dose import accessing dose data and converting + 3) test dicom helax structure import + + WARNING: The values for comparison need to be adjusted if the input files are changed! + */ + + int DicomHelaxIOTest(int argc, char* argv[]) + { + typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: 1: structure file name + // 2: dose1 directory name + + + std::string RTSTRUCT_FILENAME; + std::string RTDOSE_DIRNAME; + + + if (argc > 1) + { + RTSTRUCT_FILENAME = argv[1]; + } + + if (argc > 2) + { + RTDOSE_DIRNAME = argv[2]; + } + + + + OFCondition status; + DcmFileFormat fileformat; + + /* read dicom-rt dose */ + io::helax::DicomHelaxFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_DIRNAME.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + //1) test dicom dose import if geometric info was set correctly + core::GeometricInfo geoInfo = doseAccessor1->getGeometricInfo(); + CHECK_EQUAL(64, geoInfo.getNumRows()); + CHECK_EQUAL(54, geoInfo.getNumColumns()); + CHECK_EQUAL(52, geoInfo.getNumSlices()); + CHECK_EQUAL(OrientationMatrix(), geoInfo.getOrientationMatrix()); + + const VoxelGridID start = 0; + const VoxelGridIndex3D start3D(0); + + VoxelGridID end, inbetween; + VoxelGridIndex3D end3D, inbetween3D; + + //2) test dicom dose import accessing dose data and converting + + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor1-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor1->getGridSize() / 2.0)); + doseAccessor1->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor1-> getDoseAt(inbetween3D)); + + end = doseAccessor1->getGridSize() - 1; + doseAccessor1->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(end), doseAccessor1-> getDoseAt(end3D)); + + + /* structure set */ + StructureSetPointer rtStructureSet = io::dicom::DicomFileStructureSetGenerator( + RTSTRUCT_FILENAME.c_str()).generateStructureSet(); + //3) test structure import + CHECK_EQUAL(8, rtStructureSet->getNumberOfStructures()); + + CHECK_EQUAL("Patient outline", (rtStructureSet->getStructure(0))->getLabel()); + CHECK_EQUAL("boost", (rtStructureSet->getStructure(1))->getLabel()); + CHECK_EQUAL("Chiasma", (rtStructureSet->getStructure(2))->getLabel()); + CHECK_EQUAL("Sehnerv li.", (rtStructureSet->getStructure(3))->getLabel()); + CHECK_EQUAL("Sehnerv re.", (rtStructureSet->getStructure(4))->getLabel()); + CHECK_EQUAL("Auge li.", (rtStructureSet->getStructure(5))->getLabel()); + CHECK_EQUAL("Auge re.", (rtStructureSet->getStructure(6))->getLabel()); + CHECK_EQUAL("Hirnstamm", (rtStructureSet->getStructure(7))->getLabel()); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/helax/files.cmake b/testing/io/helax/files.cmake new file mode 100644 index 0000000..f67a20a --- /dev/null +++ b/testing/io/helax/files.cmake @@ -0,0 +1,8 @@ +SET(CPP_FILES + DicomHelaxDoseAccessorGeneratorTest.cpp + DicomHelaxIOTest.cpp + rttbHelaxIOTests.cpp +) + +SET(H_FILES +) diff --git a/testing/io/helax/rttbHelaxIOTests.cpp b/testing/io/helax/rttbHelaxIOTests.cpp new file mode 100644 index 0000000..7fc8ec9 --- /dev/null +++ b/testing/io/helax/rttbHelaxIOTests.cpp @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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 + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include "litMultiTestsMain.h" + +namespace rttb{ + namespace testing{ + + void registerTests() + { + LIT_REGISTER_TEST(DicomHelaxDoseAccessorGeneratorTest); + LIT_REGISTER_TEST(DicomHelaxIOTest); + + } + } + } + +int main(int argc, char* argv[]) + { + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + + catch(...) + { + result = -1; + } + + return result; + } diff --git a/testing/io/itk/CMakeLists.txt b/testing/io/itk/CMakeLists.txt new file mode 100644 index 0000000..f31e7b2 --- /dev/null +++ b/testing/io/itk/CMakeLists.txt @@ -0,0 +1,26 @@ +#----------------------------------------------------------------------------- +# 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}/MatchPointLogo.mhd") + +ADD_TEST(ITKIOTest ${ITKIO_TEST} ITKIOTest +"${TEST_DATA_ROOT}/MatchPointLogo.mhd") + +ADD_TEST(ITKDoseAccessorConverterTest ${ITKIO_TEST} ITKDoseAccessorConverterTest + "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/MatchPointLogo.mhd" ) + + +RTTB_CREATE_TEST_MODULE(rttbITKIO DEPENDS RTTBITKIO RTTBDicomIO PACKAGE_DEPENDS Boost Litmus MatchPoint ITK DCMTK) + diff --git a/testing/io/itk/ITKDoseAccessorConverterTest.cpp b/testing/io/itk/ITKDoseAccessorConverterTest.cpp new file mode 100644 index 0000000..8e53e6f --- /dev/null +++ b/testing/io/itk/ITKDoseAccessorConverterTest.cpp @@ -0,0 +1,138 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include "litCheckMacros.h" +#include "litImageTester.h" +#include "litTestImageIO.h" + +#include "itkImage.h" +#include "itkImageFileReader.h" + +#include "rttbBaseType.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbITKImageDoseAccessor.h" +#include "rttbITKImageFileDoseAccessorGenerator.h" +#include "rttbITKImageDoseAccessorGenerator.h" +#include "rttbITKImageDoseAccessorConverter.h" +#include "rttbDoseAccessorProcessorBase.h" +#include "rttbDoseAccessorConversionSettingInterface.h" +#include "rttbInvalidDoseException.h" + + +namespace rttb +{ + + namespace testing + { + + /*!@brief ITKDoseAccessorConverterTest - test the conversion rttb dose accessor ->itk + 1) test with dicom file (DicomDoseAccessorGenerator) + 2) test with mhd file (ITKImageFileDoseAccessorGenerator) + */ + + int ITKDoseAccessorConverterTest(int argc, char* argv[]) + { + typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: + // 1: dose1 file name + // 2: dose2 file name + + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + + if (argc > 1) + { + RTDOSE_FILENAME = argv[1]; + } + + if (argc > 2) + { + RTDOSE2_FILENAME = argv[2]; + } + + //1) Read dicomFile and test getITKImage() + + io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator(RTDOSE_FILENAME.c_str()); + DoseAccessorPointer doseAccessor(doseAccessorGenerator.generateDoseAccessor()); + + io::itk::ITKImageDoseAccessorConverter itkConverter(doseAccessor); + + CHECK_NO_THROW(itkConverter.process()); + CHECK_NO_THROW(itkConverter.getITKImage()); + + io::itk::ITKDoseImageType::IndexType itkIndex; + itkIndex[0] = itkIndex[1] = itkIndex[2] = 0; + + VoxelGridIndex3D rttbIndex(0, 0, 0); + + CHECK_EQUAL(itkConverter.getITKImage()->GetPixel(itkIndex), doseAccessor->getDoseAt(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), doseAccessor->getDoseAt(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), doseAccessor->getDoseAt(rttbIndex)); + + //2) Read mhdFile and test getITKImage() with Litmus TestImageIO + + io::itk::ITKImageFileDoseAccessorGenerator doseAccessorGenerator2(RTDOSE2_FILENAME.c_str()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + io::itk::ITKImageDoseAccessorConverter itkConverter2(doseAccessor2); + + CHECK_NO_THROW(itkConverter2.process()); + CHECK_NO_THROW(itkConverter2.getITKImage()); + + io::itk::ITKDoseImageType::Pointer expectedImage = + lit::TestImageIO::readImage( + RTDOSE2_FILENAME); + + ::lit::ImageTester + tester; + tester.setExpectedImage(expectedImage); + tester.setActualImage(itkConverter2.getITKImage()); + + CHECK_TESTER(tester); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/itk/ITKDoseAccessorGeneratorTest.cpp b/testing/io/itk/ITKDoseAccessorGeneratorTest.cpp new file mode 100644 index 0000000..8794db1 --- /dev/null +++ b/testing/io/itk/ITKDoseAccessorGeneratorTest.cpp @@ -0,0 +1,97 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include "litCheckMacros.h" + +#include "itkImage.h" +#include "itkImageFileReader.h" + +#include "rttbBaseType.h" +#include "rttbITKImageDoseAccessor.h" +#include "rttbITKImageFileDoseAccessorGenerator.h" +#include "rttbITKImageDoseAccessorGenerator.h" +#include "rttbInvalidDoseException.h" + + +namespace rttb +{ + + namespace testing + { + + /*!@brief ITKDoseAccessorGeneratorTest - test the generators for dicom data + 1) test itk file generator generateDoseAccessor() + 2) test itk generator generateDoseAccessor() + */ + + int ITKDoseAccessorGeneratorTest(int argc, char* argv[]) + { + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: + // 1: mhd/raw file name + + std::string RTDOSE_FILENAME; + + if (argc > 1) + { + RTDOSE_FILENAME = argv[1]; + } + + + /* test ITKFileDoseAccessorGenerator generateDoseAccessor()*/ + CHECK_THROW_EXPLICIT(io::itk::ITKImageFileDoseAccessorGenerator("test.test").generateDoseAccessor(), + core::InvalidDoseException); + CHECK_NO_THROW(io::itk::ITKImageFileDoseAccessorGenerator( + RTDOSE_FILENAME.c_str()).generateDoseAccessor()); + + /* test ITKDoseAccessorGenerator generateDoseAccessor()*/ + typedef itk::Image< DoseTypeGy, 3 > DoseImageType; + typedef itk::ImageFileReader ReaderType; + + DoseImageType::ConstPointer invalidDose = DoseImageType::New(); + + ReaderType::Pointer reader = ReaderType::New(); + + CHECK_THROW_EXPLICIT(io::itk::ITKImageDoseAccessorGenerator( + invalidDose.GetPointer()).generateDoseAccessor(), core::InvalidDoseException); + + reader->SetFileName(RTDOSE_FILENAME); + //important to update the reader (won't work without) + reader->Update(); + + CHECK_NO_THROW(io::itk::ITKImageDoseAccessorGenerator(reader->GetOutput()).generateDoseAccessor()); + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/itk/ITKIOTest.cpp b/testing/io/itk/ITKIOTest.cpp new file mode 100644 index 0000000..6f5b27a --- /dev/null +++ b/testing/io/itk/ITKIOTest.cpp @@ -0,0 +1,169 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbITKImageDoseAccessor.h" +#include "rttbITKImageFileDoseAccessorGenerator.h" +#include "rttbITKImageDoseAccessorGenerator.h" + + +namespace rttb +{ + + namespace testing + { + + /*!@brief ITKIOTest - test the IO for ITK data + 1) test ITK dose import if geometric info was set correctly + 2) test ITKImageFileDoseAccessorGenerator accessing dose data and converting (*.mhd file) + 3) test ITKImageDoseAccessorGenerator accessing dose data and converting (*.mhd file) + + WARNING: The values for comparison need to be adjusted if the input files are changed! + */ + + int ITKIOTest(int argc, char* argv[]) + { + typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + //ARGUMENTS: + // 1: mhd file name + + std::string RTDOSE_FILENAME; + + if (argc > 1) + { + RTDOSE_FILENAME = argv[1]; + } + + /* read dose in *.mhd file */ + io::itk::ITKImageFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_FILENAME.c_str()); + DoseAccessorPointer doseAccessor1(doseAccessorGenerator1.generateDoseAccessor()); + + //1) test ITK dose import if geometric info was set correctly + core::GeometricInfo geoInfo = doseAccessor1->getGeometricInfo(); + CHECK_EQUAL(100, geoInfo.getNumRows()); + CHECK_EQUAL(100, geoInfo.getNumColumns()); + CHECK_EQUAL(60, geoInfo.getNumSlices()); + //orientation matrix equals identity matrix + CHECK_EQUAL(OrientationMatrix(), geoInfo.getOrientationMatrix()); + + const VoxelGridID start = 0; + const VoxelGridIndex3D start3D(0); + + VoxelGridID end, inbetween, inbetween2; + VoxelGridIndex3D end3D, inbetween3D, inbetween23D; + + //2) test ITK dose import accessing dose data and converting (*.mhd file) + CHECK(doseAccessor1->getGeometricInfo().validID(start)); + CHECK(doseAccessor1->getGeometricInfo().validIndex(start3D)); + + CHECK(!(doseAccessor1->getGeometricInfo().validID(doseAccessor1->getGridSize()))); + CHECK(!(doseAccessor1->getGeometricInfo().validIndex(VoxelGridIndex3D( + doseAccessor1->getGridSize())))); + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(start)); + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor1-> getDoseAt(start3D)); + + inbetween = 204837; + doseAccessor1->getGeometricInfo().convert(inbetween, inbetween3D); + CHECK(doseAccessor1->getGeometricInfo().validID(inbetween)); + CHECK(doseAccessor1->getGeometricInfo().validIndex(inbetween3D)); + + CHECK_EQUAL(242.0, doseAccessor1->getDoseAt(inbetween)); + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor1-> getDoseAt(inbetween3D)); + + inbetween2 = 283742; + doseAccessor1->getGeometricInfo().convert(inbetween2, inbetween23D); + CHECK(doseAccessor1->getGeometricInfo().validID(inbetween2)); + CHECK(doseAccessor1->getGeometricInfo().validIndex(inbetween23D)); + CHECK_EQUAL(111.0, doseAccessor1->getDoseAt(inbetween2)); + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween2), doseAccessor1-> getDoseAt(inbetween23D)); + + end = doseAccessor1->getGridSize() - 1; + doseAccessor1->getGeometricInfo().convert(end, end3D); + CHECK(doseAccessor1->getGeometricInfo().validID(end)); + CHECK(doseAccessor1->getGeometricInfo().validIndex(end3D)); + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(end)); + CHECK_EQUAL(doseAccessor1->getDoseAt(end), doseAccessor1-> getDoseAt(end3D)); + + typedef itk::Image< DoseTypeGy, 3 > DoseImageType; + typedef itk::ImageFileReader ReaderType; + + ReaderType::Pointer reader = ReaderType::New(); + + reader->SetFileName(RTDOSE_FILENAME); + //important to update the reader (won't work without) + reader->Update(); + io::itk::ITKImageDoseAccessorGenerator doseAccessorGenerator2(reader->GetOutput()); + DoseAccessorPointer doseAccessor2(doseAccessorGenerator2.generateDoseAccessor()); + + //3) test ITK dose import accessing dose data and converting (ITKImageDoseAccessor) + CHECK(doseAccessor2->getGeometricInfo().validID(start)); + CHECK(doseAccessor2->getGeometricInfo().validIndex(start3D)); + + CHECK(!(doseAccessor2->getGeometricInfo().validID(doseAccessor2->getGridSize()))); + CHECK(!(doseAccessor2->getGeometricInfo().validIndex(VoxelGridIndex3D( + doseAccessor2->getGridSize())))); + + CHECK_EQUAL(0, doseAccessor2->getDoseAt(start)); + CHECK_EQUAL(doseAccessor2->getDoseAt(start), doseAccessor2->getDoseAt(start3D)); + + CHECK(doseAccessor2->getGeometricInfo().validID(inbetween)); + CHECK(doseAccessor2->getGeometricInfo().validIndex(inbetween3D)); + + CHECK_EQUAL(242.0, doseAccessor2->getDoseAt(inbetween)); + CHECK_EQUAL(doseAccessor2->getDoseAt(inbetween), doseAccessor2->getDoseAt(inbetween3D)); + + CHECK(doseAccessor2->getGeometricInfo().validID(inbetween2)); + CHECK(doseAccessor2->getGeometricInfo().validIndex(inbetween23D)); + + CHECK_EQUAL(111.0, doseAccessor2->getDoseAt(inbetween2)); + CHECK_EQUAL(doseAccessor2->getDoseAt(inbetween2), doseAccessor2->getDoseAt(inbetween23D)); + + end = doseAccessor2->getGridSize() - 1; + doseAccessor2->getGeometricInfo().convert(end, end3D); + CHECK(doseAccessor2->getGeometricInfo().validID(end)); + CHECK(doseAccessor2->getGeometricInfo().validIndex(end3D)); + + CHECK_EQUAL(0, doseAccessor2->getDoseAt(end)); + CHECK_EQUAL(doseAccessor2->getDoseAt(end), doseAccessor2-> getDoseAt(end3D)); + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/itk/files.cmake b/testing/io/itk/files.cmake new file mode 100644 index 0000000..5cc66aa --- /dev/null +++ b/testing/io/itk/files.cmake @@ -0,0 +1,9 @@ +SET(CPP_FILES + ITKDoseAccessorGeneratorTest.cpp + ITKDoseAccessorConverterTest.cpp + ITKIOTest.cpp + rttbITKIOTests.cpp + ) + +SET(H_FILES +) diff --git a/testing/io/itk/rttbITKIOTests.cpp b/testing/io/itk/rttbITKIOTests.cpp new file mode 100644 index 0000000..24e286e --- /dev/null +++ b/testing/io/itk/rttbITKIOTests.cpp @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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 + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include "litMultiTestsMain.h" + +namespace rttb{ + namespace testing{ + + void registerTests() + { + LIT_REGISTER_TEST(ITKDoseAccessorGeneratorTest); + LIT_REGISTER_TEST(ITKDoseAccessorConverterTest); + LIT_REGISTER_TEST(ITKIOTest); + } + } + } + +int main(int argc, char* argv[]) + { + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + + catch(...) + { + result = -1; + } + + return result; + } diff --git a/testing/io/other/CMakeLists.txt b/testing/io/other/CMakeLists.txt new file mode 100644 index 0000000..5c69bad --- /dev/null +++ b/testing/io/other/CMakeLists.txt @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(OTHERIO_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbOtherIOTests) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- + +ADD_TEST(DoseStatisticsIOTest ${OTHERIO_TEST} DoseStatisticsIOTest) + +ADD_TEST(DVHXMLIOTest ${OTHERIO_TEST} DVHXMLIOTest "${TEST_DATA_ROOT}/TestDVH/dvh_1.txt") + +ADD_TEST(OtherIOTest ${OTHERIO_TEST} OtherIOTest) + +RTTB_CREATE_TEST_MODULE(rttbOtherIO DEPENDS RTTBOtherIO PACKAGE_DEPENDS Boost Litmus) + diff --git a/testing/io/other/DVHXMLIOTest.cpp b/testing/io/other/DVHXMLIOTest.cpp new file mode 100644 index 0000000..39d0fb8 --- /dev/null +++ b/testing/io/other/DVHXMLIOTest.cpp @@ -0,0 +1,139 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbDVHSet.h" +#include "rttbDVHTxtFileReader.h" +#include "rttbDVHTxtFileWriter.h" +#include "rttbDVHXMLFileReader.h" +#include "rttbDVHXMLFileWriter.h" +#include "rttbInvalidParameterException.h" +#include "rttbNullPointerException.h" + +#include "../../core/DummyDVHGenerator.h" + + +namespace rttb{ + + namespace testing{ + + /*! @brief DVHXMLIOTest - test the IO for DVH xml data + 1) test writing dvh to xml file + 2) test reading DVH from xml file + 3) test reading dvh from txt file and writing to xml, check equal + */ + + int DVHXMLIOTest(int argc, char* argv[] ) + { + typedef core::DVHSet::DVHSetType DVHSetType; + typedef core::DVH::DVHPointer DVHPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + + std::string DVHTXT_FILENAME; + + if (argc>1) + { + DVHTXT_FILENAME = argv[1]; + } + + /* generate dummy DVH */ + const IDType structureIDPrefix = "myStructure"; + const IDType doseID = "myDose"; + + DummyDVHGenerator dvhGenerator; + + DVHPointer spMyDVH = boost::make_shared(dvhGenerator.generateDVH(structureIDPrefix,doseID)); + + // 1) test writing DVH to xml file + DVHType typeCum = {DVHType::Cumulative}; + DVHType typeDiff = {DVHType::Differential}; + FileNameString fN1 = "test.xml"; + CHECK_NO_THROW(io::other::DVHXMLFileWriter(fN1, typeDiff)); + CHECK_NO_THROW(io::other::DVHXMLFileWriter(fN1, typeCum)); + + io::other::DVHXMLFileWriter dvhWriter (fN1, typeCum); + + CHECK_EQUAL(fN1, dvhWriter.getFileName()); + + FileNameString fN2 = "otherFile.xml"; + CHECK_NO_THROW(dvhWriter.setFileName(fN2)); + CHECK_EQUAL(fN2, dvhWriter.getFileName()); + + CHECK_EQUAL(DVHType::Cumulative, dvhWriter.getDVHType().Type); + CHECK_NO_THROW(dvhWriter.setDVHType(typeDiff)); + CHECK_EQUAL(DVHType::Differential, dvhWriter.getDVHType().Type); + + DVHPointer emptyDvh; + CHECK_THROW_EXPLICIT(dvhWriter.writeDVH(emptyDvh), core::NullPointerException); + //CHECK_THROW_EXPLICIT(dvhWriter.writeDVH(spMyDVH), core::InvalidParameterException); + CHECK_NO_THROW(dvhWriter.setDVHType(typeDiff)); + CHECK_NO_THROW(dvhWriter.writeDVH(spMyDVH)); + + // 2) test reading DVH from xml file + io::other::DVHXMLFileReader dvhReader(fN2); + + DVHPointer importedDVH = dvhReader.generateDVH(); + + CHECK_EQUAL(*importedDVH, *spMyDVH); + + //3) test reading dvh from txt file and writing to xml + io::other::DVHTxtFileReader dvhReader2(DVHTXT_FILENAME); + DVHPointer importedDVH2 = dvhReader2.generateDVH(); + FileNameString toWrite = "test.xml"; + io::other::DVHXMLFileWriter xmlWriter(toWrite, typeDiff); + xmlWriter.writeDVH(importedDVH2); + + io::other::DVHXMLFileReader xmlReader(toWrite); + DVHPointer readDVH=xmlReader.generateDVH(); + const double errorConstant = 1e-7; + + CHECK_CLOSE(importedDVH2->getDeltaD(), readDVH->getDeltaD(), errorConstant); + CHECK_CLOSE(importedDVH2->getDeltaV(), readDVH->getDeltaV(), errorConstant); + CHECK(importedDVH2->getDoseID() == readDVH->getDoseID()); + CHECK(importedDVH2->getStructureID() == readDVH->getStructureID()); + CHECK_CLOSE(importedDVH2->getMaximum(), readDVH->getMaximum(), errorConstant); + CHECK_CLOSE(importedDVH2->getMinimum(), readDVH->getMinimum(), errorConstant); + CHECK_CLOSE(importedDVH2->getMean(), readDVH->getMean(), errorConstant); + CHECK(importedDVH2->getDataDifferential().size() == readDVH->getDataDifferential().size()); + for(int i=0; igetDataDifferential().size(); i++){ + CHECK_CLOSE(importedDVH2->getDataDifferential().at(i), readDVH->getDataDifferential().at(i), errorConstant); + } + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/other/DoseStatisticsIOTest.cpp b/testing/io/other/DoseStatisticsIOTest.cpp new file mode 100644 index 0000000..b85aca5 --- /dev/null +++ b/testing/io/other/DoseStatisticsIOTest.cpp @@ -0,0 +1,96 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDoseStatistics.h" +#include "rttbDoseStatisticsXMLWriter.h" +#include "rttbGenericDoseIterator.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbInvalidParameterException.h" +#include "rttbNullPointerException.h" + +#include "../../core/DummyDoseAccessor.h" + + +namespace rttb{ + + namespace testing{ + + /*! @brief OtherIOTest - test the IO for dose statistics + 1) test exception + 2) test writing statistcs to xml file + */ + + int DoseStatisticsIOTest(int argc, char* argv[] ) + { + typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; + typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; + typedef boost::shared_ptr DoseStatisticsPtr; + + PREPARE_DEFAULT_TEST_REPORTING; + + /* generate dummy dose */ + boost::shared_ptr spTestDoseAccessor = boost::make_shared(); + DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); + + boost::shared_ptr spTestDoseIterator = + boost::make_shared(spDoseAccessor); + DoseIteratorPointer spDoseIterator (spTestDoseIterator); + + rttb::algorithms::DoseStatistics myDoseStats(spDoseIterator); + DoseStatisticsPtr myDoseStatsPtr=boost::make_shared(myDoseStats); + + /* test exception */ + CHECK_THROW_EXPLICIT(io::other::writeDoseStatistics(myDoseStatsPtr,"test.test", 0),core::InvalidParameterException); + + + /* test writing statistcs to xml file */ + FileNameString fN="testStatistics.xml"; + CHECK_NO_THROW(io::other::writeDoseStatistics(myDoseStatsPtr,fN)); + + /* test writing statistcs to string */ + boost::property_tree::ptree pt = io::other::writeDoseStatistics(myDoseStatsPtr); + XMLString str = io::other::writerDoseStatisticsToString(myDoseStatsPtr); + + std::stringstream sstr; + boost::property_tree::xml_parser::write_xml(sstr,pt); + CHECK_EQUAL(str, sstr.str()); + + + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/other/OtherIOTest.cpp b/testing/io/other/OtherIOTest.cpp new file mode 100644 index 0000000..86cbf30 --- /dev/null +++ b/testing/io/other/OtherIOTest.cpp @@ -0,0 +1,116 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDVH.h" +#include "rttbDVHSet.h" +#include "rttbDVHTxtFileReader.h" +#include "rttbDVHTxtFileWriter.h" +#include "rttbInvalidParameterException.h" +#include "rttbNullPointerException.h" + +#include "../../core/DummyDVHGenerator.h" + + +namespace rttb{ + + namespace testing{ + + /*! @brief OtherIOTest - test the IO for DVH txt data + 1) test writing dvh to text file + 2) test reading DVH from text file + */ + + int OtherIOTest(int argc, char* argv[] ) + { + typedef core::DVHSet::DVHSetType DVHSetType; + typedef core::DVH::DVHPointer DVHPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + + /* generate dummy DVH */ + const IDType structureIDPrefix = "myStructure"; + const IDType doseID = "myDose"; + + DummyDVHGenerator dvhGenerator; + + DVHPointer spMyDVH = boost::make_shared(dvhGenerator.generateDVH(structureIDPrefix,doseID)); + + // 1) test writing DVH to text file + DVHType typeCum = {DVHType::Cumulative}; + DVHType typeDiff = {DVHType::Differential}; + FileNameString fN1 = "test.txt"; + CHECK_NO_THROW(io::other::DVHTxtFileWriter(fN1, typeDiff)); + CHECK_NO_THROW(io::other::DVHTxtFileWriter(fN1, typeCum)); + + + io::other::DVHTxtFileWriter dvhWriter (fN1, typeCum); + + CHECK_EQUAL(fN1, dvhWriter.getFileName()); + + FileNameString fN2 = "otherFile.txt"; + CHECK_NO_THROW(dvhWriter.setFileName(fN2)); + CHECK_EQUAL(fN2, dvhWriter.getFileName()); + + CHECK_EQUAL(DVHType::Cumulative, dvhWriter.getDVHType().Type); + CHECK_NO_THROW(dvhWriter.setDVHType(typeDiff)); + CHECK_EQUAL(DVHType::Differential, dvhWriter.getDVHType().Type); + + DVHPointer emptyDvh; + CHECK_THROW_EXPLICIT(dvhWriter.writeDVH(emptyDvh),core::NullPointerException); + CHECK_NO_THROW(dvhWriter.setDVHType(typeDiff)); + CHECK_NO_THROW(dvhWriter.writeDVH(spMyDVH)); + + // 2) test reading DVH from text file + CHECK_NO_THROW(io::other::DVHTxtFileReader dvhReader(fN1)); + io::other::DVHTxtFileReader dvhReaderTest(fN1); + CHECK_THROW_EXPLICIT(dvhReaderTest.generateDVH(),core::InvalidParameterException); + CHECK_NO_THROW(io::other::DVHTxtFileReader dvhReader(fN2)); + + io::other::DVHTxtFileReader dvhReader(fN2); + + CHECK_NO_THROW(dvhReader.resetFileName(fN1)); + CHECK_THROW_EXPLICIT(dvhReader.generateDVH(), core::InvalidParameterException); + CHECK_NO_THROW(dvhReader.resetFileName(fN2)); + + CHECK_NO_THROW(dvhReader.generateDVH()); + + DVHPointer importedDVH = dvhReader.generateDVH(); + + CHECK_EQUAL(*importedDVH, *spMyDVH); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/other/files.cmake b/testing/io/other/files.cmake new file mode 100644 index 0000000..e1ecb94 --- /dev/null +++ b/testing/io/other/files.cmake @@ -0,0 +1,13 @@ +SET(CPP_FILES + ../../core/DummyDoseAccessor.cpp + ../../core/DummyDVHGenerator.cpp + DoseStatisticsIOTest.cpp + DVHXMLIOTest.cpp + OtherIOTest.cpp + rttbIOTests.cpp + ) + +SET(H_FILES + ../../core/DummyDoseAccessor.h + ../../core/DummyDVHGenerator.h +) diff --git a/testing/io/other/rttbIOTests.cpp b/testing/io/other/rttbIOTests.cpp new file mode 100644 index 0000000..b902728 --- /dev/null +++ b/testing/io/other/rttbIOTests.cpp @@ -0,0 +1,60 @@ +// ----------------------------------------------------------------------- +// 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 + +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + +#include "litMultiTestsMain.h" + +namespace rttb{ + namespace testing{ + + void registerTests() + { + LIT_REGISTER_TEST(DoseStatisticsIOTest); + LIT_REGISTER_TEST(DVHXMLIOTest); + LIT_REGISTER_TEST(OtherIOTest); + } + } +} + +int main(int argc, char* argv[]) +{ + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + + catch(...) + { + result = -1; + } + + return result; +} diff --git a/testing/io/rttbDoseAccessorTester.cpp b/testing/io/rttbDoseAccessorTester.cpp new file mode 100644 index 0000000..c371b95 --- /dev/null +++ b/testing/io/rttbDoseAccessorTester.cpp @@ -0,0 +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-> getDoseAt(id) != _referenceDose-> getDoseAt(id3D)) + || (_compareDose->getDoseAt(id) != _compareDose-> getDoseAt(id3D))) + { + _conversionFailed = true; + _failedID = id; + return false; + } + + if ((_referenceDose->getDoseAt(id) != _compareDose->getDoseAt(id)) + || (_referenceDose->getDoseAt(id3D) != _compareDose->getDoseAt(id3D))) + { + float tmpDifference = abs(_referenceDose->getDoseAt(id) - _compareDose->getDoseAt(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 new file mode 100644 index 0000000..0e57604 --- /dev/null +++ b/testing/io/rttbDoseAccessorTester.h @@ -0,0 +1,93 @@ +// ----------------------------------------------------------------------- +// 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/trunk/testing/core/DVHCalculatorTest.cpp $ +*/ + +#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 + 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 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/CMakeLists.txt b/testing/io/virtuos/CMakeLists.txt new file mode 100644 index 0000000..68840d9 --- /dev/null +++ b/testing/io/virtuos/CMakeLists.txt @@ -0,0 +1,52 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(VIRTUOSIO_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbVirtuosIOTests) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(VirtuosDoseAccessorGeneratorTest ${VIRTUOSIO_TEST} VirtuosDoseAccessorGeneratorTest +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.pln" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.dos.gz" +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.dos") + +ADD_TEST(VirtuosDoseIOTest ${VIRTUOSIO_TEST} VirtuosDoseIOTest +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.pln" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.dos.gz" +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.dos") + +#ADD_TEST(VirtuosDoseIOTest ${VIRTUOSIO_TEST} VirtuosDoseIOTest +#"${TEST_DATA_ROOT}/Virtuos/0001220308_Loga_Ivonne/0001220308_Loga_Ivonne111.pln" "${TEST_DATA_ROOT}/Virtuos/0001220308_Loga_Ivonne/0001220308_Loga_Ivonne111.dos.gz" +#"${TEST_DATA_ROOT}/Virtuos/0001220308_Loga_Ivonne/0001220308_Loga_Ivonne112.dos.gz") + +ADD_TEST(VirtuosStructureIOTest ${VIRTUOSIO_TEST} VirtuosStructureIOTest +"${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.vdx" "${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.ctx.gz" +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac000.vdx" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac000.ctx.gz" +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac101.dos.gz") + +ADD_TEST(VirtuosStructureSetGeneratorTest ${VIRTUOSIO_TEST} VirtuosStructureSetGeneratorTest +"${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.vdx" "${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.ctx.gz" +"${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac000.vdx" "${TEST_DATA_ROOT}/Virtuos/prostate_ac/prostate_ac000.ctx.gz" +) + +#trip data +ADD_TEST(TripStructureIOTest ${VIRTUOSIO_TEST} TripStructureIOTest +"${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.vdx" "${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.ctx.gz" "${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030000.ctx" +"${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030101.dos.gz" "${TEST_DATA_ROOT}/Virtuos/NHHTrip/NHH030101.dos") + +ADD_TEST(TripDoseIOTest ${VIRTUOSIO_TEST} TripDoseIOTest +"${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030101.pln" "${TEST_DATA_ROOT}/Virtuos/NHH030/NHH030101.dos.gz" "${TEST_DATA_ROOT}/Virtuos/NHHTrip/NHH030101.dos") + +ADD_TEST(VirtuosDVHCalculatorExampleTest ${VIRTUOSIO_TEST} VirtuosDVHCalculatorExampleTest +"${TEST_DATA_ROOT}/Virtuos/MPM_LR_ah/MPM_LR_ah101.dos.gz" +"${TEST_DATA_ROOT}/Virtuos/MPM_LR_ah/MPM_LR_ah000.vdx" +"${TEST_DATA_ROOT}/Virtuos/MPM_LR_ah/MPM_LR_ah101.pln" +"${TEST_DATA_ROOT}/Virtuos/MPM_LR_ah/MPM_LR_ah000.ctx.gz" ) + +RTTB_CREATE_TEST_MODULE(rttbVirtuosIO DEPENDS RTTBVirtuosIO RTTBMasks PACKAGE_DEPENDS Boost Litmus VirtuosIO) + diff --git a/testing/io/virtuos/TripDoseIOTest.cpp b/testing/io/virtuos/TripDoseIOTest.cpp new file mode 100644 index 0000000..836b77b --- /dev/null +++ b/testing/io/virtuos/TripDoseIOTest.cpp @@ -0,0 +1,236 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbVirtuosPlanFileDoseAccessorGenerator.h" +#include "rttbVirtuosDoseFileDoseAccessorGenerator.h" +#include "rttbVirtuosDoseAccessor.h" +#include "rttbVirtuosFileStructureSetGenerator.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "rttbIndexOutOfBoundsException.h" +#include "../rttbDoseAccessorTester.h" + + +namespace rttb +{ + + namespace testing + { + + /*! @brief TripDoseIOTest - test the IO for virtuos data + 2) test virtuos dose import if geometric info was set correctly + 3) test trip dose import accessing dose data and converting + + Since the same class is used for the import of VIRTUOS structures, the constructors are + tested more thoroughly in VirtuosDoseIOTest. + + WARNING: The values for comparison need to be adjusted if the input files are changed! + + @see VirtuosDoseIOTest + + */ + + int TripDoseIOTest(int argc, char* argv[]) + { + typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + // 1: plan file name (virtuos) .../testing/data/Virtuos/NHH030101.pln + // 2: dose1 file name (virtuos) .../testing/data/Virtuos/NHH030101.dos.gz + // 3: dose2 file name (trip): .../testing/data/Virtuos/trip/NHH030101.dos + // WARNING: Test will fail if dose2 does not contain the same dose as dose1! + + std::string RTPLAN_FILENAME; + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + + if (argc > 1) + { + RTPLAN_FILENAME = argv[1]; + } + + if (argc > 2) + { + RTDOSE_FILENAME = argv[2]; + } + + if (argc > 3) + { + RTDOSE2_FILENAME = argv[3]; + } + + + /* import VIRTUOS dose */ + io::virtuos::VirtuosPlanFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_FILENAME.c_str(), + RTPLAN_FILENAME.c_str()); + boost::shared_ptr doseAccessor1 = + boost::static_pointer_cast + (doseAccessorGenerator1.generateDoseAccessor()); + + + //CHECK_EQUAL(76.0,spDoseAccessor1->getPrescribedDose()); + //CHECK_EQUAL(885.0,spDoseAccessor1->getNormalizationDose()); + + //2) test dose import if geometric info was set correctly + core::GeometricInfo geoInfo = doseAccessor1->getGeometricInfo(); + CHECK_EQUAL(256, geoInfo.getNumRows()); + CHECK_EQUAL(256, geoInfo.getNumColumns()); + CHECK_EQUAL(165, geoInfo.getNumSlices()); + CHECK_EQUAL(256 * 256 * 165, doseAccessor1->getGridSize()); + CHECK_EQUAL(OrientationMatrix(), geoInfo.getOrientationMatrix()); + + const VoxelGridID start = 0; + const VoxelGridIndex3D start3D(0); + + VoxelGridID end, inbetween; + VoxelGridIndex3D end3D, inbetween3D; + + //3) test dose import accessing dose data and converting + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(start)); + CHECK_EQUAL(0, doseAccessor1-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor1-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor1->getGridSize() / 2.0)); + doseAccessor1->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(inbetween)); + CHECK_EQUAL(0, doseAccessor1-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor1-> getDoseAt(inbetween3D)); + + end = doseAccessor1->getGridSize() - 1; + doseAccessor1->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(end)); + CHECK_EQUAL(0, doseAccessor1-> getDoseAt(end3D)); + CHECK_EQUAL(doseAccessor1->getDoseAt(end), doseAccessor1-> getDoseAt(end3D)); + + /* Dose without plan */ + io::virtuos::VirtuosDoseFileDoseAccessorGenerator doseAccessorGenerator2(RTDOSE2_FILENAME.c_str(), + doseAccessor1->getNormalizationDose(), + doseAccessor1->getPrescribedDose()); + boost::shared_ptr doseAccessor2 = + boost::static_pointer_cast + (doseAccessorGenerator2.generateDoseAccessor()); + + + CHECK_EQUAL(doseAccessor1->getPrescribedDose(), doseAccessor2->getPrescribedDose()); + CHECK_EQUAL(doseAccessor1->getNormalizationDose(), doseAccessor2->getNormalizationDose()); + + + //2) test dose import if geometric info was set correctly + core::GeometricInfo geoInfo2 = doseAccessor2->getGeometricInfo(); + CHECK_EQUAL(geoInfo.getNumRows(), geoInfo2.getNumRows()); + CHECK_EQUAL(geoInfo.getNumColumns(), geoInfo2.getNumColumns()); + CHECK_EQUAL(geoInfo.getNumSlices(), geoInfo2.getNumSlices()); + CHECK_EQUAL(doseAccessor1->getGridSize(), doseAccessor2->getGridSize()); + CHECK_EQUAL(geoInfo.getOrientationMatrix(), geoInfo2.getOrientationMatrix()); + + //3) test dose import accessing dose data and converting + + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor2->getDoseAt(start)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(start3D), doseAccessor2-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(start), doseAccessor2-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor2->getGridSize() / 2.0)); + doseAccessor2->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor2->getDoseAt(inbetween)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(inbetween3D), doseAccessor2-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(inbetween), doseAccessor2-> getDoseAt(inbetween3D)); + + end = doseAccessor2->getGridSize() - 1; + doseAccessor2->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(end), doseAccessor2->getDoseAt(end)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(end3D), doseAccessor2-> getDoseAt(end3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(end), doseAccessor2-> getDoseAt(end3D)); + + /* Import Trip dose */ + io::virtuos::VirtuosDoseFileDoseAccessorGenerator doseAccessorGenerator3(RTDOSE2_FILENAME.c_str(), + doseAccessor1->getNormalizationDose(), + doseAccessor1->getPrescribedDose()); + boost::shared_ptr doseAccessor3 = + boost::static_pointer_cast + (doseAccessorGenerator3.generateDoseAccessor()); + + CHECK_EQUAL(doseAccessor1->getPrescribedDose(), doseAccessor3->getPrescribedDose()); + CHECK_EQUAL(doseAccessor1->getNormalizationDose(), doseAccessor3->getNormalizationDose()); + + + //2) test dose import if geometric info was set correctly + core::GeometricInfo geoInfo3 = doseAccessor3->getGeometricInfo(); + CHECK_EQUAL(geoInfo.getNumRows(), geoInfo3.getNumRows()); + CHECK_EQUAL(geoInfo.getNumColumns(), geoInfo3.getNumColumns()); + CHECK_EQUAL(geoInfo.getNumSlices(), geoInfo3.getNumSlices()); + CHECK_EQUAL(doseAccessor1->getGridSize(), doseAccessor3->getGridSize()); + CHECK_EQUAL(geoInfo.getOrientationMatrix(), geoInfo3.getOrientationMatrix()); + + //3) test dose import accessing dose data and converting + + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor3->getDoseAt(start)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(start3D), doseAccessor3-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor3->getDoseAt(start), doseAccessor3-> getDoseAt(start3D)); + + inbetween = int(std::floor(doseAccessor3->getGridSize() / 2.0)); + doseAccessor3->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor3->getDoseAt(inbetween)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(inbetween3D), doseAccessor3-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor3->getDoseAt(inbetween), doseAccessor3-> getDoseAt(inbetween3D)); + + io::virtuos::VirtuosPlanFileDoseAccessorGenerator doseAccessorGenerator4(RTDOSE2_FILENAME.c_str(), + RTPLAN_FILENAME.c_str()); + boost::shared_ptr spDoseAccessor4 = + boost::static_pointer_cast + (doseAccessorGenerator4.generateDoseAccessor()); + DoseAccessorPointer doseAccessor4(spDoseAccessor4); + + DoseAccessorTester doseCompare(doseAccessor1, doseAccessor2); + CHECK_TESTER(doseCompare); + + DoseAccessorTester doseCompare2(doseAccessor3, doseAccessor4); + CHECK_TESTER(doseCompare2); + + DoseAccessorTester doseCompare3(doseAccessor1, doseAccessor3); + CHECK_TESTER(doseCompare3); + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/virtuos/TripStructureIOTest.cpp b/testing/io/virtuos/TripStructureIOTest.cpp new file mode 100644 index 0000000..6e4b212 --- /dev/null +++ b/testing/io/virtuos/TripStructureIOTest.cpp @@ -0,0 +1,227 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#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 TripStructureIOTest - test the IO for virtuos data + 1) test virtuos structure import + + Since the same class is used for the import of VIRTUOS structures, the constructors are + tested more thoroughly in VirtuosStructureIOTest. + + WARNING: The values for comparison need to be adjusted if the input files are changed! + + @see VirtuosStructureIOTest + + @todo Test is sucessful for import in any CT (ctx) and on virtuos doses. + import on trip dose file will be handled by a new issue. Preliminary code can be found + at the end of this file. + */ + + int TripStructureIOTest(int argc, char* argv[] ) + { + typedef core::StructureSetGeneratorInterface::StructureSetPointer StructureSetPointer; + typedef core::StructureSet::StructTypePointer StructTypePointer; + + 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" + // 3: ctx file name (trip) ".../testing/data/Virtuos/NHH030000.ctx" + // 4: dose file name (virtuos) ".../testing/data/Virtuos/NHH030101.dos.gz" + // 5: dose file name (trip) ".../testing/data/Virtuos/NHH030101.dos" + + /*! @todo provide an adequate number of test data. Does not work with other test data currently provided. */ + + std::string RTSTRUCT_FILENAME; + std::string CT_FILENAME; + std::string CT_FILENAME_TRIP; + std::string DOSE_FILENAME; + std::string DOSE_FILENAME_TRIP; + + if (argc>1) + { + RTSTRUCT_FILENAME = argv[1]; + } + if (argc>2) + { + CT_FILENAME = argv[2]; + } + if (argc>3) + { + CT_FILENAME_TRIP = argv[3]; + } + if (argc>4) + { + DOSE_FILENAME = argv[4]; + } + if (argc>5) + { + DOSE_FILENAME_TRIP = argv[5]; + } + + /* structure set */ + StructureSetPointer rtStructureSet=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(),CT_FILENAME.c_str()).generateStructureSet(); + //1) test structure import + CHECK_EQUAL(1,rtStructureSet->getNumberOfStructures()); + + CHECK_EQUAL("TARGET_1",(rtStructureSet->getStructure(0))->getLabel()); + + core::Structure myStructure = *(rtStructureSet->getStructure(0)); + CHECK_EQUAL(320,myStructure.getNumberOfEndpoints()); + + PolygonSequenceType myPolygonSeq = myStructure.getStructureVector(); + CHECK_EQUAL(8,myPolygonSeq.size()); + + PolygonType myPolygon = myPolygonSeq.at(0); + + CHECK_CLOSE(125.975,myPolygon.at(0).x(),errorConstant); + CHECK_CLOSE(118.120,myPolygon.at(0).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(0).z(),errorConstant); + + CHECK_CLOSE(124.487,myPolygon.at(1).x(),errorConstant); + CHECK_CLOSE(118.477,myPolygon.at(1).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(1).z(),errorConstant); + + CHECK_CLOSE(117.870,myPolygon.at(10).x(),errorConstant); + CHECK_CLOSE(129.275,myPolygon.at(10).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(10).z(),errorConstant); + + CHECK_CLOSE(129.025,myPolygon.at(20).x(),errorConstant); + CHECK_CLOSE(137.380,myPolygon.at(20).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(20).z(),errorConstant); + + CHECK_CLOSE(127.5,myPolygon.at(myPolygon.size()-1).x(),errorConstant); + CHECK_CLOSE(118,myPolygon.at(myPolygon.size()-1).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(myPolygon.size()-1).z(),errorConstant); + + //The original Virtuos contours are closed (last point = 1st point), but the ones used here do not have this double point. + + + /* structure set on trip */ + StructureSetPointer rtStructureSet2=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(),CT_FILENAME_TRIP.c_str()).generateStructureSet(); + //1) test structure import + CHECK_EQUAL(1,rtStructureSet2->getNumberOfStructures()); + CHECK_EQUAL(rtStructureSet2->getNumberOfStructures(),rtStructureSet2->getNumberOfStructures()); + + CHECK_EQUAL("TARGET_1",(rtStructureSet2->getStructure(0))->getLabel()); + + for(int s = 0; s < rtStructureSet2->getNumberOfStructures(); s++){ + CHECK_EQUAL((rtStructureSet->getStructure(s))->getLabel(),(rtStructureSet2->getStructure(s))->getLabel()); + + core::Structure myStructure2 = *(rtStructureSet2->getStructure(s)); + core::Structure myStructure1c = *(rtStructureSet->getStructure(s)); + CHECK_EQUAL(myStructure1c.getNumberOfEndpoints(),myStructure2.getNumberOfEndpoints()); + PolygonSequenceType myPolygonSeq2 = myStructure2.getStructureVector(); + PolygonSequenceType myPolygonSeq1c = myStructure2.getStructureVector(); + CHECK_EQUAL(myPolygonSeq1c.size(),myPolygonSeq2.size()); + + for(int ps = 0; ps < myPolygonSeq2.size(); ps++){ + PolygonType myPolygon1c = myPolygonSeq1c.at(ps); + PolygonType myPolygon2 = myPolygonSeq2.at(ps); + CHECK_CLOSE(myPolygon1c.at(0).x(),myPolygon2.at(0).x(),errorConstant); + CHECK_CLOSE(myPolygon1c.at(0).y(),myPolygon2.at(0).y(),errorConstant); + CHECK_CLOSE(myPolygon1c.at(0).z(),myPolygon2.at(0).z(),errorConstant); + } + } + + /* structure set on dose */ + StructureSetPointer rtStructureSet3=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(),DOSE_FILENAME.c_str()).generateStructureSet(); + //1) test structure import, should be equal to structure imported on CT-image + CHECK_EQUAL(1,rtStructureSet3->getNumberOfStructures()); + CHECK_EQUAL(rtStructureSet2->getNumberOfStructures(),rtStructureSet3->getNumberOfStructures()); + + CHECK_EQUAL("TARGET_1",(rtStructureSet3->getStructure(0))->getLabel()); + + for(int s = 0; s < rtStructureSet3->getNumberOfStructures(); s++){ + CHECK_EQUAL((rtStructureSet3->getStructure(s))->getLabel(),(rtStructureSet3->getStructure(s))->getLabel()); + + core::Structure myStructure3 = *(rtStructureSet3->getStructure(s)); + core::Structure myStructure2c = *(rtStructureSet2->getStructure(s)); + CHECK_EQUAL(myStructure2c.getNumberOfEndpoints(),myStructure3.getNumberOfEndpoints()); + PolygonSequenceType myPolygonSeq3 = myStructure3.getStructureVector(); + PolygonSequenceType myPolygonSeq2c = myStructure3.getStructureVector(); + CHECK_EQUAL(myPolygonSeq2c.size(),myPolygonSeq3.size()); + + for(int ps = 0; ps < myPolygonSeq3.size(); ps++){ + PolygonType myPolygon2c = myPolygonSeq2c.at(ps); + PolygonType myPolygon3 = myPolygonSeq3.at(ps); + CHECK_CLOSE(myPolygon2c.at(0).x(),myPolygon3.at(0).x(),errorConstant); + CHECK_CLOSE(myPolygon2c.at(0).y(),myPolygon3.at(0).y(),errorConstant); + CHECK_CLOSE(myPolygon2c.at(0).z(),myPolygon3.at(0).z(),errorConstant); + } + } + + /* structure set on trip dose*/ + /* @todo loading structure fails currently keep code and fix in seperate issue*/ + StructureSetPointer rtStructureSet4=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(),DOSE_FILENAME_TRIP.c_str()).generateStructureSet(); + //1) test structure import, should be equal to structure imported on CT-image + CHECK_EQUAL(1,rtStructureSet4->getNumberOfStructures()); + CHECK_EQUAL(rtStructureSet2->getNumberOfStructures(),rtStructureSet4->getNumberOfStructures()); + + CHECK_EQUAL("TARGET_1",(rtStructureSet4->getStructure(0))->getLabel()); + + int s = 0; + + core::Structure myStructure4 = *(rtStructureSet4->getStructure(s)); + core::Structure myStructure2c = *(rtStructureSet2->getStructure(s)); + CHECK_EQUAL(myStructure2c.getNumberOfEndpoints(),myStructure4.getNumberOfEndpoints()); + PolygonSequenceType myPolygonSeq4 = myStructure4.getStructureVector(); + PolygonSequenceType myPolygonSeq2c = myStructure2c.getStructureVector(); + CHECK_EQUAL(myPolygonSeq2c.size(),myPolygonSeq4.size()); + + for(int ps = 0; ps < myPolygonSeq4.size(); ps++){ + PolygonType myPolygon2c = myPolygonSeq2c.at(ps); + PolygonType myPolygon4 = myPolygonSeq4.at(ps); + CHECK_CLOSE(myPolygon2c.at(0).x(),myPolygon4.at(0).x(),errorConstant); + CHECK_CLOSE(myPolygon2c.at(0).y(),myPolygon4.at(0).y(),errorConstant); + CHECK_CLOSE(myPolygon2c.at(0).z(),myPolygon4.at(0).z(),errorConstant); + } + + /**/ + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp b/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp new file mode 100644 index 0000000..f4963b2 --- /dev/null +++ b/testing/io/virtuos/VirtuosDVHCalculatorExampleTest.cpp @@ -0,0 +1,168 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include + +#include "litCheckMacros.h" + + + + +#include "rttbBaseType.h" +#include "rttbDVHCalculator.h" +#include "rttbGenericMaskedDoseIterator.h" +#include "rttbGenericDoseIterator.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidParameterException.h" +#include "rttbOTBMaskAccessor.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 core::DVHCalculator::MaskedDoseIteratorPointer MaskedDoseIteratorPointer; + typedef masks::OTBMaskAccessor::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>1) + { + Virtuos_Dose_File = argv[1]; + } + if (argc>2) + { + Virtuos_Structure_File = argv[2]; + } + if (argc>3) + { + Virtuos_Plan_File = argv[3]; + } + if (argc>4) + { + 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(); + + for(int i=1;igetNumberOfStructures();i++){ + //if(i=6 || i==8){//todo: assertion bug! + if( i==8){ + + //create MaskAccessor for each structure + boost::shared_ptr spOTBMaskAccessorVirtuos = + boost::make_shared(rtStructureSetVirtuos->getStructure(i), doseAccessorVirtuos->getGeometricInfo()); + + spOTBMaskAccessorVirtuos->updateMask(); + + MaskAccessorPointer spMaskAccessor(spOTBMaskAccessorVirtuos); + + //create corresponding MaskedDoseIterator + + boost::shared_ptr spMaskedDoseIteratorTmp = + boost::make_shared(spMaskAccessor, doseAccessorVirtuos); + + DoseIteratorPointer spMaskedDoseIterator (spMaskedDoseIteratorTmp); + + std::cout << "dvh calc"<getStructure(i))->getUID(),doseAccessorVirtuos->getDoseUID(),0.367944); + else + calc=new rttb::core::DVHCalculator(spMaskedDoseIterator,(rtStructureSetVirtuos->getStructure(i))->getUID(),doseAccessorVirtuos->getDoseUID(),0.510107); + + + DVHPointer dvhPtr=calc->generateDVH(); + + if(i==6) + { + CHECK_CLOSE(42.497532,dvhPtr->getMaximum(),errorConstant); + CHECK_CLOSE(2.7595800000000001,dvhPtr->getMinimum(),errorConstant); + CHECK_CLOSE(7.4752642058590695,dvhPtr->getMean(),errorConstant); + CHECK_CLOSE(6.4390200000000002,dvhPtr->getMedian(),errorConstant); + CHECK_CLOSE(5.7031320000000001,dvhPtr->getModal(),errorConstant); + CHECK_CLOSE(3.5065188466477824,dvhPtr->getStdDeviation(),errorConstant); + CHECK_CLOSE(12.295674421896095,dvhPtr->getVariance(),errorConstant); + CHECK_CLOSE(177218.04900734947,dvhPtr->getNumberOfVoxels(),1e-2); + } + + if(i==8) + { + CHECK_CLOSE(68.099284499999996,dvhPtr->getMaximum(),errorConstant); + CHECK_CLOSE(24.7401895,dvhPtr->getMinimum(),errorConstant); + CHECK_CLOSE(54.384709827101481,dvhPtr->getMean(),errorConstant); + CHECK_CLOSE(54.836502499999995,dvhPtr->getMedian(),errorConstant); + CHECK_CLOSE(54.836502499999995,dvhPtr->getModal(),errorConstant); + CHECK_CLOSE(3.3345924130915088,dvhPtr->getStdDeviation(),errorConstant); + CHECK_CLOSE(11.119506561447452,dvhPtr->getVariance(),errorConstant); + CHECK_CLOSE(338856.04793872859,dvhPtr->getNumberOfVoxels(),1e-2); + } + + } + + } + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/virtuos/VirtuosDoseAccessorGeneratorTest.cpp b/testing/io/virtuos/VirtuosDoseAccessorGeneratorTest.cpp new file mode 100644 index 0000000..47dce5d --- /dev/null +++ b/testing/io/virtuos/VirtuosDoseAccessorGeneratorTest.cpp @@ -0,0 +1,151 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbVirtuosCubeinfoDoseAccessorGenerator.h" +#include "rttbVirtuosDoseFileDoseAccessorGenerator.h" +#include "rttbVirtuosPlanFileDoseAccessorGenerator.h" +#include "rttbVirtuosDoseAccessor.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "../rttbDoseAccessorTester.h" + + +namespace rttb +{ + + namespace testing + { + + /*! @brief VirtuosDoseAccessorGeneratorTest - test the accessor generator generateDoseAccessor() of virtuos data + 1) test virtuos accessor generator with only dose file + 2) test virtuos accessor generator with dose and plan file + 3) test virtuos accessor generator with cubeinfo + */ + + int VirtuosDoseAccessorGeneratorTest(int argc, char* argv[]) + { + + PREPARE_DEFAULT_TEST_REPORTING; + // 1: plan file name (virtuos) .../testing/data/Virtuos/prostate_ac/prostate_ac101.pln + // 2: dose1 file name (virtuos) .../testing/data/Virtuos/prostate_ac/prostate_ac101.dos.gz + // 3: dose2 file name (trip): .../testing/data/Virtuos/prostate_ac/prostate_ac101.dos + // WARNING: Test will fail if dose2 does not contain the same dose as dose1! + + std::string RTPLAN_FILENAME; + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + + if (argc > 1) + { + RTPLAN_FILENAME = argv[1]; + } + + if (argc > 2) + { + RTDOSE_FILENAME = argv[2]; + } + + if (argc > 3) + { + RTDOSE2_FILENAME = argv[3]; + } + + //1) test virtuos accessor generator with only dose file + CHECK_NO_THROW(io::virtuos::VirtuosDoseFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str(), 0, + 0).generateDoseAccessor()); + CHECK_THROW_EXPLICIT( + io::virtuos::VirtuosDoseFileDoseAccessorGenerator("test.dos.gz", 0, 0).generateDoseAccessor(), + core::InvalidParameterException); + + + + //2) test virtuos accessor generator with dose and plan file + CHECK_NO_THROW(io::virtuos::VirtuosPlanFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str(), "")); + CHECK_NO_THROW(io::virtuos::VirtuosPlanFileDoseAccessorGenerator("myDose.dos.gz", + RTPLAN_FILENAME.c_str())); + + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosPlanFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str(), + "").generateDoseAccessor(), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosPlanFileDoseAccessorGenerator("", + RTPLAN_FILENAME.c_str()).generateDoseAccessor(), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosPlanFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str(), + "myPlanfile.pln").generateDoseAccessor(), core::InvalidParameterException); + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosPlanFileDoseAccessorGenerator("myDose.dos.gz", + RTPLAN_FILENAME.c_str()).generateDoseAccessor(), core::InvalidParameterException); + + CHECK_NO_THROW(io::virtuos::VirtuosPlanFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str(), + RTPLAN_FILENAME.c_str()).generateDoseAccessor()); + + + std::string testFileName = RTPLAN_FILENAME; + size_t gzPosition = testFileName.find(".pln"); + + if (gzPosition != std::string::npos) + { + testFileName.erase(gzPosition, testFileName.length()); + } + + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosPlanFileDoseAccessorGenerator(RTDOSE_FILENAME.c_str(), + testFileName).generateDoseAccessor(), core::InvalidParameterException); + + testFileName = RTDOSE_FILENAME; + gzPosition = testFileName.find(".dos"); + + if (gzPosition != std::string::npos) + { + testFileName.erase(gzPosition, testFileName.length()); + } + + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosPlanFileDoseAccessorGenerator(testFileName, + RTPLAN_FILENAME.c_str()).generateDoseAccessor(), core::InvalidParameterException); + + //3) test virtuos accessor generator with cubeinfo + Cubeinfo** pPointerOnVirtuosCube = new Cubeinfo*; + *pPointerOnVirtuosCube = create_cubeinfo(0); + CHECK_NO_THROW(io::virtuos::VirtuosCubeinfoDoseAccessorGenerator(*pPointerOnVirtuosCube)); + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosCubeinfoDoseAccessorGenerator( + *pPointerOnVirtuosCube).generateDoseAccessor(), core::InvalidDoseException); + + nc_init_cubeinfo(*pPointerOnVirtuosCube); + CHECK_THROW_EXPLICIT(io::virtuos::VirtuosCubeinfoDoseAccessorGenerator( + *pPointerOnVirtuosCube).generateDoseAccessor(), core::InvalidDoseException); + + opencube(RTDOSE2_FILENAME.c_str() , *pPointerOnVirtuosCube); + CHECK_NO_THROW(io::virtuos::VirtuosCubeinfoDoseAccessorGenerator( + *pPointerOnVirtuosCube).generateDoseAccessor()); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/virtuos/VirtuosDoseIOTest.cpp b/testing/io/virtuos/VirtuosDoseIOTest.cpp new file mode 100644 index 0000000..0e54907 --- /dev/null +++ b/testing/io/virtuos/VirtuosDoseIOTest.cpp @@ -0,0 +1,229 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbGeometricInfo.h" +#include "rttbDoseIteratorInterface.h" +#include "rttbVirtuosCubeinfoDoseAccessorGenerator.h" +#include "rttbVirtuosDoseFileDoseAccessorGenerator.h" +#include "rttbVirtuosPlanFileDoseAccessorGenerator.h" +#include "rttbVirtuosDoseAccessor.h" +#include "rttbNullPointerException.h" +#include "rttbInvalidDoseException.h" +#include "rttbInvalidParameterException.h" +#include "rttbIndexOutOfBoundsException.h" +#include "../rttbDoseAccessorTester.h" + + +namespace rttb +{ + + namespace testing + { + + /*! @brief VirtuosDoseIOTest - test the IO for virtuos data + 1) test getPrescribedDose() and getNormalizationDose() + 2) test virtuos dose import if geometric info was set correctly + 3) test virtuos dose import accessing dose data and converting + + WARNING: The values for comparison need to be adjusted if the input files are changed! + */ + + int VirtuosDoseIOTest(int argc, char* argv[]) + { + typedef core::DoseIteratorInterface::DoseAccessorPointer DoseAccessorPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + // 1: plan file name (virtuos) .../testing/data/Virtuos/prostate_ac/prostate_ac101.pln + // 2: dose1 file name (virtuos) .../testing/data/Virtuos/prostate_ac/prostate_ac101.dos.gz + // 3: dose2 file name (trip): .../testing/data/Virtuos/prostate_ac/prostate_ac101.dos + // WARNING: Test will fail if dose2 does not contain the same dose as dose1! + + std::string RTPLAN_FILENAME; + std::string RTDOSE_FILENAME; + std::string RTDOSE2_FILENAME; + + if (argc > 1) + { + RTPLAN_FILENAME = argv[1]; + } + + if (argc > 2) + { + RTDOSE_FILENAME = argv[2]; + } + + if (argc > 3) + { + RTDOSE2_FILENAME = argv[3]; + } + + //1) test getPrescribedDose() and getNormalizationDose() + + io::virtuos::VirtuosDoseFileDoseAccessorGenerator doseAccessorGeneratorTest3( + RTDOSE_FILENAME.c_str(), 885.0, 76.0); + boost::shared_ptr virtuosTest3 = + boost::static_pointer_cast + (doseAccessorGeneratorTest3.generateDoseAccessor()); + CHECK_EQUAL(76.0, virtuosTest3->getPrescribedDose()); + CHECK_EQUAL(885.0, virtuosTest3->getNormalizationDose()); + + io::virtuos::VirtuosDoseFileDoseAccessorGenerator doseAccessorGeneratorTest4( + RTDOSE_FILENAME.c_str(), 0, 0); + boost::shared_ptr virtuosTest4 = + boost::static_pointer_cast + (doseAccessorGeneratorTest4.generateDoseAccessor()); + CHECK_EQUAL(1, virtuosTest4->getPrescribedDose()); + CHECK_EQUAL(1, virtuosTest4->getNormalizationDose()); + + io::virtuos::VirtuosPlanFileDoseAccessorGenerator doseAccessorGenerator1(RTDOSE_FILENAME.c_str(), + RTPLAN_FILENAME.c_str()); + boost::shared_ptr doseAccessor1 = + boost::static_pointer_cast + (doseAccessorGenerator1.generateDoseAccessor()); + + CHECK_EQUAL(76.0, doseAccessor1->getPrescribedDose()); + CHECK_EQUAL(885.0, doseAccessor1->getNormalizationDose()); + + //2) test dose import if geometric info was set correctly + core::GeometricInfo geoInfo = doseAccessor1->getGeometricInfo(); + CHECK_EQUAL(256, geoInfo.getNumRows()); + CHECK_EQUAL(256, geoInfo.getNumColumns()); + CHECK_EQUAL(144, geoInfo.getNumSlices()); + CHECK_EQUAL(256 * 256 * 144, doseAccessor1->getGridSize()); + CHECK_EQUAL(OrientationMatrix(), geoInfo.getOrientationMatrix()); + + const VoxelGridID start = 0; + const VoxelGridIndex3D start3D(0); + + VoxelGridID end, inbetween; + VoxelGridIndex3D end3D, inbetween3D; + + //3) test dose import accessing dose data and converting + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(start)); + CHECK_EQUAL(0, doseAccessor1-> getDoseAt(start3D)); + + inbetween = int(doseAccessor1->getGridSize() / 2.0); + doseAccessor1->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(inbetween)); + CHECK_EQUAL(0, doseAccessor1-> getDoseAt(inbetween3D)); + + end = doseAccessor1->getGridSize() - 1; + doseAccessor1->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(0, doseAccessor1->getDoseAt(end)); + CHECK_EQUAL(0, doseAccessor1-> getDoseAt(end3D)); + + /* Dose without plan */ + io::virtuos::VirtuosDoseFileDoseAccessorGenerator doseAccessorGenerator2(RTDOSE2_FILENAME.c_str(), + doseAccessor1->getNormalizationDose(), + doseAccessor1->getPrescribedDose()); + boost::shared_ptr doseAccessor2 = + boost::static_pointer_cast + (doseAccessorGenerator2.generateDoseAccessor()); + + CHECK_EQUAL(doseAccessor1->getPrescribedDose(), doseAccessor2->getPrescribedDose()); + CHECK_EQUAL(doseAccessor1->getNormalizationDose(), doseAccessor2->getNormalizationDose()); + + + //2) test dose import if geometric info was set correctly + core::GeometricInfo geoInfo2 = doseAccessor2->getGeometricInfo(); + CHECK_EQUAL(geoInfo.getNumRows(), geoInfo2.getNumRows()); + CHECK_EQUAL(geoInfo.getNumColumns(), geoInfo2.getNumColumns()); + CHECK_EQUAL(geoInfo.getNumSlices(), geoInfo2.getNumSlices()); + CHECK_EQUAL(doseAccessor1->getGridSize(), doseAccessor2->getGridSize()); + CHECK_EQUAL(geoInfo.getOrientationMatrix(), geoInfo2.getOrientationMatrix()); + + //3) test dose import accessing dose data and converting + + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor2->getDoseAt(start)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(start3D), doseAccessor2-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(start), doseAccessor2-> getDoseAt(start3D)); + + inbetween = int(doseAccessor2->getGridSize() / 2.0); + doseAccessor2->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor2->getDoseAt(inbetween)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(inbetween3D), doseAccessor2-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(inbetween), doseAccessor2-> getDoseAt(inbetween3D)); + + end = doseAccessor2->getGridSize() - 1; + doseAccessor2->getGeometricInfo().convert(end, end3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(end), doseAccessor2->getDoseAt(end)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(end3D), doseAccessor2-> getDoseAt(end3D)); + CHECK_EQUAL(doseAccessor2->getDoseAt(end), doseAccessor2-> getDoseAt(end3D)); + + /* Import Trip dose */ + io::virtuos::VirtuosDoseFileDoseAccessorGenerator doseAccessorGenerator3(RTDOSE2_FILENAME.c_str(), + doseAccessor1->getNormalizationDose(), + doseAccessor1->getPrescribedDose()); + boost::shared_ptr doseAccessor3 = + boost::static_pointer_cast + (doseAccessorGenerator3.generateDoseAccessor()); + + + CHECK_EQUAL(doseAccessor1->getPrescribedDose(), doseAccessor3->getPrescribedDose()); + CHECK_EQUAL(doseAccessor1->getNormalizationDose(), doseAccessor3->getNormalizationDose()); + + + //2) test dose import if geometric info was set correctly + core::GeometricInfo geoInfo3 = doseAccessor3->getGeometricInfo(); + CHECK_EQUAL(geoInfo.getNumRows(), geoInfo3.getNumRows()); + CHECK_EQUAL(geoInfo.getNumColumns(), geoInfo3.getNumColumns()); + CHECK_EQUAL(geoInfo.getNumSlices(), geoInfo3.getNumSlices()); + CHECK_EQUAL(doseAccessor1->getGridSize(), doseAccessor3->getGridSize()); + CHECK_EQUAL(geoInfo.getOrientationMatrix(), geoInfo3.getOrientationMatrix()); + + //3) test dose import accessing dose data and converting + + CHECK_EQUAL(doseAccessor1->getDoseAt(start), doseAccessor3->getDoseAt(start)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(start3D), doseAccessor3-> getDoseAt(start3D)); + CHECK_EQUAL(doseAccessor3->getDoseAt(start), doseAccessor3-> getDoseAt(start3D)); + + inbetween = int(doseAccessor3->getGridSize() / 2.0); + doseAccessor3->getGeometricInfo().convert(inbetween, inbetween3D); + + CHECK_EQUAL(doseAccessor1->getDoseAt(inbetween), doseAccessor3->getDoseAt(inbetween)); + CHECK_EQUAL(doseAccessor1-> getDoseAt(inbetween3D), doseAccessor3-> getDoseAt(inbetween3D)); + CHECK_EQUAL(doseAccessor3->getDoseAt(inbetween), doseAccessor3-> getDoseAt(inbetween3D)); + + DoseAccessorTester doseCompare(doseAccessor1, doseAccessor3); + CHECK_TESTER(doseCompare); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/virtuos/VirtuosStructureIOTest.cpp b/testing/io/virtuos/VirtuosStructureIOTest.cpp new file mode 100644 index 0000000..6969c75 --- /dev/null +++ b/testing/io/virtuos/VirtuosStructureIOTest.cpp @@ -0,0 +1,202 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#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 VirtuosIOTest - test the IO for virtuos data + 1) test virtuos structure import + + WARNING: The values for comparison need to be adjusted if the input files are changed! + */ + + int VirtuosStructureIOTest(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" + // 3: structure file name ".../testing/data/Virtuos/prostate_ac/prostate_ac000.vdx" + // 4: ctx file name (virtuos) ".../testing/data/Virtuos/prostate_ac/prostate_ac000.ctx.gz" + // 5: dos file name (virtuos) ".../testing/data/ Virtuos/prostate_ac/prostate_ac101.dos.gz" + + /*! @todo provide a adequate number of test data. Does not work with other test data currently provided. Structures are not loaded! */ + + std::string RTSTRUCT_FILENAME; + std::string RTSTRUCT_FILENAME2; + std::string CT_FILENAME; + std::string CT_FILENAME2; + std::string DOSE_FILENAME; + + if (argc>1) + { + RTSTRUCT_FILENAME = argv[1]; + } + if (argc>2) + { + CT_FILENAME = argv[2]; + } + if (argc>3) + { + RTSTRUCT_FILENAME2 = argv[3]; + } + if (argc>4) + { + CT_FILENAME2 = argv[4]; + } + if (argc>5) + { + DOSE_FILENAME = argv[5]; + } + + /* structure set */ + + StructureSetPointer rtStructureSet=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME.c_str(),CT_FILENAME.c_str()).generateStructureSet(); + //2) test structure import + CHECK_EQUAL(1,rtStructureSet->getNumberOfStructures()); + + CHECK_EQUAL("TARGET_1",(rtStructureSet->getStructure(0))->getLabel()); + + core::Structure myStructure = *(rtStructureSet->getStructure(0)); + CHECK_EQUAL(320,myStructure.getNumberOfEndpoints()); + + PolygonSequenceType myPolygonSeq = myStructure.getStructureVector(); + CHECK_EQUAL(8,myPolygonSeq.size()); + + PolygonType myPolygon = myPolygonSeq.at(0); + + CHECK_CLOSE(125.975,myPolygon.at(0).x(),errorConstant); + CHECK_CLOSE(118.120,myPolygon.at(0).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(0).z(),errorConstant); + + CHECK_CLOSE(124.487,myPolygon.at(1).x(),errorConstant); + CHECK_CLOSE(118.477,myPolygon.at(1).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(1).z(),errorConstant); + + CHECK_CLOSE(117.870,myPolygon.at(10).x(),errorConstant); + CHECK_CLOSE(129.275,myPolygon.at(10).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(10).z(),errorConstant); + + CHECK_CLOSE(129.025,myPolygon.at(20).x(),errorConstant); + CHECK_CLOSE(137.380,myPolygon.at(20).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(20).z(),errorConstant); + + CHECK_CLOSE(127.5,myPolygon.at(myPolygon.size()-1).x(),errorConstant); + CHECK_CLOSE(118,myPolygon.at(myPolygon.size()-1).y(),errorConstant); + CHECK_CLOSE(121.000,myPolygon.at(myPolygon.size()-1).z(),errorConstant); + //The original Virtuos contours are closed (last point = 1st point), but the ones used here do not have this double point. + + + /* structure set2 */ + StructureSetPointer rtStructureSet2=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME2.c_str(),CT_FILENAME2.c_str()).generateStructureSet(); + //2) test structure import + CHECK_EQUAL(9,rtStructureSet2->getNumberOfStructures()); + + CHECK_EQUAL("BLASE",(rtStructureSet2->getStructure(0))->getLabel()); + CHECK_EQUAL("HUEFTK_LINKS",(rtStructureSet2->getStructure(3))->getLabel()); + CHECK_EQUAL("RAND",(rtStructureSet2->getStructure(5))->getLabel()); + CHECK_EQUAL("PTV",(rtStructureSet2->getStructure(8))->getLabel()); + + core::Structure myStructure2 = *(rtStructureSet2->getStructure(8)); + CHECK_EQUAL(1653,myStructure2.getNumberOfEndpoints()); + + PolygonSequenceType myPolygonSeq2 = myStructure2.getStructureVector(); + CHECK_EQUAL(27,myPolygonSeq2.size()); + + PolygonType myPolygon2 = myPolygonSeq2.at(0); + CHECK_CLOSE(244.511,myPolygon2.at(0).x(),errorConstant); + CHECK_CLOSE(215.215,myPolygon2.at(0).y(),errorConstant); + CHECK_CLOSE(149,myPolygon2.at(0).z(),errorConstant); + + CHECK_CLOSE(242.192,myPolygon2.at(1).x(),errorConstant); + CHECK_CLOSE(215.215,myPolygon2.at(1).y(),errorConstant); + CHECK_CLOSE(149,myPolygon2.at(1).z(),errorConstant); + + CHECK_CLOSE(221.318,myPolygon2.at(10).x(),errorConstant); + CHECK_CLOSE(225.347,myPolygon2.at(10).y(),errorConstant); + CHECK_CLOSE(149,myPolygon2.at(10).z(),errorConstant); + + CHECK_CLOSE(220.098,myPolygon2.at(20).x(),errorConstant); + CHECK_CLOSE(239.873,myPolygon2.at(20).y(),errorConstant); + CHECK_CLOSE(149,myPolygon2.at(20).z(),errorConstant); + + CHECK_CLOSE(245.854,myPolygon2.at(myPolygon2.size()-1).x(),errorConstant); + CHECK_CLOSE(215.581,myPolygon2.at(myPolygon2.size()-1).y(),errorConstant); + CHECK_CLOSE(149,myPolygon2.at(myPolygon2.size()-1).z(),errorConstant); + + /* structure set2 on dose */ + StructureSetPointer rtStructureSet3=io::virtuos::VirtuosFileStructureSetGenerator(RTSTRUCT_FILENAME2.c_str(),DOSE_FILENAME.c_str()).generateStructureSet(); + //2) test structure import, should be equal to structure imported on CT-image + CHECK_EQUAL(9,rtStructureSet3->getNumberOfStructures()); + + CHECK_EQUAL("BLASE",(rtStructureSet3->getStructure(0))->getLabel()); + CHECK_EQUAL("HUEFTK_LINKS",(rtStructureSet3->getStructure(3))->getLabel()); + CHECK_EQUAL("RAND",(rtStructureSet3->getStructure(5))->getLabel()); + CHECK_EQUAL("PTV",(rtStructureSet3->getStructure(8))->getLabel()); + + for(int s = 0; s < rtStructureSet3->getNumberOfStructures(); s++){ + CHECK_EQUAL((rtStructureSet2->getStructure(s))->getLabel(),(rtStructureSet3->getStructure(s))->getLabel()); + + core::Structure myStructure3 = *(rtStructureSet3->getStructure(s)); + core::Structure myStructure2c = *(rtStructureSet2->getStructure(s)); + CHECK_EQUAL(myStructure2c.getNumberOfEndpoints(),myStructure3.getNumberOfEndpoints()); + PolygonSequenceType myPolygonSeq3 = myStructure3.getStructureVector(); + PolygonSequenceType myPolygonSeq2c = myStructure2c.getStructureVector(); + CHECK_EQUAL(myPolygonSeq2c.size(),myPolygonSeq3.size()); + + for(int ps = 0; ps < myPolygonSeq3.size(); ps++){ + PolygonType myPolygon2c = myPolygonSeq2c.at(ps); + PolygonType myPolygon3 = myPolygonSeq3.at(ps); + CHECK_CLOSE(myPolygon2c.at(0).x(),myPolygon3.at(0).x(),errorConstant); + CHECK_CLOSE(myPolygon2c.at(0).y(),myPolygon3.at(0).y(),errorConstant); + CHECK_CLOSE(myPolygon2c.at(0).z(),myPolygon3.at(0).z(),errorConstant); + } + } + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp b/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp new file mode 100644 index 0000000..13f60c8 --- /dev/null +++ b/testing/io/virtuos/VirtuosStructureSetGeneratorTest.cpp @@ -0,0 +1,104 @@ +// ----------------------------------------------------------------------- +// 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/trunk/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 +#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"); + 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/io/virtuos/files.cmake b/testing/io/virtuos/files.cmake new file mode 100644 index 0000000..0c57d7e --- /dev/null +++ b/testing/io/virtuos/files.cmake @@ -0,0 +1,14 @@ +SET(CPP_FILES rttbVirtuosIOTests.cpp + TripDoseIOTest.cpp + TripStructureIOTest.cpp + VirtuosDoseIOTest.cpp + VirtuosDoseAccessorGeneratorTest.cpp + VirtuosDVHCalculatorExampleTest.cpp + VirtuosStructureIOTest.cpp + VirtuosStructureSetGeneratorTest.cpp + ../rttbDoseAccessorTester.cpp + ) + +SET(H_FILES + ../rttbDoseAccessorTester.h +) diff --git a/testing/io/virtuos/rttbVirtuosIOTests.cpp b/testing/io/virtuos/rttbVirtuosIOTests.cpp new file mode 100644 index 0000000..90b1390 --- /dev/null +++ b/testing/io/virtuos/rttbVirtuosIOTests.cpp @@ -0,0 +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 rttbCoreTests 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(VirtuosDoseAccessorGeneratorTest); + LIT_REGISTER_TEST(VirtuosDoseIOTest); + LIT_REGISTER_TEST(VirtuosStructureIOTest); + LIT_REGISTER_TEST(VirtuosStructureSetGeneratorTest); + LIT_REGISTER_TEST(TripStructureIOTest); + LIT_REGISTER_TEST(TripDoseIOTest); + LIT_REGISTER_TEST(VirtuosDVHCalculatorExampleTest); + } + } + } + +int main(int argc, char* argv[]) + { + int result = 0; + + rttb::testing::registerTests(); + + try + { + result = lit::multiTestsMain(argc,argv); + } + + catch(...) + { + result = -1; + } + + return result; + } diff --git a/testing/masks/CMakeLists.txt b/testing/masks/CMakeLists.txt new file mode 100644 index 0000000..8c6f926 --- /dev/null +++ b/testing/masks/CMakeLists.txt @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(MASKS_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbMasksTests) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- + +ADD_TEST(OTBMaskAccessorTest ${MASKS_TESTS} OTBMaskAccessorTest +) + +RTTB_CREATE_TEST_MODULE(rttbMasks DEPENDS RTTBMasks PACKAGE_DEPENDS Boost Litmus DCMTK) + diff --git a/testing/masks/OTBMaskAccessorTest.cpp b/testing/masks/OTBMaskAccessorTest.cpp new file mode 100644 index 0000000..cc1e5c8 --- /dev/null +++ b/testing/masks/OTBMaskAccessorTest.cpp @@ -0,0 +1,197 @@ +// ----------------------------------------------------------------------- +// 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 "rttbOTBMaskAccessor.h" +#include "rttbMaskVoxel.h" +#include "rttbNullPointerException.h" +#include "rttbException.h" +#include "../core/DummyStructure.h" +#include "../core/DummyDoseAccessor.h" +#include "rttbMaskVoxelListTester.h" +#include "rttbMaskRectStructTester.h" + + +namespace rttb +{ + namespace testing + { + + /*! @brief GenericMaskedDoseIteratorTest. + 1) test constructors + 2) test updateMask (do relevant voxels match?) + 3) test valid/convert + 4) test getMaskAt + 5) other interface functions (simple getters) + */ + int OTBMaskAccessorTest(int argc, char* argv[]) + { + PREPARE_DEFAULT_TEST_REPORTING; + + typedef core::Structure::StructTypePointer StructTypePointer; + typedef masks::OTBMaskAccessor::MaskVoxelListPointer MaskVoxelListPointer; + typedef masks::OTBMaskAccessor::MaskVoxelList MaskVoxelList; + + // 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); + + + //1) test constructors + CHECK_NO_THROW(masks::OTBMaskAccessor(spMyStruct, spTestDoseAccessor->getGeometricInfo())); + masks::OTBMaskAccessor testOTB1(spMyStruct, spTestDoseAccessor->getGeometricInfo()); + CHECK_EQUAL(spTestDoseAccessor->getGeometricInfo(), testOTB1.getGeometricInfo()); + + CHECK_NO_THROW(masks::OTBMaskAccessor(spMyStruct, spTestDoseAccessor->getGeometricInfo())); + masks::OTBMaskAccessor testOTB2(spMyStruct, spTestDoseAccessor->getGeometricInfo()); + CHECK_EQUAL(spTestDoseAccessor->getGeometricInfo(), testOTB2.getGeometricInfo()); + + //2) test updateMask (do relevant voxels match?) + CHECK_NO_THROW(testOTB1.updateMask()); + CHECK_NO_THROW(testOTB1.getRelevantVoxelVector()); + MaskVoxelListPointer relVoxelOTB1 = testOTB1.getRelevantVoxelVector(); + + boost::shared_ptr spMaskAccessor = + boost::make_shared(spMyStruct, spTestDoseAccessor->getGeometricInfo()); + MaskRectStructTester voxelizationTester(spMaskAccessor, zPlane); + CHECK_TESTER(voxelizationTester); + + MaskVoxelListPointer relVoxelOTB2 = testOTB2.getRelevantVoxelVector(); + + MaskVoxelListTester listComp(relVoxelOTB1, relVoxelOTB2); + CHECK_TESTER(listComp); + + //3) test valid/convert + const VoxelGridIndex3D gridIndexIn1(0, 0, 0); + const VoxelGridIndex3D gridIndexIn2(4, 3, 4); + const VoxelGridIndex3D gridIndexIn3(testOTB2.getGeometricInfo().getNumColumns() - 1, + testOTB2.getGeometricInfo().getNumRows() - 1, + testOTB2.getGeometricInfo().getNumSlices() - 1); + const VoxelGridIndex3D gridIndexOut1(testOTB2.getGeometricInfo().getNumColumns(), + testOTB2.getGeometricInfo().getNumRows(), + testOTB2.getGeometricInfo().getNumSlices()); + const VoxelGridIndex3D gridIndexOut2(testOTB2.getGeometricInfo().getNumRows() * 2, + testOTB2.getGeometricInfo().getNumRows() + 5, + testOTB2.getGeometricInfo().getNumSlices() - 2); + + CHECK(testOTB2.getGeometricInfo().validIndex(gridIndexIn1)); + CHECK(testOTB2.getGeometricInfo().validIndex(gridIndexIn2)); + std::cout << gridIndexIn3 << std::endl; + CHECK(testOTB2.getGeometricInfo().validIndex(gridIndexIn3)); + CHECK(!testOTB2.getGeometricInfo().validIndex(gridIndexOut1)); + CHECK(!testOTB2.getGeometricInfo().validIndex(gridIndexOut2)); + + const VoxelGridID gridIDIn1(0); + int in2 = gridIndexIn2.z() * testOTB2.getGeometricInfo().getNumColumns() * + testOTB2.getGeometricInfo().getNumRows() + + gridIndexIn2.y() * testOTB2.getGeometricInfo().getNumColumns() + gridIndexIn2.x(); + const VoxelGridID gridIDIn2(in2); + const VoxelGridID gridIDIn3(testOTB2.getGeometricInfo().getNumberOfVoxels() - 1); //size-1 + const VoxelGridID gridIDOut1(testOTB2.getGeometricInfo().getNumberOfVoxels()); //size + const VoxelGridID gridIDOut2(testOTB2.getGeometricInfo().getNumberOfVoxels() + 10); + + CHECK(testOTB2.getGeometricInfo().validID(gridIDIn1)); + CHECK(testOTB2.getGeometricInfo().validID(gridIDIn2)); + CHECK(testOTB2.getGeometricInfo().validID(gridIDIn3)); + CHECK(!testOTB2.getGeometricInfo().validID(gridIDOut1)); + CHECK(!testOTB2.getGeometricInfo().validID(gridIDOut2)); + + VoxelGridIndex3D test; + CHECK(testOTB2.getGeometricInfo().convert(gridIDIn1, test)); + CHECK_EQUAL(gridIndexIn1, test); + CHECK(testOTB2.getGeometricInfo().convert(gridIDIn2, test)); + CHECK_EQUAL(gridIndexIn2, test); + CHECK(!testOTB2.getGeometricInfo().convert(gridIDOut1, test)); + //gridIndexOut1 is invalid, test is therefore not asigned, therefore testing is not necessary + //CHECK_EQUAL(gridIndexOut1,test); + VoxelGridID testId; + CHECK(testOTB2.getGeometricInfo().convert(gridIndexIn1, testId)); + CHECK_EQUAL(gridIDIn1, testId); + CHECK(testOTB2.getGeometricInfo().convert(gridIndexIn2, testId)); + CHECK_EQUAL(gridIDIn2, testId); + CHECK(!testOTB2.getGeometricInfo().convert(gridIndexOut1, testId)); + //gridIDOut1 is invalid, testId is therefore not asigned, therefore testing is not necessary + //CHECK_EQUAL(exp,testId); + + //4) test getMaskAt + const VoxelGridIndex3D inMask1(2, 1, 4); //corner -> volumeFraction = 0.25 + const VoxelGridIndex3D inMask2(3, 4, 4); //inside ->volumeFraction = 1 + const VoxelGridIndex3D inMask3(4, 5, 4); //side -> volumeFraction = 0.5 + const VoxelGridIndex3D outMask1(7, 5, 4); + const VoxelGridIndex3D outMask2(2, 1, 2); + + core::MaskVoxel tmpMV1(0), tmpMV2(0); + CHECK(testOTB2.getMaskAt(inMask1, tmpMV1)); + CHECK(testOTB2.getGeometricInfo().convert(inMask1, testId)); + CHECK(testOTB2.getMaskAt(testId, tmpMV2)); + CHECK_EQUAL(tmpMV1, tmpMV2); + CHECK_EQUAL(0.25, tmpMV1.getRelevantVolumeFraction()); + CHECK_EQUAL(testId, tmpMV1.getVoxelGridID()); + + CHECK(testOTB2.getMaskAt(inMask2, tmpMV1)); + CHECK(testOTB2.getGeometricInfo().convert(inMask2, testId)); + CHECK(testOTB2.getMaskAt(testId, tmpMV2)); + CHECK_EQUAL(tmpMV1, tmpMV2); + CHECK_EQUAL(1, tmpMV1.getRelevantVolumeFraction()); + CHECK_EQUAL(testId, tmpMV1.getVoxelGridID()); + + CHECK(testOTB2.getMaskAt(inMask3, tmpMV1)); + CHECK(testOTB2.getGeometricInfo().convert(inMask3, testId)); + CHECK(testOTB2.getMaskAt(testId, tmpMV2)); + CHECK_EQUAL(tmpMV1, tmpMV2); + CHECK_EQUAL(0.5, tmpMV1.getRelevantVolumeFraction()); + CHECK_EQUAL(testId, tmpMV1.getVoxelGridID()); + + CHECK(!testOTB2.getMaskAt(outMask1, tmpMV1)); + CHECK(testOTB2.getGeometricInfo().convert(outMask1, testId)); + CHECK(!testOTB2.getMaskAt(testId, tmpMV2)); + CHECK_EQUAL(tmpMV1, tmpMV2); + CHECK_EQUAL(0, tmpMV1.getRelevantVolumeFraction()); + //CHECK_EQUAL(testId,tmpMV1.getVoxelGridID()); -> return value will not be valid outside the mask + + CHECK(!testOTB2.getMaskAt(outMask2, tmpMV1)); + CHECK(testOTB2.getGeometricInfo().convert(outMask2, testId)); + CHECK(!testOTB2.getMaskAt(testId, tmpMV2)); + CHECK_EQUAL(tmpMV1, tmpMV2); + CHECK_EQUAL(0, tmpMV1.getRelevantVolumeFraction()); + //CHECK_EQUAL(testId,tmpMV1.getVoxelGridID()); -> return value will not be valid outside the mask + + //5) other interface functions + CHECK_EQUAL(true, testOTB1.isGridHomogeneous()); + CHECK_NO_THROW(testOTB1.getMaskUID()); + CHECK(testOTB1.getMaskUID() != testOTB2.getMaskUID()); + + RETURN_AND_REPORT_TEST_SUCCESS; + } + }//testing +}//rttb + diff --git a/testing/masks/files.cmake b/testing/masks/files.cmake new file mode 100644 index 0000000..9d49d07 --- /dev/null +++ b/testing/masks/files.cmake @@ -0,0 +1,17 @@ +SET(CPP_FILES + ../core/DummyStructure.cpp + ../core/CreateTestStructure.cpp + ../core/DummyDoseAccessor.cpp + OTBMaskAccessorTest.cpp + rttbMaskVoxelListTester.cpp + rttbMaskRectStructTester.cpp + rttbMasksTests.cpp + ) + +SET(H_FILES + ../core/DummyStructure.h + ../core/CreateTestStructure.h + ../core/DummyDoseAccessor.h + rttbMaskVoxelListTester.h + rttbMaskRectStructTester.h +) diff --git a/testing/masks/rttbMaskRectStructTester.cpp b/testing/masks/rttbMaskRectStructTester.cpp new file mode 100644 index 0000000..6478e2b --- /dev/null +++ b/testing/masks/rttbMaskRectStructTester.cpp @@ -0,0 +1,184 @@ +// ----------------------------------------------------------------------- +// 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 "rttbMaskRectStructTester.h" + +namespace rttb +{ + + namespace testing + { + + MaskRectStructTester::MaskRectStructTester(MaskAccessorPointer aMaskAccessor, GridIndexType z) + { + _maskAccessor = aMaskAccessor; + _referenceList = _maskAccessor->getRelevantVoxelVector(); + + _differentSlice = false; + _outsideStructure = false; + _wrongRVF = false; + _conversionFailed = false; + + _zIndex = z; + } + + void MaskRectStructTester::setAccessor(const MaskAccessorPointer aMaskAccessor) + { + _maskAccessor = aMaskAccessor; + _referenceList = _maskAccessor->getRelevantVoxelVector(); + } + void MaskRectStructTester::setZ(const GridIndexType z) + { + _zIndex = z; + } + + lit::StringType MaskRectStructTester::getTestDescription(void) const + { + return "Check if generated MaskVoxelList corresponds to the given rectangular structure."; + }; + + bool MaskRectStructTester::doCheck(void) const + { + _pResults->onTestStart(getCurrentTestLabel()); + + _differentSlice = false; + _outsideStructure = false; + _wrongRVF = false; + _conversionFailed = false; + + VoxelGridIndex3D indexOTB; + MaskVoxelList::iterator iterR; + VoxelGridID index = 0; + + for (iterR = _referenceList->begin(); iterR != _referenceList->end(); ++iterR, ++index) + { + if (!(_maskAccessor->getGeometricInfo().convert(iterR->getVoxelGridID(), indexOTB))) + { + _conversionFailed = true; + _failedListIndex = index; + break; + } + + if (indexOTB.z() != _zIndex) + { + _differentSlice = true; + _failedListIndex = index; + _wrongVal = indexOTB.z(); + break; + } + //corners are [2,1],[5,1],[5,5],[2,5], relevant voxels need to lie inside + else if ((indexOTB.x() < 2) || (indexOTB.x() > 5)) + { + _outsideStructure = true; + _failedListIndex = index; + break; + } + else if ((indexOTB.y() < 1) || (indexOTB.y() > 5)) + { + _outsideStructure = true; + _failedListIndex = index; + break; + } + //check corners + else if ((indexOTB.x() == 2 && indexOTB.y() == 1) || (indexOTB.x() == 2 && indexOTB.y() == 5) || + (indexOTB.x() == 5 && indexOTB.y() == 1) || (indexOTB.x() == 5 && indexOTB.y() == 5)) + { + if (0.25 != iterR->getRelevantVolumeFraction()) + { + _wrongRVF = true; + _failedListIndex = index; + _wrongVal = iterR->getRelevantVolumeFraction(); + break; + } + } + else if ((indexOTB.x() == 2) || (indexOTB.y() == 5) || (indexOTB.y() == 1) || (indexOTB.x() == 5)) + { + if (0.5 != iterR->getRelevantVolumeFraction()) + { + _wrongRVF = true; + _failedListIndex = index; + _wrongVal = iterR->getRelevantVolumeFraction(); + break; + } + } + //check sides + else //should be inside + { + if (1 != iterR->getRelevantVolumeFraction()) + { + _wrongRVF = true; + _failedListIndex = index; + _wrongVal = iterR->getRelevantVolumeFraction(); + break; + } + } + }//end for(iterR = _referenceList->begin(); iterR != _referenceLi... + + if (_conversionFailed || _differentSlice || _outsideStructure || _wrongRVF) + { + return false; + } + + return true; + } + + void MaskRectStructTester::handleSuccess(void) const + { + std::ostringstream stream; + stream << "The Voxelization is inside the given rectangle" << std::endl; + + _pResults->onTestSuccess(getCurrentTestLabel(), stream.str()); + } + + void MaskRectStructTester::handleFailure(void) const + { + std::ostringstream stream; + stream << "Voxelization was not correct" << std::endl; + + if (_conversionFailed) + { + stream << std::endl << "The conversion failed at position" << _failedListIndex << std::endl; + } + + if (_differentSlice) + { + stream << std::endl << "The voxelisation was not in plane " << _zIndex << " at position " << + _failedListIndex << " was in " << _wrongVal << std::endl; + } + + if (_outsideStructure) + { + stream << std::endl << "The voxelisation was outside the rectangle at position " << _failedListIndex + << std::endl; + } + + if (_wrongRVF) + { + stream << std::endl << "The voxelisation had the wrong relevant volume fraction at position " << + _failedListIndex << " was in " << _wrongVal << std::endl; + } + + _pResults->onTestFailure(getCurrentTestLabel(), stream.str()); + } + + } +} \ No newline at end of file diff --git a/testing/masks/rttbMaskRectStructTester.h b/testing/masks/rttbMaskRectStructTester.h new file mode 100644 index 0000000..9fe5115 --- /dev/null +++ b/testing/masks/rttbMaskRectStructTester.h @@ -0,0 +1,98 @@ +// ----------------------------------------------------------------------- +// 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/trunk/testing/core/DVHCalculatorTest.cpp $ +*/ + +#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; + + 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 new file mode 100644 index 0000000..f6a6978 --- /dev/null +++ b/testing/masks/rttbMaskVoxelListTester.cpp @@ -0,0 +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) +*/ +#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(); + if (diff > _maxDifference) { + _maxDifference = diff; + } + _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"<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 new file mode 100644 index 0000000..ab8c609 --- /dev/null +++ b/testing/masks/rttbMaskVoxelListTester.h @@ -0,0 +1,91 @@ +// ----------------------------------------------------------------------- +// 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/trunk/testing/core/DVHCalculatorTest.cpp $ +*/ + +#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 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/masks/rttbMasksTests.cpp b/testing/masks/rttbMasksTests.cpp new file mode 100644 index 0000000..aeb91ef --- /dev/null +++ b/testing/masks/rttbMasksTests.cpp @@ -0,0 +1,61 @@ +// ----------------------------------------------------------------------- +// 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 +#if defined(_MSC_VER) +#pragma warning ( disable : 4786 ) +#endif + + +#include "litMultiTestsMain.h" + +namespace rttb{ + namespace testing{ + + void registerTests() + { + LIT_REGISTER_TEST(OTBMaskAccessorTest); + } + } + } + +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/models/BioModelScatterPlotTest.cpp b/testing/models/BioModelScatterPlotTest.cpp new file mode 100644 index 0000000..70efa07 --- /dev/null +++ b/testing/models/BioModelScatterPlotTest.cpp @@ -0,0 +1,216 @@ +// ----------------------------------------------------------------------- +// 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 "rttbBaseTypeModels.h" +#include "rttbBioModel.h" +#include "rttbBioModelScatterPlots.h" +#include "../core/DummyDVHGenerator.h" +#include "DummyModel.h" +#include "rttbIntegration.h" + +#include +#include +#include + +namespace rttb{ + namespace testing{ + + typedef models::ScatterPlotType ScatterPlotType; + + /*! @brief BioModelScatterPlotTest. Test the scatterplot methods. + 1) test if parameters are set correctly + 2) test if scatterData contain each model value exactly once + */ + int BioModelScatterPlotTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + //generate artificial DVH and corresponding statistical values + DoseTypeGy binSize = DoseTypeGy(0.1); + DoseVoxelVolumeType voxelVolume = 8; + + DoseCalcType value = 1000; + DummyDVHGenerator dummyDVH; + + const IDType structureID = "myStructure"; + const IDType doseID = "myDose"; + + core::DVH::DVHPointer dvhPtr = boost::make_shared(dummyDVH.generateDVH(structureID, doseID, 0,2000)); + + //test Dummy Model + models::BioModelParamType param1 = 0.35; + models::BioModelParamType param2 = 0.023333333333333; + models::BioModelParamType param3 = 10000000; + + //model values will be dose/normalisationDose + DoseTypeGy normalisationDose = 10; + + rttb::models::DummyModel model(dvhPtr); + model.resetCounters(); + + //default number of points is 100 + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(model, 0, param1, 0.5, normalisationDose)); + model.resetCounters(); + ScatterPlotType scatter = models::getScatterPlotVary1Parameter(model, 0, param1, 0.5, normalisationDose); + //only 1st parameter should gave been changed + CHECK_EQUAL(100,model.getSetParam1Count()); + CHECK_EQUAL(0,model.getSetParam2Count()); + CHECK_EQUAL(0,model.getSetParam3Count()); + CHECK_EQUAL(100,model.getCalcCount()); + + // all model values should match the corresponding dose/normalizationDose + ScatterPlotType::iterator iter; + for (iter = scatter.begin(); iter != scatter.end(); ++iter){ + double currentKey = iter->first; + if ((currentKey/normalisationDose) != iter->second.first){ + CHECK_EQUAL((currentKey/normalisationDose), iter->second.first); + } + } + + model.resetCounters(); + + //number of points is 50 + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(model, 1, param2, 0.25, normalisationDose,50)); + model.resetCounters(); + scatter = models::getScatterPlotVary1Parameter(model, 1, param2, 0.25, normalisationDose,50); + //only 1st parameter should gave been changed + CHECK_EQUAL(0,model.getSetParam1Count()); + CHECK_EQUAL(50,model.getSetParam2Count()); + CHECK_EQUAL(0,model.getSetParam3Count()); + CHECK_EQUAL(50,model.getCalcCount()); + + // all model values should match the corresponding dose/normalizationDose + for (iter = scatter.begin(); iter != scatter.end(); ++iter){ + double currentKey = iter->first; + if ((currentKey/normalisationDose) != iter->second.first){ + CHECK_EQUAL((currentKey/normalisationDose), iter->second.first); + } + } + + model.resetCounters(); + + //number of points is 150 + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(model, 2, param3, 0.75, normalisationDose,150)); + model.resetCounters(); + scatter = models::getScatterPlotVary1Parameter(model, 2, param3, 0.75, normalisationDose,150); + //only 1st parameter should gave been changed + CHECK_EQUAL(0,model.getSetParam1Count()); + CHECK_EQUAL(0,model.getSetParam2Count()); + CHECK_EQUAL(150,model.getSetParam3Count()); + CHECK_EQUAL(150,model.getCalcCount()); + + // all model values should match the corresponding dose/normalizationDose + for (iter = scatter.begin(); iter != scatter.end(); ++iter){ + double currentKey = iter->first; + if ((currentKey/normalisationDose) != iter->second.first){ + CHECK_EQUAL((currentKey/normalisationDose), iter->second.first); + } + } + + model.resetCounters(); + std::vector paramIDVec; + models::BioModel::ParamVectorType meanVec; + models::BioModel::ParamVectorType varianceVec; + + paramIDVec.push_back(0); meanVec.push_back(1); varianceVec.push_back(0.6); + paramIDVec.push_back(1); meanVec.push_back(10); varianceVec.push_back(0.5); + paramIDVec.push_back(2); meanVec.push_back(100); varianceVec.push_back(0.4); + + //number of points is 50 + CHECK_NO_THROW(models::getScatterPlotVaryParameters(model, paramIDVec, meanVec, varianceVec, normalisationDose,50)); + model.resetCounters(); + scatter = models::getScatterPlotVaryParameters(model, paramIDVec, meanVec, varianceVec, normalisationDose,50); + //only 1st parameter should gave been changed + CHECK_EQUAL(50,model.getSetParam1Count()); + CHECK_EQUAL(50,model.getSetParam2Count()); + CHECK_EQUAL(50,model.getSetParam3Count()); + CHECK_EQUAL(50,model.getCalcCount()); + + // all model values should match the corresponding dose/normalizationDose + for (iter = scatter.begin(); iter != scatter.end(); ++iter){ + double currentKey = iter->first; + if ((currentKey/normalisationDose) != iter->second.first){ + CHECK_EQUAL((currentKey/normalisationDose), iter->second.first); + } + } + + model.resetCounters(); + + //test 2000 points + CHECK_NO_THROW(models::getScatterPlotVaryParameters(model, paramIDVec, meanVec, varianceVec, normalisationDose,10000)); + + //test iterativeIntegration + /*std::string bedFileName="d:/Temp/bed.txt"; + std::ifstream dvh_ifstr(bedFileName,std::ios::in); + std::vector volumeV2; + std::vector bedV2; + + //std::cout << "iterative integration"<< std::endl; + if ( !dvh_ifstr.is_open() ) + { + std::cerr<< "DVH file name invalid: could not open the dvh file!"<> volume; + volumeV2.push_back(volume); + //std::cout << "("<< volume << ","; + + std::string lineSub2 = line.substr(found2+1,found3-found2-1); + //std::cout << lineSub2 << std::endl; + std::stringstream ss2(lineSub2); + double bed; + ss2 >> bed; + bedV2.push_back(bed); + //std::cout << bed << ");"; + + } + + } + + } + + struct rttb::models::TcpParams params={0.302101,0.08,10000000,volumeV2,bedV2}; + + double result = rttb::models::integrateTCP(0, params);*/ + + RETURN_AND_REPORT_TEST_SUCCESS; + + } + + }//testing +}//rttb \ No newline at end of file diff --git a/testing/models/BioModelTest.cpp b/testing/models/BioModelTest.cpp new file mode 100644 index 0000000..94993f8 --- /dev/null +++ b/testing/models/BioModelTest.cpp @@ -0,0 +1,288 @@ +// ----------------------------------------------------------------------- +// 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 "rttbBaseTypeModels.h" +#include "rttbBioModel.h" +#include "rttbDVH.h" +#include "rttbTCPLQModel.h" +#include "rttbNTCPLKBModel.h" +#include "rttbNTCPRSModel.h" +#include "rttbBaseTypeModels.h" +#include "rttbBioModelCurve.h" +#include "rttbInvalidParameterException.h" +#include "rttbBioModelScatterPlots.h" + +namespace rttb{ + namespace testing{ + + typedef core::DVH::DataDifferentialType DataDifferentialType; + + /*! @brief RTBioModelTest. TCP calculated using a DVH PTV and LQ Model. NTCP tested using 3 Normal Tissue DVHs and LKB/RS Model. + TCPLQ: + 1) test constructors (values as expected?) + 2) test init (calcTCPxxx) + 3) test set/get + + NTCP (LKB): + 1) test constructors (values as expected?) + 2) test init (calcxxx) + 3) test set/get + + NTCP (RS): + 1) test constructors (values as expected?) + 2) test init (calcxxx) + 3) test set/get + */ + int BioModelTest(int argc, char* argv[] ) + { + PREPARE_DEFAULT_TEST_REPORTING; + + //generate artificial DVH and corresponding statistical values + DoseTypeGy binSize = DoseTypeGy(0.1); + DoseVoxelVolumeType voxelVolume = 8; + + DataDifferentialType aDataDifferential; + + DoseCalcType value = 0; + DVHVoxelNumber numberOfVoxels = 0; + + // creat default values + for(int i = 0; i < 98; i++){ + value = 0; + numberOfVoxels+=value; + aDataDifferential.push_back( value ); + } + aDataDifferential.push_back( 10 ); + aDataDifferential.push_back( 20 ); + + const IDType structureID = "myStructure"; + const IDType doseID = "myDose"; + const IDType voxelizationID = "myVoxelization"; + + core::DVH::DVHPointer dvhPtr = boost::make_shared(aDataDifferential, binSize, voxelVolume, structureID, doseID, voxelizationID); + + //test TCP LQ Model + models::BioModelParamType alpha = 0.35; + models::BioModelParamType beta = 0.023333333333333; + models::BioModelParamType roh = 10000000; + int numFractions = 1; + + DoseTypeGy normalizationDose = 50; + + //1) test constructors (values as expected?) + rttb::models::TCPLQModel tcplq=rttb::models::TCPLQModel(); + CHECK_EQUAL(0,tcplq.getAlphaMean()); + CHECK_EQUAL(0,tcplq.getAlpahBeta()); + CHECK_EQUAL(0,tcplq.getRho()); + CHECK_EQUAL(0,tcplq.getValue()); + + tcplq=rttb::models::TCPLQModel(dvhPtr,roh, numFractions,alpha/beta, alpha,0.08); + CHECK_EQUAL(alpha,tcplq.getAlphaMean()); + CHECK_EQUAL(alpha/beta,tcplq.getAlpahBeta()); + CHECK_EQUAL(roh,tcplq.getRho()); + CHECK_EQUAL(0,tcplq.getValue()); + + tcplq=rttb::models::TCPLQModel(); + CHECK_EQUAL(0,tcplq.getAlphaMean()); + CHECK_EQUAL(0,tcplq.getAlpahBeta()); + CHECK_EQUAL(0,tcplq.getRho()); + CHECK_EQUAL(0,tcplq.getValue()); + + tcplq=rttb::models::TCPLQModel(dvhPtr,alpha, beta, roh, numFractions); + CHECK_EQUAL(alpha,tcplq.getAlphaMean()); + CHECK_EQUAL(alpha/beta,tcplq.getAlpahBeta()); + CHECK_EQUAL(roh,tcplq.getRho()); + CHECK_EQUAL(0,tcplq.getValue()); + + //2) test init (calcTCPxxx) + CHECK_NO_THROW(tcplq.init(1)); + + //3) test set/get + CHECK_EQUAL(0,tcplq.getValue()); + CHECK_NO_THROW(tcplq.setParameters(alpha,10,roh,0.08)); + CHECK_EQUAL(10,tcplq.getAlpahBeta()); + CHECK_EQUAL(0.08,tcplq.getAlphaVariance()); + CHECK_EQUAL(alpha,tcplq.getAlphaMean()); + CHECK_EQUAL(roh,tcplq.getRho()); + + CHECK_NO_THROW(models::getCurveDoseVSBioModel(tcplq,normalizationDose)); + + std::vector aParameterVector; + aParameterVector.push_back(alpha+0.02); + CHECK_THROW_EXPLICIT(tcplq.setParameterVector(aParameterVector),core::InvalidParameterException); + aParameterVector.push_back(0.06); + aParameterVector.push_back(8); + aParameterVector.push_back(roh/10); + CHECK_NO_THROW(tcplq.setParameterVector(aParameterVector)); + CHECK_EQUAL(8,tcplq.getAlpahBeta()); + CHECK_EQUAL(0.06,tcplq.getAlphaVariance()); + CHECK_EQUAL(alpha+0.02,tcplq.getAlphaMean()); + CHECK_EQUAL(roh/10,tcplq.getRho()); + + for (int i = 0; i < 4; i++){ + CHECK_NO_THROW(tcplq.setParameterByID(i,models::BioModelParamType(i))); + } + + CHECK_THROW_EXPLICIT(tcplq.setParameterByID(4,4.0),core::InvalidParameterException); + + CHECK_EQUAL(0,tcplq.getParameterID("alphaMean")); + CHECK_EQUAL(0,tcplq.getAlphaMean()); + CHECK_EQUAL(1,tcplq.getParameterID("alphaVariance")); + CHECK_EQUAL(1,tcplq.getAlphaVariance()); + CHECK_EQUAL(2,tcplq.getParameterID("alpha_beta")); + CHECK_EQUAL(2,tcplq.getAlpahBeta()); + CHECK_EQUAL(3,tcplq.getParameterID("rho")); + CHECK_EQUAL(3,tcplq.getRho()); + + + //test NTCPLKBModel + //1) test constructors (values as expected?) + models::BioModelParamType aVal = 10; + models::BioModelParamType mVal = 0.16; + models::BioModelParamType d50Val = 35; + CHECK_NO_THROW(rttb::models::NTCPLKBModel()); + rttb::models::NTCPLKBModel lkb=rttb::models::NTCPLKBModel(); + CHECK_EQUAL(0,lkb.getA()); + CHECK_EQUAL(0,lkb.getM()); + CHECK_EQUAL(0,lkb.getD50()); + CHECK_EQUAL(0,lkb.getValue()); + CHECK_NO_THROW(rttb::models::NTCPLKBModel(dvhPtr, d50Val, mVal, aVal)); + lkb=rttb::models::NTCPLKBModel(dvhPtr, d50Val, mVal, aVal); + CHECK_EQUAL(0,lkb.getValue()); + CHECK_EQUAL(dvhPtr,lkb.getDVH()); + CHECK_EQUAL(aVal,lkb.getA()); + CHECK_EQUAL(mVal,lkb.getM()); + CHECK_EQUAL(d50Val,lkb.getD50()); + + //2) test init (calcxxx) + CHECK_NO_THROW(lkb.init(1)); + lkb.getValue(); + + //3) test set/get + lkb=rttb::models::NTCPLKBModel(); + CHECK_EQUAL(0,lkb.getA()); + CHECK_EQUAL(0,lkb.getM()); + CHECK_EQUAL(0,lkb.getD50()); + lkb.setDVH(dvhPtr); + CHECK_EQUAL(dvhPtr,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(models::getCurveEUDVSBioModel(lkb)); + + aParameterVector.clear(); + aParameterVector.push_back(d50Val+5); + CHECK_THROW_EXPLICIT(lkb.setParameterVector(aParameterVector),core::InvalidParameterException); + aParameterVector.push_back(mVal+0.2); + aParameterVector.push_back(aVal+0.5); + CHECK_NO_THROW(lkb.setParameterVector(aParameterVector)); + CHECK_EQUAL(aVal+0.5,lkb.getA()); + CHECK_EQUAL(mVal+0.2,lkb.getM()); + CHECK_EQUAL(d50Val+5,lkb.getD50()); + + for (int i = 0; i < 3; i++){ + CHECK_NO_THROW(lkb.setParameterByID(i,models::BioModelParamType(i))); + } + + CHECK_THROW_EXPLICIT(lkb.setParameterByID(3,4.0),core::InvalidParameterException); + + CHECK_EQUAL(0,lkb.getParameterID("d50")); + CHECK_EQUAL(0,lkb.getD50()); + CHECK_EQUAL(1,lkb.getParameterID("m")); + CHECK_EQUAL(1,lkb.getM()); + CHECK_EQUAL(2,lkb.getParameterID("a")); + CHECK_EQUAL(2,lkb.getA()); + + //test NTCPRSModel + //1) test constructors (values as expected?) + CHECK_NO_THROW(rttb::models::NTCPRSModel()); + models::BioModelParamType gammaVal = 1.7; + models::BioModelParamType sVal = 1; + CHECK_NO_THROW(rttb::models::NTCPRSModel(dvhPtr,d50Val,gammaVal,sVal)); + rttb::models::NTCPRSModel rs=rttb::models::NTCPRSModel(dvhPtr,d50Val,gammaVal,sVal); + CHECK_EQUAL(dvhPtr,rs.getDVH()); + CHECK_EQUAL(d50Val,rs.getD50()); + CHECK_EQUAL(gammaVal,rs.getGamma()); + CHECK_EQUAL(sVal,rs.getS()); + + rs=rttb::models::NTCPRSModel(); + CHECK_EQUAL(0,rs.getGamma()); + CHECK_EQUAL(0,rs.getS()); + CHECK_EQUAL(0,rs.getD50()); + + //3) test set/get + rs.setDVH(dvhPtr); + CHECK_EQUAL(dvhPtr,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()); + + //2) test init (calcxxx) + CHECK_NO_THROW(rs.init(1)); + + //3) test set/get continued + aParameterVector.clear(); + aParameterVector.push_back(d50Val+5); + CHECK_THROW_EXPLICIT(rs.setParameterVector(aParameterVector),core::InvalidParameterException); + aParameterVector.push_back(gammaVal+0.2); + aParameterVector.push_back(sVal+0.5); + CHECK_NO_THROW(rs.setParameterVector(aParameterVector)); + CHECK_EQUAL(gammaVal+0.2,rs.getGamma()); + CHECK_EQUAL(sVal+0.5,rs.getS()); + CHECK_EQUAL(d50Val+5,rs.getD50()); + + for (int i = 0; i < 3; i++){ + CHECK_NO_THROW(rs.setParameterByID(i,models::BioModelParamType(i))); + } + + CHECK_THROW_EXPLICIT(rs.setParameterByID(3,4.0),core::InvalidParameterException); + + CHECK_EQUAL(0,rs.getParameterID("d50")); + CHECK_EQUAL(0,rs.getD50()); + CHECK_EQUAL(1,rs.getParameterID("gamma")); + CHECK_EQUAL(1,rs.getGamma()); + CHECK_EQUAL(2,rs.getParameterID("s")); + CHECK_EQUAL(2,rs.getS()); + + //Scatter plot tests + CHECK_NO_THROW(models::getScatterPlotVary1Parameter(tcplq,0,alpha,0,normalizationDose));//variance=0, will be set to 1e-30 + CHECK_THROW_EXPLICIT(models::getScatterPlotVary1Parameter(tcplq,0,alpha,alpha*0.1,0),core::InvalidParameterException);//normalisationdose=0 + CHECK_THROW_EXPLICIT(models::getScatterPlotVary1Parameter(tcplq,0,alpha,alpha*0.1,normalizationDose,10000,0,0),core::InvalidParameterException);//maxDose-minDose=0 + + RETURN_AND_REPORT_TEST_SUCCESS; + + } + + }//testing +}//rttb \ No newline at end of file diff --git a/testing/models/CMakeLists.txt b/testing/models/CMakeLists.txt new file mode 100644 index 0000000..af42f50 --- /dev/null +++ b/testing/models/CMakeLists.txt @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Setup the system information test. Write out some basic failsafe +# information in case the test doesn't run. +#----------------------------------------------------------------------------- + + +SET(MODELS_TESTS ${EXECUTABLE_OUTPUT_PATH}/rttbModelTests) +SET(MODELS_HEADER_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbModelsHeaderTest) + +SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) + +SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) + + +#----------------------------------------------------------------------------- +ADD_TEST(BioModelTest ${MODELS_TESTS} BioModelTest) +ADD_TEST(BioModelScatterPlotTest ${MODELS_TESTS} BioModelScatterPlotTest) + +RTTB_CREATE_TEST_MODULE(rttbModel DEPENDS RTTBModels RTTBOtherIO PACKAGE_DEPENDS Boost Litmus DCMTK) + diff --git a/testing/models/DummyModel.cpp b/testing/models/DummyModel.cpp new file mode 100644 index 0000000..4b4aabf --- /dev/null +++ b/testing/models/DummyModel.cpp @@ -0,0 +1,101 @@ +// ----------------------------------------------------------------------- +// 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 "DummyModel.h" + +namespace rttb{ + namespace models{ + DummyModel::DummyModel(DVHPointer aDvh): BioModel(aDvh){ + _calcCount = 0; + _setParam1Count = 0; + _setParam2Count = 0; + _setParam3Count = 0; + } + + BioModelValueType DummyModel::calcModel(const double doseFactor){ + _calcCount++; + _value = doseFactor; + return _value; + } + + void DummyModel::setParameterVector(const ParamVectorType aParameterVector){ + if(aParameterVector.size()!=3) + { + std::cerr<<"aParameterVector.size must be 3! Nothing will be done."< + +#include "rttbScatterTester.h" + +namespace rttb{ + + namespace testing{ + + ScatterTester::ScatterTester(models::CurveDataType aReferenceCurve, models::ScatterPlotType aCompareScatter, models::BioModelParamType aVariance){ + _referenceCurve = aReferenceCurve; + _compareScatter = aCompareScatter; + _variance = aVariance; + _numDifference = 0; + _allowExceptions = false; + } + + void ScatterTester::setReferenceCurve(const models::CurveDataType aReferenceCurve){ + _referenceCurve = aReferenceCurve; + } + void ScatterTester::setCompareScatter(const models::ScatterPlotType aCompareScatter){ + _compareScatter = aCompareScatter; + } + void ScatterTester::setVariance(const models::BioModelParamType aVariance){ + _variance = aVariance; + } + void ScatterTester::setAllowExceptions(const bool allow){ + _allowExceptions = allow; + }; + + + lit::StringType ScatterTester::getTestDescription(void) const { + return "Compare a model curve and the corresponding scatter plot. The values should be comparable."; + }; + + bool ScatterTester::doCheck(void) const{ + _pResults->onTestStart(getCurrentTestLabel()); + + const double scatterError = errorConstant*10+_variance; + + _numDifference = 0; + _maxDifference = 0; + _meanDifference = 0; + + double lower,upper, difference,oldVal; + models::CurveDataType::const_iterator iterC = _referenceCurve.begin(); + oldVal = iterC->second; + models::ScatterPlotType::const_iterator iter; + for (iter = _compareScatter.begin(); iter != _compareScatter.end(); ++iter){ + double currentKey = iter->first; + //only set old value if the reference iterator iterC moves + if (currentKey > iterC->first){ + oldVal = iterC->second; + while(currentKey > iterC->first){ + ++iterC; + } + } + if (iterC != _referenceCurve.end()){ + //determine if curve is ascending or descending + lower = oldVal; + upper = iterC->second; + if(upper < lower){ + lower = iterC->second; + upper = oldVal; + } + //check boundaries because no interpolation is done + if (upper<(iter->second).first) { + difference = abs(upper-(iter->second).first); + if (difference < scatterError){continue;} + else if (difference > _maxDifference){ + _maxDifference = difference; + } + _meanDifference+=difference; + ++_numDifference; + } + if(lower>(iter->second).first){ + difference = abs(upper-(iter->second).first); + if (difference < scatterError){continue;} + else if (difference > _maxDifference){ + _maxDifference = difference; + } + _meanDifference+=difference; + ++_numDifference; + } + } + }//end for(iter = _compareScatter.begin(); iter != _compareScatter.end(); ++iter) + if (_numDifference > 0){ + _meanDifference/=_numDifference; + if (!_allowExceptions){ + return false; + } + else{ + // allow up to 5% exceptions + if(_numDifference/_compareScatter.size() <= 0.05){ + return true; + } + return false; + } + } + return true; + } + + void ScatterTester::handleSuccess(void) const{ + std::ostringstream stream; + if (!_allowExceptions){ + stream << "Curve and scatter plot are similar"<onTestSuccess(getCurrentTestLabel(), stream.str()); + } + + void ScatterTester::handleFailure(void) const{ + std::ostringstream stream; + stream << "Curve and scatter plot do not correspond"<< std::endl; + stream << std::endl << "Error voxel count: "<< _numDifference << std::endl; + stream << std::endl << "Maximum difference: "<< _maxDifference << std::endl; + stream << std::endl << "Mean difference: "<< _meanDifference << ", variance was "<<_variance<onTestFailure(getCurrentTestLabel(), stream.str()); + } + + } +} \ No newline at end of file diff --git a/testing/models/rttbScatterTester.h b/testing/models/rttbScatterTester.h new file mode 100644 index 0000000..4bce3fe --- /dev/null +++ b/testing/models/rttbScatterTester.h @@ -0,0 +1,106 @@ +// ----------------------------------------------------------------------- +// 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/trunk/testing/core/DVHCalculatorTest.cpp $ +*/ + +#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 + 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 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