diff --git a/CMakeLists.txt b/CMakeLists.txt index d651f56..706b879 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,312 +1,306 @@ #----------------------------------------------------------------------------- # This is the root RTToolbox CMakeList file. #----------------------------------------------------------------------------- PROJECT(RTToolbox) CMAKE_MINIMUM_REQUIRED(VERSION 3.1) # RTToolbox version number. SET(RTToolbox_VERSION_MAJOR "5") SET(RTToolbox_VERSION_MINOR "1") 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}") SET(RTToolbox_PREFIX "RTTB") # default build type SET(CMAKE_BUILD_TYPE Release) MARK_AS_ADVANCED(BUILD_SHARED_LIBS) IF (WIN32) IF (MSVC_VERSION LESS 1800) MESSAGE(FATAL_ERROR "RTToolbox requires at least Visual Studio 2013.") ELSEIF (MSVC_VERSION GREATER_EQUAL 1910) IF (${CMAKE_VERSION} VERSION_LESS "3.10.3") MESSAGE(AUTHOR_WARNING "Please use CMake version 3.10.3 or newer for Visual Studio 2017 as new boost versions are not recognized using old CMake versions.") ENDIF() ENDIF() add_definitions(-D_SCL_SECURE_NO_WARNINGS) ELSE (WIN32) IF (CMAKE_COMPILER_IS_GNUCC) IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.3) MESSAGE(AUTHOR_WARNING "RTToolbox was tested with GCC 5 and GCC 7 only. You are using GCC " ${CMAKE_CXX_COMPILER_VERSION} ". You might need to change some code in order to compile RT Toolbox.") ENDIF() ELSE() MESSAGE(AUTHOR_WARNING "RTToolbox was only tested with GCC. This compiler might not work.") ENDIF() ENDIF(WIN32) #----------------------------------------------------------------------------- # CMake Function(s) and Macro(s) #----------------------------------------------------------------------------- include(cmake/MacroParseArguments.cmake) include(cmake/rttbMacroCreateModuleConf.cmake) include(cmake/rttbMacroCreateModule.cmake) include(cmake/rttbMacroCreateApplication.cmake) include(cmake/rttbMacroCheckModule.cmake) include(cmake/rttbMacroUseModule.cmake) include(cmake/rttbMacroCreateTestModule.cmake) include(cmake/rttbFunctionOrganizeSources.cmake) include(cmake/rttbMacroCreateApplicationTests.cmake) #----------------------------------------------------------------------------- # Basis config RTTB module infrastructure #----------------------------------------------------------------------------- set(RTTB_MODULES_CONF_DIR ${RTToolbox_BINARY_DIR}/modulesConf CACHE INTERNAL "Modules Conf") set(RTTB_MODULES_PACKAGE_DEPENDS_DIR ${RTToolbox_SOURCE_DIR}/cmake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${RTTB_MODULES_PACKAGE_DEPENDS_DIR}) #----------------------------------------------------------------------------- # Testing setup # Configure Dart testing support. This should be done before any # MESSAGE(FATAL_ERROR ...) commands are invoked. #----------------------------------------------------------------------------- #build no tests as default OPTION(BUILD_TESTING "build tests" OFF) IF(BUILD_TESTING) SET(CTEST_NEW_FORMAT 1) INCLUDE(CTest) ENABLE_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 #----------------------------------------------------------------------------- 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 (NOT RTTB_CXX_STANDARD) set(RTTB_CXX_STANDARD 11) ENDIF (NOT RTTB_CXX_STANDARD) set(CMAKE_CXX_STANDARD ${RTTB_CXX_STANDARD} CACHE STRING "") set(CMAKE_CXX_STANDARD_REQUIRED 1) OPTION(CMAKE_CXX_EXTENSIONS "" ON) MARK_AS_ADVANCED(CMAKE_CXX_STANDARD CMAKE_CXX_STANDARD_REQUIRED CMAKE_CXX_EXTENSIONS) IF (WIN32) OPTION(BUILD_SHARED_LIBS "Build RTToolbox with shared libraries." OFF) ELSE (WIN32) OPTION(BUILD_SHARED_LIBS "Build RTToolbox with shared libraries." ON) ENDIF (WIN32) IF(NOT BUILD_SHARED_LIBS) IF(UNIX) MESSAGE(FATAL_ERROR "RTToolbox currently does not support a static build on unix like systems. We are working on that...") ENDIF(UNIX) ENDIF(NOT BUILD_SHARED_LIBS) SET(RTTB_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) #Raise warning level (MSVC has W3 default warning level) IF (WIN32) IF(NOT BUILD_SHARED_LIBS) set(CMAKE_CXX_FLAGS "/W4 /EHsc") ENDIF() ELSE() IF (CMAKE_COMPILER_IS_GNUCC) set(CMAKE_CXX_FLAGS "-Wall") ENDIF() ENDIF() 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. #----------------------------------------------------------------------------- OPTION(BUILD_All_Modules "All modules will be built" OFF) OPTION(BUILD_Apps "Determine if the CLI applications will be generated." OFF) MESSAGE (STATUS "generating Project RTToolbox") ADD_SUBDIRECTORY (code) IF (BUILD_Apps) ADD_SUBDIRECTORY (apps) ENDIF() IF (BUILD_TESTING) ADD_SUBDIRECTORY (testing) ENDIF (BUILD_TESTING) ADD_SUBDIRECTORY (documentation) #----------------------------------------------------------------------------- # Help other projects use RTToolbox. #----------------------------------------------------------------------------- EXPORT(PACKAGE RTToolbox) # 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 DESTINATION ${RTTOOLBOX_INSTALL_PACKAGE_DIR} COMPONENT Development - ) - INSTALL(EXPORT RTToolboxTargets - FILE RTToolboxTargets.cmake - DESTINATION ${RTTOOLBOX_INSTALL_PACKAGE_DIR} - COMPONENT Development - ) ENDIF(NOT RTToolbox_INSTALL_NO_DEVELOPMENT) diff --git a/README.md b/README.md index 2c02ca6..49e0999 100644 --- a/README.md +++ b/README.md @@ -1,256 +1,269 @@ + # RTToolbox RTToolbox is a software library to support quantitative analysis of treatment outcome for radiotherapy. The RTToolbox was designed following object-oriented design (OOD) principles and was implemented in the language C++. Features include: * import of radiotherapy data (e.g. dose distributions and structure sets) from DICOM-RT format and other standard image processing formats * DVH calculation * Dose statistic calculation * arithmetic operations on dose distributions * structure relationship analyses (e.g. fully-contained, partially-contained) * Calculation of dose comparison indices such as Conformity Index (CI), Homogeneity Index (HI) and Conformation Number (CN) * Calculation of biological models including TCP, NTCP, EUD and BED Also, the RTToolbox provides apps e.g. for DVH/Dose Statistic calculation or Dose accumulation that provides a convenient access of RT scenarios without computer-science knowledge. ## Getting Started These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. ### Prerequisites #### Build system * [CMake](https://cmake.org), version 3.1 or higher #### Compiler * Visual Studio 2013 * Visual Studio 2015 * Visual Studio 2017 * GCC 5.4 +* GCC 7.3 -Other compiler may work as well, but are not tested. +Other compilers may work as well, but are not tested. #### Linking Static/Dynamic library support Can be changed with advanced option `BUILD_SHARED_LIBS` :warning: building RTToolbox as dynamic library under Windows and as static library under Linux is an experimental feature. #### Third party libraries * [boost](http://www.boost.org ), version 1.64.0 or higher * [DCMTK](http://dicom.offis.de/dcmtk.php.en ), with RT support - 3.6.1_20121102 or newer * [ITK](https://itk.org ), version 4.4 or higher (*optional*) * for DoseInterpolation support with ITK transformation or ITK File IO support * [MatchPoint](http://mitk.org/download/thirdparty/MatchPoint_rev1610.tar.gz ), version 0.12 or higher (*optional*) * for DoseInterpolation support with MatchPoint registration objects :information_source: To make sure everything runs smoothly, please make sure that all libraries and the RTToolbox are either compiled with `/MD` or `/MT` flags. ##### Boost +In case you work with Windows, we recommend using the [pre-build versions of boost](https://sourceforge.net/projects/boost/files/boost-binaries/). + +If you want to build the library yourself, consider the following: + Build (using the same compiler options as RTToolbox, usually `STATIC LINKING` and `x64` architecture). The following components are needed: * `filesystem`, * `system`, * `thread` and * `program_options` * if you plan to build the apps (*optional*) -:information_source: eventually, it might be needed to add the CMake variable `BOOST_LIBRARY_DIR` and set it to the respective library. +:information_source: eventually, it might be needed to add the CMake variable `BOOST_LIBRARYDIR` and set it to the respective library path of boost. For Windows: To build Boost open a command prompt, change to your boost source directory and copy following command(s): Debug: -b2 -j12 --with-filesystem --with-system --with-thread --with-program_options --with-date_time --with-atomic --with-chrono toolset=msvc-14.1 address-model=64 variant=debug threading=multi link=shared define=_BIND_TO_CURRENT_VCLIBS_VERSION +`b2 -j12 --with-filesystem --with-system --with-thread --with-program_options --with-date_time --with-atomic --with-chrono toolset=msvc-14.1 address-model=64 variant=debug threading=multi link=shared define=_BIND_TO_CURRENT_VCLIBS_VERSION` Release: -b2 -j12 --with-filesystem --with-system --with-thread --with-program_options --with-date_time --with-atomic --with-chrono toolset=msvc-14.1 address-model=64 variant=release threading=multi link=shared +`b2 -j12 --with-filesystem --with-system --with-thread --with-program_options --with-date_time --with-atomic --with-chrono toolset=msvc-14.1 address-model=64 variant=release threading=multi link=shared` If you don´t require `program_options` delete `--with-program_options` from the command before executing it. ##### DCMTK For Windows: To compile DCMTK with `/MD` flags (standard for all other libs), you need to patch the CMAKE options of DCMTK (`PathToDCMTK\CMake\dcmtkPrepare.cmake`), either by replacing `"/MT"` with `"/MD"` or by explicitly 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 .*") ENDIF(DCMTK_OVERWRITE_WIN32_COMPILER_FLAGS AND NOT BUILD_SHARED_LIBS) ``` -Then build DCMTK. `BUILD_APPS` can be switched off. +`BUILD_APPS` can be switched off. +Then build DCMTK. + +For Linux: +install required dependencies (Ubuntu 18.04 and newer): `sudo apt-get install libpng-dev libtiff5-dev libxml2-dev libjpeg8-dev zlib1g-dev libwrap0-dev libssl-dev` +install required dependencies (Ubuntu 17.10 and older): `sudo apt-get install libpng12-dev libtiff5-dev libxml2-dev libjpeg8-dev zlib1g-dev libwrap0-dev libssl-dev` +Enable `BUILD_SHARED_LIBS`. `BUILD_APPS` can be switched off. ##### ITK Build ITK with default options. :warning: ensure that compiler enables C++11 features by setting `CMAKE_CXX_STANDARD=11` (default for supported compilers) :warning: Only use one ITK version consistently throughout all libraries and RTToolbox! Otherwise, linker errors will occur. ##### MatchPoint -Configure MatchPoint. You need to disable BUILD_TESTING before building it. +Configure MatchPoint. Please disable `BUILD_TESTING` before building it. :warning: ensure that compiler enables C++11 features by setting `CMAKE_CXX_STANDARD=11` (default for supported compilers) :warning: Only use one ITK version consistently throughout all libraries and RTToolbox! Otherwise, linker errors will occur. ### Building RT-Toolbox * Configure with CMake -* Set `BOOST_INCLUDE_DIR` and `BOOST_DIR` to the main boost directory (where `boost_build.jam` is located). Eventually, you have to set `BOOST_LIBRARY_DIR` to pathToMainBoostDirectory/stage/lib. +* Set `BOOST_INCLUDE_DIR` to the main boost directory. Eventually set `BOOST_LIBRARYDIR` to the respective path (e.g. `/lib64-msvc-14.1\` for Visual Studio 2017 and 64-bit) * Select all packages you like to build (Parameters `BUILD_*` ; e.g. `BUILD_IO_Dicom`). * `BUILD_IO_Dicom`: Reading and writing of DICOM-RT files * `BUILD_IO_HELAX`: Reading of Helax DICOM files * `BUILD_IO_ITK`: Generic reading/writing with ITK * `BUILD_Interpolation`: Dose Interpolation * `BUILD_InterpolationMatchPointTransformation`: Dose Interpolation with Match Point registration support. * `BUILD_Masks`: Voxelization support * `BUILD_Models`: Calculation of dosimetrical models like TCP, NTCP etc. * `BUILD_Apps`: To build the RTTB command line apps (five available) * `BioModelCalc`: calculate the radiobiological effect based on dose * `DoseAcc`: Do dose accumulation * `DoseMap`: Do dose mapping * `DoseTool`: Compute Dose statistics and DVH * `VoxelizerTool`: Voxelize an RTSTRUCT file Some modules of RT-Toolbox are mandatory (e.g. `RTTBCore`) and build automatically. :information_source: enabling `BUILD_All_Modules` builds all modules (except Apps and Testing modules). -:information_source: if you build RTTB with VS dynamic, you must ensure that code that uses RTTB DLLs uses the same stl +:information_source: if you build RTTB with VS dynamic, you must ensure that code that uses RTTB DLLs uses the same STL -Set DCMTK_DIR to your dcmtk binary file directory and DCMTK_SOURCE_DIR to your dcmtk source directory. +Set `DCMTK_DIR` to your dcmtk binary file directory and `DCMTK_SOURCE_DIR` to your dcmtk source directory. -If you want to build RT-Toolbox with ITK and/or MatchPoint set your ITK_DIR to your itk binary file directory and/or MatchPoint_DIR to your binary matchpoint directory. +If you want to build RT-Toolbox with ITK and/or MatchPoint set your `ITK_DIR` to your itk binary file directory and/or `MatchPoint_DIR` to your binary matchpoint directory. All directory entries left empty do not require a manual input. Finally, Generate the compilation files for your environment and built it. ### Examples Some examples can be found in ´testing/examples´: * `RTBioModelExampleTest`: Computation of Biological model indices (TCP/NTCP) from a given DVH * `RTDoseIndexTest`: Computation of dose indices (Conformation Number, Conformal Index, Conformity index) from a given DVH * `RTDoseStatisticsDicomTest`: Computation of dose statistics (max dose/mean dose/min dose/Dx/Vx) based on dose data for a specified structure * `RTDVHTest`: Computation of statistics (max value/mean value/min value/Dx/Vx) based on a DVH Other examples include: * `DVHCalculatorTest` (`testing/core`): Computation of a DVH from dose and structure * `VoxelizationValidationTest` (`testing/validation`): Computation of a voxelization * `ITKDoseAccessorConverterTest`: (`testing/io/itk`): Saving image RTToolbox image data as an ITK file ## Running the tests [CTest](https://cmake.org/Wiki/CMake/Testing_With_CTest) is used as testing framework. See their documentation for general testing questions. :information_source: The used testing library Litmus is build automatically. :warning: currently, you have access to testing data only with ssh. That means that a [phabricator](https://phabricator.mitk.org/) account and access to `RTTB-data` repository is mandatory. Please contact rttb(at)dkfz.de for further information. Enabling testing is done as follows: * Enable `BUILD_TESTING` * Configure with CMake * Enable tests of interest * Generate CMake configuration +* Build RT-Toolbox * Run tests (build `RUN_TESTS` project or call `ctest` in commandline) to ensure that everything is correct. :information_source: `BUILD_Tester_All` builds all test modules. ## Contributing Please add a github issue and send a pull request if you want to contribute. ## Versioning We use the Ubuntu Release versioning scheme. v2017.02 was released in February 2017. We aim at releasing stable versions once a year. For the versions available, see the [tags on this repository](https://github.com/MIC-DKFZ/RTTB/tags). ## Authors See the list of [contributors](https://github.com/MIC-DKFZ/RTTB/contributors) who participated in this project. ## License This project is licensed under the BSD License - see the [LICENSE](LICENSE) file for details ## Contact Software Development for Integrated Diagnostics and Therapy (SIDT), German Cancer Research Center (DKFZ), Heidelberg, Germany. Web: https://www.dkfz-heidelberg.de/en/mic/research/SIDT/sidt_projects.html E-mail: rttb(at)dkfz.de ## Acknowledgments * **Billie Thompson** - *Template of the readme* - [PurpleBooth](https://github.com/PurpleBooth) diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 37d61b6..427ffcd 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,56 +1,56 @@ MESSAGE(STATUS "processing RTToolbox apps") #extract and build ArgumentParsingLib include(ExternalProject) message(STATUS "ArgumentParsingLib will be automatically downloaded and built.") set(ArgumentParsingLib_SOURCE_DIR "${CMAKE_BINARY_DIR}/external/ArgumentParsingLib-src") set(ArgumentParsingLib_BUILD_DIR "${CMAKE_BINARY_DIR}/external/ArgumentParsingLib-build") set(ArgumentParsingLib_CMAKE_DIR "${CMAKE_BINARY_DIR}/external/ArgumentParsingLib-cmake") IF(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(ARG_BUILD_SHARED_LIBS ON) ELSE(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(ARG_BUILD_SHARED_LIBS OFF) ENDIF(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) ExternalProject_Add( ArgumentParsingLib URL ${RTToolbox_SOURCE_DIR}/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz - URL_HASH SHA1=f8b18b107af82f9e62324c21b967ab925972b8de + URL_HASH SHA1=6E55D67860D3F571D5EAB393F4AF70AD63DAE377 SOURCE_DIR ${ArgumentParsingLib_SOURCE_DIR} BINARY_DIR ${ArgumentParsingLib_BUILD_DIR} PREFIX ${ArgumentParsingLib_CMAKE_DIR} INSTALL_COMMAND "" UPDATE_COMMAND "" CMAKE_ARGS -DBUILD_TESTS:BOOL=OFF -DBoost_INCLUDE_DIR:STRING=${Boost_INCLUDE_DIR} -DBUILD_SHARED_LIBS:BOOL=${ARG_BUILD_SHARED_LIBS} ) OPTION(BUILD_App_DoseAcc "Determine if the demo application DoseAcc will be generated." ON) IF(BUILD_App_DoseAcc) ADD_SUBDIRECTORY(DoseAcc) ENDIF() OPTION(BUILD_App_DoseMap "Determine if the application DoseMap will be generated." ON) IF(BUILD_App_DoseMap) ADD_SUBDIRECTORY(DoseMap) ENDIF() OPTION(BUILD_App_DoseTool "Determine if the application DoseTool will be generated." ON) IF(BUILD_App_DoseTool) ADD_SUBDIRECTORY(DoseTool) ENDIF() OPTION(BUILD_App_VoxelizerTool "Determine if the application VoxelizerTool will be generated." ON) IF(BUILD_App_VoxelizerTool) ADD_SUBDIRECTORY(VoxelizerTool) ENDIF() OPTION(BUILD_App_BioModelCalc "Determine if the demo application BioModelCalc will be generated." ON) IF(BUILD_App_BioModelCalc) ADD_SUBDIRECTORY(BioModelCalc) ENDIF() \ No newline at end of file diff --git a/apps/VoxelizerTool/VoxelizerTool.cpp b/apps/VoxelizerTool/VoxelizerTool.cpp index 789b713..73acf4f 100644 --- a/apps/VoxelizerTool/VoxelizerTool.cpp +++ b/apps/VoxelizerTool/VoxelizerTool.cpp @@ -1,144 +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. // //------------------------------------------------------------------------ #include #include "VoxelizerToolHelper.h" #include "VoxelizerToolCmdLineParser.h" #include "VoxelizerToolApplicationData.h" #include "rttbDoseLoader.cpp" #include "rttbStructLoader.cpp" int main(int argc, const char** argv) { rttb::apps::voxelizerTool::ApplicationData appData; const std::string appCategory = "RT-Toolbox App"; const std::string appName = "VoxelizerTool"; const std::string appDesc = "An App to voxelize RT structures in a reference image."; const std::string appContributor = "SIDT@DKFZ"; const std::string appVersion = RTTB_FULL_VERSION_STRING; boost::shared_ptr argParser; try { argParser = boost::make_shared(argc, argv, appName, appVersion, appDesc, appContributor, appCategory); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; return 5; } // This is vital. The application needs to exit if the "help" or "version" parameter is set // because this means the other parameters won't be parsed. if (argParser->isSet(argParser->OPTION_HELP) || argParser->isSet(argParser->OPTION_VERSION) || argParser->isSet(argParser->OPTION_XML)) { return 0; } rttb::apps::voxelizerTool::populateAppData(argParser, appData); + if (argParser->isSet(argParser->OPTION_ALL_STRUCTS)) + { + if (appData._outputFilename == "out.nrrd") + { + appData._outputFilename = "output.nrrd"; + } + if (appData._multipleStructs == false || appData._regEx != ".*") + { + std::cout << std::endl << "WARNING: MultipleStructs will be turned on and struct regex will be generalized!" << std::endl; + appData._multipleStructs = true; + appData._regEx = ".*"; + } + + } + std::cout << std::endl << "*******************************************" << std::endl; std::cout << "Struct file: " << appData._structFile << std::endl; std::cout << "Reference Image: " << appData._referenceFile << std::endl; std::cout << "Output file: " << appData._outputFilename << std::endl; std::cout << "Struct regex: " << appData._regEx << std::endl; std::cout << "Add structures: " << appData._addStructures << std::endl; std::cout << "Multiple Struct: " << appData._multipleStructs << std::endl; std::cout << "Strict voxelization: " << !appData._noStrictVoxelization << std::endl << std::endl; std::cout << "reading reference and structure file..." << std::endl; try { appData._dose = rttb::io::utils::loadDose(appData._referenceFile, appData._referenceFileLoadStyle); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (...) { std::cerr << "Error!!! unknown error while reading input dose image." << std::endl; return 2; } try { appData._struct = rttb::io::utils::loadStruct(appData._structFile, appData._structFileLoadStyle, appData._regEx); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (...) { std::cerr << "Error!!! unknown error while reading input struct image." << std::endl; return 2; } std::cout << "done." << std::endl; try { rttb::apps::voxelizerTool::processData(appData); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error while doing voxelization!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (itk::ExceptionObject& err) { std::cerr << "ExceptionObject caught !" << std::endl; std::cerr << err << std::endl; return 3; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 3; } catch (...) { std::cerr << "Error!!! unknown error while doing voxelization." << std::endl; return 3; } return 0; } diff --git a/apps/VoxelizerTool/VoxelizerToolApplicationData.cpp b/apps/VoxelizerTool/VoxelizerToolApplicationData.cpp index 40228c3..0447077 100644 --- a/apps/VoxelizerTool/VoxelizerToolApplicationData.cpp +++ b/apps/VoxelizerTool/VoxelizerToolApplicationData.cpp @@ -1,87 +1,93 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include "VoxelizerToolApplicationData.h" #include "VoxelizerToolCmdLineParser.h" namespace rttb { namespace apps { namespace voxelizerTool { ApplicationData:: ApplicationData() { this->reset(); } void ApplicationData:: reset() { _structFile = ""; _referenceFile = ""; _outputFilename = ""; _regEx=""; _multipleStructs = false; _binaryVoxelization = false; _addStructures = false; _noStrictVoxelization = false; + _allStructs = false; } void populateAppData(boost::shared_ptr argParser, ApplicationData& appData) { appData._structFile = argParser->get(argParser->OPTION_STRUCT_FILE); appData._referenceFile = argParser->get(argParser->OPTION_REFERENCE_FILE); appData._outputFilename = argParser->get(argParser->OPTION_OUTPUT_FILE_NAME); appData._referenceFileLoadStyle = argParser->get(argParser->OPTION_REFERENCE_FILE_LOAD_STYLE); //only valid option for reading structs is dicom until now. appData._structFileLoadStyle = "dicom"; appData._regEx = argParser->get(argParser->OPTION_REGEX); if (argParser->isSet(argParser->OPTION_MULTIPLE_STRUCTS)) { appData._multipleStructs = true; if (argParser->isSet(argParser->OPTION_ADDSTRUCTURES)) { appData._addStructures = true; } else { appData._addStructures = false; } } if (argParser->isSet(argParser->OPTION_BINARY_VOXELIZATION)) { appData._binaryVoxelization = true; } if (argParser->isSet(argParser->OPTION_ADDSTRUCTURES)) { appData._multipleStructs = false; appData._addStructures = true; } if (argParser->isSet(argParser->OPTION_NO_STRICT_VOXELIZATION)) { appData._noStrictVoxelization = true; } + + if (argParser->isSet(argParser->OPTION_ALL_STRUCTS)) + { + appData._allStructs = true; + } } } } } diff --git a/apps/VoxelizerTool/VoxelizerToolApplicationData.h b/apps/VoxelizerTool/VoxelizerToolApplicationData.h index 2ea07f6..74a6585 100644 --- a/apps/VoxelizerTool/VoxelizerToolApplicationData.h +++ b/apps/VoxelizerTool/VoxelizerToolApplicationData.h @@ -1,65 +1,66 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #ifndef __VoxelizerApplicationData_h #define __VoxelizerApplicationData_h #include "boost/shared_ptr.hpp" #include "rttbDoseAccessorInterface.h" #include "rttbStructureSetGeneratorInterface.h" #include #include namespace rttb { namespace apps { namespace voxelizerTool { class VoxelizerCmdLineParser; /*! @class ApplicationData @brief Class for storing all relevant variables needed in VoxelizerTool */ class ApplicationData { public: core::DoseAccessorInterface::Pointer _dose; core::StructureSet::Pointer _struct; std::string _structFile; std::string _referenceFile; std::string _outputFilename; std::string _regEx; std::string _referenceFileLoadStyle; std::string _structFileLoadStyle; bool _multipleStructs; bool _binaryVoxelization; bool _addStructures; bool _noStrictVoxelization; + bool _allStructs; /*! @brief Resets the variables. */ void reset(); ApplicationData(); }; /*! @brief Reads the necessary arguments from the VoxelizerCmdLineParser and writes them in the respective variables of ApplicationData. */ void populateAppData(boost::shared_ptr argParser, ApplicationData& appData); } } } #endif diff --git a/apps/VoxelizerTool/VoxelizerToolCmdLineParser.cpp b/apps/VoxelizerTool/VoxelizerToolCmdLineParser.cpp index 3dae3dd..fa044c0 100644 --- a/apps/VoxelizerTool/VoxelizerToolCmdLineParser.cpp +++ b/apps/VoxelizerTool/VoxelizerToolCmdLineParser.cpp @@ -1,115 +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. // //------------------------------------------------------------------------ #include "VoxelizerToolCmdLineParser.h" namespace rttb { namespace apps { namespace voxelizerTool { VoxelizerCmdLineParser::VoxelizerCmdLineParser(int argc, const char** argv, const std::string& name, const std::string& version, const std::string& description, const std::string& contributor, const std::string& category) : CmdLineParserBase(name, version, description, contributor, category) { //REQUIRED addOption(OPTION_STRUCT_FILE, OPTION_GROUP_REQUIRED, "Filename of the structfile (*.dcm)", 's', true); addInformationForXML(OPTION_STRUCT_FILE, cmdlineparsing::XMLGenerator::paramType::INPUT, {"dcm", "*"}); addOption(OPTION_REFERENCE_FILE, OPTION_GROUP_REQUIRED, "Filename of the reference image (*.dcm)", 'r', true); addInformationForXML(OPTION_REFERENCE_FILE, cmdlineparsing::XMLGenerator::paramType::INPUT, { "dcm", "*" }); addOptionWithDefaultValue(OPTION_OUTPUT_FILE_NAME, OPTION_GROUP_REQUIRED, "Set output file name. Remark: if it used in conjunction with flag -m, it is only regarded as " "hint for the file name pattern. VoxelizerTool will add a suffix indicating the voxelized " - "structure to each filename.","out.hdr","out.hdr", 'o', true); - addInformationForXML(OPTION_OUTPUT_FILE_NAME, cmdlineparsing::XMLGenerator::paramType::OUTPUT, { "hdr", "nrrd", "*" }); + "structure to each filename.","out.nrrd","out.nrrd", 'o', true); + addInformationForXML(OPTION_OUTPUT_FILE_NAME, cmdlineparsing::XMLGenerator::paramType::OUTPUT, { "nrrd", "hdr", "*" }); addPositionalOption(OPTION_STRUCT_FILE,1); addPositionalOption(OPTION_REFERENCE_FILE, 1); addPositionalOption(OPTION_OUTPUT_FILE_NAME, 1); - addOption(OPTION_REGEX, OPTION_GROUP_REQUIRED, - "set a regular expression describing the structs of interest",'e', true); + addOptionWithDefaultValue(OPTION_REGEX, OPTION_GROUP_REQUIRED, + "set a regular expression describing the structs of interest",".*",".*",'e', true); addInformationForXML(OPTION_REGEX, cmdlineparsing::XMLGenerator::paramType::STRING); addOptionWithDefaultValue(OPTION_REFERENCE_FILE_LOAD_STYLE, OPTION_GROUP_REQUIRED, "set the load style for the reference file. Available styles are:" "\ndicom: normal dicom dose" "\nitk: use itk image loading" "\nitkDicom: use itk dicom image loading", "dicom", "dicom", 'y', true); addInformationForXML(OPTION_REFERENCE_FILE_LOAD_STYLE, cmdlineparsing::XMLGenerator::paramType::STRING); //OPTIONAL addOption(OPTION_MULTIPLE_STRUCTS, OPTION_GROUP_OPTIONAL, "if multiple structs match the regular expression" + OPTION_STRUCT_FILE + ", save all in files\n" "If structures 'Kidney_left' and 'Kidney_right' are defined,\n" "both are written under the names outputFile_Kidney_left.mhd and outputFile_Kidney_right.mhd",'m'); addInformationForXML(OPTION_MULTIPLE_STRUCTS, cmdlineparsing::XMLGenerator::paramType::BOOLEAN); addOption(OPTION_BINARY_VOXELIZATION, OPTION_GROUP_OPTIONAL, "Determines if the voxelization should be binarized (only values 0 or 1), the threshold value is by 0.5",'z'); addInformationForXML(OPTION_BINARY_VOXELIZATION, cmdlineparsing::XMLGenerator::paramType::BOOLEAN); addOption(OPTION_ADDSTRUCTURES, OPTION_GROUP_OPTIONAL, "Voxelizes multiple structs in one result file.",'a'); addInformationForXML(OPTION_ADDSTRUCTURES, cmdlineparsing::XMLGenerator::paramType::BOOLEAN); addOption(OPTION_NO_STRICT_VOXELIZATION, OPTION_GROUP_OPTIONAL, "Deviations of wrong voxel volumes are tolerated and corrected.",'i'); addInformationForXML(OPTION_NO_STRICT_VOXELIZATION, cmdlineparsing::XMLGenerator::paramType::BOOLEAN); + addOption(OPTION_ALL_STRUCTS, OPTION_GROUP_OPTIONAL, "Voxelizes all structures in a struct file",'f'); + addInformationForXML(OPTION_ALL_STRUCTS, cmdlineparsing::XMLGenerator::paramType::BOOLEAN); parse(argc, argv); } void VoxelizerCmdLineParser::validateInput() const { std::string referenceLoadStyle = get(OPTION_REFERENCE_FILE_LOAD_STYLE); if (referenceLoadStyle != "dicom" && referenceLoadStyle != "itk" && referenceLoadStyle != "itkDicom") { throw cmdlineparsing::InvalidConstraintException("Unknown load style for reference file:" + referenceLoadStyle + ".\nPlease refer to the help for valid loading style settings."); } if (get(OPTION_OUTPUT_FILE_NAME).find('.') == std::string::npos) { - throw cmdlineparsing::InvalidConstraintException(OPTION_OUTPUT_FILE_NAME + " has to specify a file format (e.g. output.hdr). None is given: " + + throw cmdlineparsing::InvalidConstraintException(OPTION_OUTPUT_FILE_NAME + " has to specify a file format (e.g. output.nrrd). None is given: " + get(OPTION_OUTPUT_FILE_NAME) ); } } void VoxelizerCmdLineParser::printHelp() const { cmdlineparsing::CmdLineParserBase::printHelp(); std::cout << "Example: VoxelizerTool -s structFile.dcm -r referenceFile.dcm -e Kidney -o outputFile.mhd -m" << std::endl; std::cout << "Computes a voxelization file outputFile.mhd based on the DICOMRT-STRUCT structFile.dcm " "in the geometry of referenceFile.dcm where "; std::cout << "the name of the struct matches the regular expression 'Kidney'.\n"; std::cout << "If structures 'Kidney_left' and 'Kidney_right' are defined, "; std::cout << "both are written under the names outputFile_Kidney_left.mhd and outputFile_Kidney_right.mhd (parameter -m)." << std::endl; } } } } \ No newline at end of file diff --git a/apps/VoxelizerTool/VoxelizerToolCmdLineParser.h b/apps/VoxelizerTool/VoxelizerToolCmdLineParser.h index 9ec9e91..abb2bb7 100644 --- a/apps/VoxelizerTool/VoxelizerToolCmdLineParser.h +++ b/apps/VoxelizerTool/VoxelizerToolCmdLineParser.h @@ -1,59 +1,59 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #ifndef __VoxelizerCmdLineParser_h #define __VoxelizerCmdLineParser_h #include "CmdLineParserBase.h" namespace rttb { namespace apps { namespace voxelizerTool { /*! @class VoxelizerCmdLineParser @brief Argument parsing is parametrized here based on ArgParserLib @see cmdlineparsing::CmdLineParserBase */ class VoxelizerCmdLineParser : public cmdlineparsing::CmdLineParserBase { public: VoxelizerCmdLineParser(int argc, const char** argv, const std::string& name, const std::string& version, const std::string& description, const std::string& contributor, const std::string& category); void validateInput() const override; void printHelp() const override; // Option groups const std::string OPTION_GROUP_REQUIRED = "Required Arguments"; const std::string OPTION_GROUP_OPTIONAL = "Optional Arguments"; // Parameters const std::string OPTION_STRUCT_FILE = "structFile"; const std::string OPTION_REFERENCE_FILE = "referenceFile"; const std::string OPTION_REFERENCE_FILE_LOAD_STYLE = "referenceFileLoadStyle"; const std::string OPTION_OUTPUT_FILE_NAME = "output"; const std::string OPTION_REGEX = "struct"; const std::string OPTION_MULTIPLE_STRUCTS = "multipleStructs"; const std::string OPTION_BINARY_VOXELIZATION = "binaryVoxelization"; const std::string OPTION_ADDSTRUCTURES = "addStructures"; const std::string OPTION_NO_STRICT_VOXELIZATION = "noStrictVoxelization"; - + const std::string OPTION_ALL_STRUCTS = "allStructs"; }; } } } #endif \ No newline at end of file diff --git a/cmake/PackageDepends/RTTB_ArgumentParsingLib_Config.cmake b/cmake/PackageDepends/RTTB_ArgumentParsingLib_Config.cmake index 56cf660..77765d3 100644 --- a/cmake/PackageDepends/RTTB_ArgumentParsingLib_Config.cmake +++ b/cmake/PackageDepends/RTTB_ArgumentParsingLib_Config.cmake @@ -1,14 +1,14 @@ #----------------------------------------------------------------------------- # ArgumentParsingLib is built automatically. Just set include dir and link directories #----------------------------------------------------------------------------- set(ArgumentParsingLib_INCLUDE_DIR ${ArgumentParsingLib_SOURCE_DIR}/main) set(ArgumentParsingLib_LIBRARY_DIR ${ArgumentParsingLib_BUILD_DIR}/main) -set(ArgumentParsingLib_Boost_INCLUDE_DIR ${Boost_INCLUDE_DIR}) +set(ArgumentParsingLib_Boost_INCLUDE_DIR ${Boost_INCLUDE_DIRS}) set(ArgumentParsingLib_Boost_LIBRARY_DIR ${Boost_LIBRARYDIR}) set(ArgumentParsingLib_Boost_LIBRARIES ${Boost_LIBRARIES}) LIST(APPEND ALL_INCLUDE_DIRECTORIES ${ArgumentParsingLib_INCLUDE_DIR} ${ArgumentParsingLib_Boost_INCLUDE_DIR}) LIST(APPEND ALL_LIBRARIES "${CMAKE_STATIC_LIBRARY_PREFIX}ArgumentParsingLib${CMAKE_STATIC_LIBRARY_SUFFIX}") LINK_DIRECTORIES(${ArgumentParsingLib_LIBRARY_DIR} ${ArgumentParsingLib_Boost_LIBRARY_DIR}) diff --git a/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake b/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake index d24e82e..d0f34ec 100644 --- a/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake +++ b/cmake/PackageDepends/RTTB_BoostBinaries_Config.cmake @@ -1,34 +1,19 @@ -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(NOT DEFINED RTTB_USE_MITK_BOOST) OPTION(RTTB_USE_MITK_BOOST "RTTB should use a boost which is available in the MITK superbuild external projects structure." OFF) MARK_AS_ADVANCED(RTTB_USE_MITK_BOOST) ENDIF(NOT DEFINED RTTB_USE_MITK_BOOST) IF(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(Boost_USE_STATIC_LIBS OFF) ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) ELSE(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(Boost_USE_STATIC_LIBS ON) ENDIF(BUILD_SHARED_LIBS OR RTTB_USE_MITK_BOOST) SET(BOOST_MIN_VERSION "1.64.0") FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} REQUIRED COMPONENTS filesystem system thread ${RTTB_Boost_ADDITIONAL_COMPONENT} QUIET) - 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) - - - + if(Boost_LIBRARIES) + LIST(APPEND ALL_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) + LIST(APPEND ALL_LIBRARIES ${Boost_LIBRARIES}) + link_directories(${Boost_LIBRARY_DIRS}) + endif() diff --git a/cmake/PackageDepends/RTTB_Boost_Config.cmake b/cmake/PackageDepends/RTTB_Boost_Config.cmake index 74effdf..3670d99 100644 --- a/cmake/PackageDepends/RTTB_Boost_Config.cmake +++ b/cmake/PackageDepends/RTTB_Boost_Config.cmake @@ -1,17 +1,5 @@ -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) - SET(BOOST_MIN_VERSION "1.64.0") FIND_PACKAGE(Boost ${BOOST_MIN_VERSION} REQUIRED QUIET) LIST(APPEND ALL_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS}) - - MARK_AS_ADVANCED(CLEAR Boost_INCLUDE_DIR) - -ENDIF(NOT Boost_FOUND) diff --git a/cmake/rttbMacroCreateModule.cmake b/cmake/rttbMacroCreateModule.cmake index 67c4597..34217c7 100644 --- a/cmake/rttbMacroCreateModule.cmake +++ b/cmake/rttbMacroCreateModule.cmake @@ -1,113 +1,115 @@ ################################################################## # # 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}) - INSTALL(TARGETS ${MODULE_PROVIDES} EXPORT RTToolboxTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) + EXPORT(TARGETS ${MODULE_PROVIDES} APPEND FILE ${RTToolbox_TARGETS_FILE}) + + INSTALL(TARGETS ${MODULE_PROVIDES} EXPORT RTToolboxExport LIBRARY DESTINATION lib ARCHIVE DESTINATION lib) 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/code/core/rttbBaseType.h b/code/core/rttbBaseType.h index 34d2fe6..7b5e336 100644 --- a/code/core/rttbBaseType.h +++ b/code/core/rttbBaseType.h @@ -1,613 +1,613 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #ifndef __BASE_TYPE_NEW_H #define __BASE_TYPE_NEW_H #include #include #include #include #include #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4251) #endif namespace rttb { const double errorConstant = 1e-5; const double reducedErrorConstant = 0.0001; - using UnsignedIndex1D = unsigned short; + using Index1D = unsigned short; - /*! @class UnsignedIndex3D + /*! @class Index3D @brief 3D index. */ - class UnsignedIndex3D: public boost::numeric::ublas::vector + class Index3D: public boost::numeric::ublas::vector { public: - UnsignedIndex3D() : boost::numeric::ublas::vector(3,0) {} - explicit UnsignedIndex3D(const UnsignedIndex1D value) : boost::numeric::ublas::vector(3, + Index3D() : boost::numeric::ublas::vector(3,0) {} + explicit Index3D(const Index1D value) : boost::numeric::ublas::vector(3, value) {} - UnsignedIndex3D(const UnsignedIndex1D xValue, const UnsignedIndex1D yValue, - const UnsignedIndex1D zValue) - : boost::numeric::ublas::vector(3, xValue) + Index3D(const Index1D xValue, const Index1D yValue, + const Index1D zValue) + : boost::numeric::ublas::vector(3, xValue) { (*this)(1) = yValue; (*this)(2) = zValue; } - const UnsignedIndex1D x() const + const Index1D x() const { return (*this)(0); } - const UnsignedIndex1D y() const + const Index1D y() const { return (*this)(1); } - const UnsignedIndex1D z() const + const Index1D z() const { return (*this)(2); } - friend bool operator==(const UnsignedIndex3D& gi1, const UnsignedIndex3D& gi2) + friend bool operator==(const Index3D& gi1, const Index3D& gi2) { if (gi1.size() != gi2.size()) { return false; } for (size_t 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) + friend std::ostream& operator<<(std::ostream& s, const Index3D& aVector) { s << "[ " << aVector(0) << ", " << aVector(1) << ", " << aVector(2) << " ]"; return s; } }; - using UnsignedIndexList = std::list; + using IndexList = std::list; using FileNameString = std::string; using ContourGeometricTypeString = std::string; using WorldCoordinate = double; /*! @class WorldCoordinate3D @brief 3D coordinate in real world coordinates like in DICOM to define ImagePositionPatient. */ class WorldCoordinate3D: public boost::numeric::ublas::vector { public: WorldCoordinate3D() : boost::numeric::ublas::vector(3,0) {} explicit 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; } const std::string toString() const { std::string s = std::to_string(x()) + " " + std::to_string(y()) + " " + std::to_string(z()); return s; } 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 (size_t i = 0; i < wc1.size(); i++) { if (wc1(i) != wc2(i)) { return false; } } return true; } bool equalsAlmost(const WorldCoordinate3D& another, double errorConstantWC = 1e-5) const { if (size() != another.size()) { return false; } double dist = norm_2(*this - another); return dist < errorConstantWC; } friend std::ostream& operator<<(std::ostream& s, const WorldCoordinate3D& aVector) { s << "[ " << aVector(0) << ", " << aVector(1) << ", " << aVector(2) << " ]"; return s; } }; /* ! @brief continuous index */ - using DoubleVoxelGridIndex3D = rttb::WorldCoordinate3D; + using ContinuousVoxelGridIndex3D = rttb::WorldCoordinate3D; - using ImageSize = rttb::UnsignedIndex3D; + using ImageSize = rttb::Index3D; using GridVolumeType = double; /*! @class SpacingVectorType3D @brief 3D spacing vector. @pre values of this vector may not be negative. */ class SpacingVectorType3D: public boost::numeric::ublas::vector { public: SpacingVectorType3D() : boost::numeric::ublas::vector(3,0) {} explicit SpacingVectorType3D(const GridVolumeType value) : boost::numeric::ublas::vector(3, value) { if (value < 0) { throw std::invalid_argument("received negative value"); } } SpacingVectorType3D(const GridVolumeType xValue, const GridVolumeType yValue, const GridVolumeType zValue) : boost::numeric::ublas::vector(3) { if (xValue < 0 || yValue < 0 || zValue < 0) { throw std::invalid_argument("received negative value"); } (*this)(0) = xValue; (*this)(1) = yValue; (*this)(2) = zValue; } SpacingVectorType3D(const SpacingVectorType3D& w) : SpacingVectorType3D(w.x(), w.y(), 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::string s = std::to_string(x()) + " " + std::to_string(y()) + " " + std::to_string(z()); return s; } 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 (size_t i = 0; i < wc1.size(); i++) { if (wc1(i) != wc2(i)) { return false; } } return true; } bool equalsAlmost(const SpacingVectorType3D& another, double errorConstantSV = 1e-5) const { if ((*this).size() != another.size()) { return false; } double dist = norm_2(*this - another); return dist < errorConstantSV; } 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 */ 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; } } explicit OrientationMatrix(const WorldCoordinate value) : boost::numeric::ublas::matrix(3, 3, value) {} bool equalsAlmost(const OrientationMatrix& anOrientationMatrix, double errorConstantOM=1e-5) 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 ((std::abs((*this)(m, n) - anOrientationMatrix(m, n)) > errorConstantOM)) { return false; } } }// end element comparison } else { return false; } } else { return false; } return true; } friend bool operator==(const OrientationMatrix& om1, const OrientationMatrix& om2) { return om1.equalsAlmost(om2, 0.0); } 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 */ using GridIndexType = unsigned int; /*! @class VoxelGridIndex3D @brief 3D voxel grid index in a discret geometry (matrix/image). @details analogous to DICOM where ImagePositionPatient gives the position of the center of the first coordinate (0/0/0) */ class VoxelGridIndex3D: public boost::numeric::ublas::vector { public: VoxelGridIndex3D() : boost::numeric::ublas::vector(3,0) {} explicit 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::string s = std::to_string(x()) + " " + std::to_string(y()) + " " + std::to_string(z()); return s; } - VoxelGridIndex3D& operator=(const UnsignedIndex3D& ui) + VoxelGridIndex3D& operator=(const Index3D& 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 (size_t 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,0) {} explicit 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::string s = std::to_string(x()) + " " + std::to_string(y()); return s; } friend bool operator==(const VoxelGridIndex2D& gi1, const VoxelGridIndex2D& gi2) { if (gi1.size() != gi2.size()) { return false; } for (size_t 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; } }; using GridSizeType = long; using VoxelGridID = int; //starts from 0 and is continuously counting all positions on the grid using VoxelGridDimensionType = unsigned int; typedef double FractionType, DVHVoxelNumber; typedef double DoseCalcType, DoseTypeGy, GenericValueType, DoseVoxelVolumeType, VolumeType, GridVolumeType, PercentType, VoxelNumberType, BEDType, LQEDType; using IDType = std::string; using StructureLabel = std::string; struct DVHRole { enum Type { TargetVolume = 1, HealthyTissue = 2, WholeVolume = 4, UserDefined = 128 } Type; }; struct DVHType { enum Type { Differential = 1, Cumulative = 2 } Type; }; using FileNameType = std::string; using PolygonType = std::vector; using PolygonSequenceType = std::vector; using IndexValueType = double; using DoseStatisticType = double; using DICOMRTFileNameString = std::string; using Uint16 = unsigned short; typedef std::string XMLString, StatisticsString; }//end: namespace rttb #ifdef _MSC_VER #pragma warning(pop) #endif #endif diff --git a/code/core/rttbGeometricInfo.cpp b/code/core/rttbGeometricInfo.cpp index 5d78d25..c1ba2ca 100644 --- a/code/core/rttbGeometricInfo.cpp +++ b/code/core/rttbGeometricInfo.cpp @@ -1,304 +1,304 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include "rttbGeometricInfo.h" #include #include #include 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() { using pmatrix = boost::numeric::ublas::permutation_matrix; 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; } void GeometricInfo::setImageSize(const ImageSize& aSize) { setNumColumns(aSize(0)); setNumRows(aSize(1)); setNumSlices(aSize(2)); } const ImageSize GeometricInfo::getImageSize() const { - return ImageSize(static_cast(getNumColumns()), static_cast(getNumRows()), static_cast(getNumSlices())); + return ImageSize(static_cast(getNumColumns()), static_cast(getNumRows()), static_cast(getNumSlices())); } void GeometricInfo::setNumColumns(const VoxelGridDimensionType aValue) { _numberOfColumns = aValue; } const VoxelGridDimensionType GeometricInfo::getNumColumns() const { return _numberOfColumns; } void GeometricInfo::setNumRows(const VoxelGridDimensionType aValue) { _numberOfRows = aValue; } const VoxelGridDimensionType GeometricInfo::getNumRows() const { return _numberOfRows; } void GeometricInfo::setNumSlices(const VoxelGridDimensionType aValue) { _numberOfFrames = aValue; } const VoxelGridDimensionType GeometricInfo::getNumSlices() const { return _numberOfFrames; } bool operator==(const GeometricInfo& gInfo, const GeometricInfo& gInfo1) { return (gInfo.getImagePositionPatient() == gInfo1.getImagePositionPatient() && gInfo.getOrientationMatrix() == gInfo1.getOrientationMatrix() && gInfo.getSpacing() == gInfo1.getSpacing() && gInfo.getNumColumns() == gInfo1.getNumColumns() && gInfo.getNumRows() == gInfo1.getNumRows() && gInfo.getNumSlices() == gInfo1.getNumSlices()); } bool GeometricInfo::equalsAlmost(const GeometricInfo& another, double errorConstantGI /*= 1e-5*/) const { return (getImagePositionPatient().equalsAlmost(another.getImagePositionPatient(), errorConstantGI) && getOrientationMatrix().equalsAlmost(another.getOrientationMatrix(), errorConstantGI) && getSpacing().equalsAlmost(another.getSpacing(), errorConstantGI) && getNumColumns() == another.getNumColumns() && getNumRows() == another.getNumRows() && getNumSlices() == another.getNumSlices()); } - bool GeometricInfo::worldCoordinateToGeometryCoordinate(const WorldCoordinate3D& aWorldCoordinate, - DoubleVoxelGridIndex3D& aIndex) + bool GeometricInfo::worldCoordinateToContinuousIndex(const WorldCoordinate3D& aWorldCoordinate, + ContinuousVoxelGridIndex3D& aIndex) const { WorldCoordinate3D distanceToIP; distanceToIP = aWorldCoordinate - _imagePositionPatient; boost::numeric::ublas::vector result = boost::numeric::ublas::prod( _invertedOrientationMatrix, distanceToIP); boost::numeric::ublas::vector resultS = boost::numeric::ublas::element_div(result, _spacing); - aIndex = DoubleVoxelGridIndex3D(resultS(0), resultS(1), resultS(2)); + aIndex = ContinuousVoxelGridIndex3D(resultS(0), resultS(1), resultS(2)); - //if we convert DoubleVoxelGridIndex3D (double) to VoxelGridIndex3D (unsigned int), we can't find out if it's negative. + //if we convert ContinuousVoxelGridIndex3D (double) to VoxelGridIndex3D (unsigned int), we can't find out if it's negative. //So we have to check before. if (aIndex(0) < -0.5 || aIndex(1) < -0.5 || aIndex(2) < -0.5){ return false; } else { //check if it is inside VoxelGridIndex3D indexInt = VoxelGridIndex3D(GridIndexType(aIndex(0) + 0.5), GridIndexType(aIndex(1) + 0.5), GridIndexType(aIndex(2) + 0.5)); return isInside(indexInt); } } bool GeometricInfo::worldCoordinateToIndex(const WorldCoordinate3D& aWorldCoordinate, VoxelGridIndex3D& aIndex) const { - DoubleVoxelGridIndex3D doubleIndex; - bool inside = worldCoordinateToGeometryCoordinate(aWorldCoordinate, doubleIndex); + ContinuousVoxelGridIndex3D doubleIndex; + bool inside = worldCoordinateToContinuousIndex(aWorldCoordinate, doubleIndex); aIndex = VoxelGridIndex3D(GridIndexType(doubleIndex(0)+0.5), GridIndexType(doubleIndex(1)+0.5), GridIndexType(doubleIndex(2)+0.5)); return inside; } - bool GeometricInfo::geometryCoordinateToWorldCoordinate(const DoubleVoxelGridIndex3D& aIndex, + bool GeometricInfo::continuousIndexToWorldCoordinate(const ContinuousVoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const { boost::numeric::ublas::vector resultS = boost::numeric::ublas::element_prod( aIndex, _spacing); boost::numeric::ublas::vector result = boost::numeric::ublas::prod( _orientationMatrix, resultS); aWorldCoordinate = result + _imagePositionPatient; - //if we convert DoubleVoxelGridIndex3D (double) to VoxelGridIndex3D (unsigned int), we can't find out if it's negative. + //if we convert ContinuousVoxelGridIndex3D (double) to VoxelGridIndex3D (unsigned int), we can't find out if it's negative. //So we have to check before. if (aIndex(0) < -0.5 || aIndex(1) < -0.5 || aIndex(2) < -0.5){ return false; } else { VoxelGridIndex3D indexInt = VoxelGridIndex3D(GridIndexType(aIndex(0) + 0.5), GridIndexType(aIndex(1) + 0.5), GridIndexType(aIndex(2) + 0.5)); return isInside(indexInt); } } bool GeometricInfo::indexToWorldCoordinate(const VoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const { - DoubleVoxelGridIndex3D indexDouble = DoubleVoxelGridIndex3D(aIndex(0), aIndex(1), + ContinuousVoxelGridIndex3D indexDouble = ContinuousVoxelGridIndex3D(aIndex(0), aIndex(1), aIndex(2)); - return geometryCoordinateToWorldCoordinate(indexDouble, aWorldCoordinate); + return continuousIndexToWorldCoordinate(indexDouble, aWorldCoordinate); } bool GeometricInfo::isInside(const VoxelGridIndex3D& aIndex) const { return (aIndex(0) >= 0 && aIndex(1) >= 0 && aIndex(2) >= 0 && aIndex(0) < static_cast(_numberOfColumns) && aIndex(1) < static_cast(_numberOfRows) && aIndex(2) < static_cast(_numberOfFrames)); } bool GeometricInfo::isInside(const WorldCoordinate3D& aWorldCoordinate) const { VoxelGridIndex3D currentIndex; return (worldCoordinateToIndex(aWorldCoordinate, currentIndex)); } const GridSizeType GeometricInfo::getNumberOfVoxels() const { auto nVoxels = static_cast(_numberOfRows * _numberOfColumns * _numberOfFrames); return nVoxels; } bool GeometricInfo::convert(const VoxelGridID& gridID, VoxelGridIndex3D& gridIndex) const { if (validID(gridID)) { gridIndex(0) = gridID % getNumColumns(); VoxelGridID tempID = (gridID - gridIndex.x()) / getNumColumns(); gridIndex(1) = tempID % getNumRows(); gridIndex(2) = (tempID - gridIndex.y()) / getNumRows(); return true; } return false; } bool GeometricInfo::convert(const VoxelGridIndex3D& gridIndex, VoxelGridID& gridID) const { if ((gridIndex.x() >= static_cast(getNumColumns())) || (gridIndex.y() >= static_cast(getNumRows())) || (gridIndex.z() >= static_cast(getNumSlices()))) { return false; } else { gridID = gridIndex.z() * getNumColumns() * getNumRows() + gridIndex.y() * getNumColumns() + gridIndex.x(); return validID(gridID); } } bool GeometricInfo::validID(const VoxelGridID aID) const { return (aID >= 0 && aID < getNumberOfVoxels()); } bool GeometricInfo::validIndex(const VoxelGridIndex3D& aIndex) const { VoxelGridID aID; if (!convert(aIndex, aID)) { return false; } else { return validID(aID); } } std::ostream& operator<<(std::ostream& s, const GeometricInfo& anGeometricInfo) { s << "[ " << anGeometricInfo.getImagePositionPatient() << "; " << anGeometricInfo.getOrientationMatrix() << "; " << anGeometricInfo.getSpacing() << "; " << "; " << anGeometricInfo.getNumColumns() << "; " << anGeometricInfo.getNumRows() << "; " << anGeometricInfo.getNumSlices() << " ]"; return s; } }//end namespace core }//end namespace rttb diff --git a/code/core/rttbGeometricInfo.h b/code/core/rttbGeometricInfo.h index d4ea7e0..26df4fb 100644 --- a/code/core/rttbGeometricInfo.h +++ b/code/core/rttbGeometricInfo.h @@ -1,187 +1,187 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #ifndef __GEOMETRIC_INFO_NEW_H #define __GEOMETRIC_INFO_NEW_H #include #include #include "rttbBaseType.h" #include #include "rttbCommon.h" #include "RTTBCoreExports.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4251) #endif namespace rttb { namespace core { /*! @brief GeometricInfo objects contain all the information required for transformations between voxel grid coordinates and world coordinates. Corresponding converter functions are also available. @note ITK Pixel Indexing used (http://www.itk.org/Doxygen45/html/classitk_1_1Image.html): The Index type reverses the order so that with Index[0] = col, Index[1] = row, Index[2] = slice. */ class RTTBCore_EXPORT GeometricInfo { public: rttbClassMacroNoParent(GeometricInfo) private: WorldCoordinate3D _imagePositionPatient{ 0 }; OrientationMatrix _orientationMatrix{ 0 }; OrientationMatrix _invertedOrientationMatrix{ 0 }; SpacingVectorType3D _spacing{ 0 }; VoxelGridDimensionType _numberOfColumns{0}; VoxelGridDimensionType _numberOfRows{0}; VoxelGridDimensionType _numberOfFrames{0}; /* @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() = default; 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; }; void setImageSize(const ImageSize& aSize); const ImageSize getImageSize() const; void setNumColumns(const VoxelGridDimensionType aValue); const VoxelGridDimensionType getNumColumns() const; void setNumRows(const VoxelGridDimensionType aValue); const VoxelGridDimensionType getNumRows() const; void setNumSlices(const VoxelGridDimensionType aValue); const VoxelGridDimensionType getNumSlices() const; /*! @brief determines equality of two GeometricInfo objects. */ friend bool RTTBCore_EXPORT operator == (const GeometricInfo& gInfo, const GeometricInfo& gInfo1); bool equalsAlmost(const GeometricInfo& another, double errorConstantGI = 1e-5) const; /*! @brief converts world coordinates to voxel grid index. @details the voxels world coordinates are defined by spacing, orientation and imagePositionPatient. (-0.5/-0.5/-0.5) --> (0/0/0) and (0.4999/0.4999/0.4999) --> (0/0/0) define the outer coordinates of a voxel with spacing=1, orientation= x y z (identity matrix) and imagePositionPatient=(0/0/0). @sa WorldCoordinate3D VoxelGridIndex3D @note The conversion of values is done even if the target index is not inside the given voxel grid. @returns false if aWorldCoordinate is outside the voxel grid, true otherwise. */ bool worldCoordinateToIndex(const WorldCoordinate3D& aWorldCoordinate, VoxelGridIndex3D& aIndex) const; /*! @brief converts world coordinates to double geometry coordinate. @details This is needed because of a double precision voxel coordinate system for voxelization. The world coordinate of the image position patient is the center of the first voxel (0.0/0.0/0.0). (-0.5/-0.5/-0.5) --> (-0.5/-0.5/-0.5) and (0.4999/0.4999/0.4999) --> (0.4999/0.4999/0.4999) with spacing=1, orientation= x y z (identity matrix) and imagePositionPatient=(0/0/0). - @sa WorldCoordinate3D, DoubleVoxelGridIndex3D + @sa WorldCoordinate3D, ContinuousVoxelGridIndex3D @note The conversion of values is done even if the target index is not inside the given voxel grid. @returns false if aWorldCoordinate is outside the voxel grid, true otherwise. */ - bool worldCoordinateToGeometryCoordinate(const WorldCoordinate3D& aWorldCoordinate, - DoubleVoxelGridIndex3D& aIndex) const; + bool worldCoordinateToContinuousIndex(const WorldCoordinate3D& aWorldCoordinate, + ContinuousVoxelGridIndex3D& aIndex) const; /*! @brief converts double geometry coordinate to world coordinates. @details This is needed because of a double precision voxel coordinate system for voxelization. The world coordinate of the image position patient is the center of the first voxel (0.0/0.0/0.0). (-0.5/-0.5/-0.5) --> (-0.5/-0.5/-0.5) and (5.5/3.2/1.0) --> (5.5/3.2/1.0) with spacing=1, orientation= x y z (identity matrix) and imagePositionPatient=(0/0/0). - @sa DoubleVoxelGridIndex3D, WorldCoordinate3D + @sa ContinuousVoxelGridIndex3D, WorldCoordinate3D @note The conversion of values is done even if the target index is not inside the given voxel grid. @returns false if aWorldCoordinate is outside the voxel grid, true otherwise. */ - bool geometryCoordinateToWorldCoordinate(const DoubleVoxelGridIndex3D& aIndex, + bool continuousIndexToWorldCoordinate(const ContinuousVoxelGridIndex3D& aIndex, WorldCoordinate3D& aWorldCoordinate) const; /*! @brief convert voxel grid index to world coordinates @details The world coordinate of the image position patient (center of the first voxel) is the center of the first voxel (0.0/0.0/0.0) (0/0/0) --> (0.0/0.0/0.0) and (1/1/2) --> (1.0/1.0/2.0) with spacing=1, orientation= x y z (identity matrix) and imagePositionPatient=(0/0/0). Thus, the center of the voxel is taken and converted. @sa VoxelGridIndex3D, WorldCoordinate3D @note The conversion of values is done even if the target index is not inside the given voxel grid. @returns false if aWorldCoordinate is outside the voxel grid, true otherwise. */ 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; 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); }; } } #ifdef _MSC_VER #pragma warning(pop) #endif #endif diff --git a/code/io/itk/rttbImageReader.tpp b/code/io/itk/rttbImageReader.tpp index 7a1f9ee..c145076 100644 --- a/code/io/itk/rttbImageReader.tpp +++ b/code/io/itk/rttbImageReader.tpp @@ -1,524 +1,524 @@ // ----------------------------------------------------------------------- // 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. // //------------------------------------------------------------------------ #ifndef __RTTB_IMAGE_READER_TPP #define __RTTB_IMAGE_READER_TPP #include "rttbImageReader.h" #include "rttbFileDispatch.h" #include "RTToolboxConfigure.h" #ifdef RTTB_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER #undef ITK_IO_FACTORY_REGISTER_MANAGER #endif #include "itkImageFileReader.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() { using ImageReaderType = ::itk::ImageFileReader< InputImageType >; 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(); auto itr = fromArray->begin(); auto end = fromArray->end(); while (itr != end) { toArray.push_back(*(*itr)); ++itr; } }; template typename ::itk::ImageSource::InputImageType>::Pointer ImageReader:: prepareNumericSource() const { //mumeric series image reader using SeriesReaderType = ::itk::ImageSeriesReader; using NamesType = ::itk::NumericSeriesFileNames; 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(); }; template typename ::itk::ImageSource::InputImageType>::Pointer ImageReader:: prepareGDCMSource() const { core::FileDispatch dispatch(_fileName); FileNameString dir = dispatch.getPath(); FileNameString strippedFileName = dispatch.getFullName(); using NamesGeneratorType = ::itk::GDCMSeriesFileNames; 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; + std::cout << "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; + std::cout << "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: " << + std::cout << "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; + std::cout << "Found right series!" << std::endl; seriesUIDs.clear(); break; } } } } using SeriesReaderType = ::itk::ImageSeriesReader; 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) using ImageReaderType = ::itk::ImageFileReader< InputImageType >; 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(); //Convert to lowercase for (char & spos : sTemp) { 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(); using ImageReaderType = ::itk::ImageFileReader< InputImageType >; using ImageSeriesReaderType = ::itk::ImageSeriesReader; auto* pFileReader = dynamic_cast(spReader.GetPointer()); auto* 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() { 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() = default; 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/masks/CMakeLists.txt b/code/masks/CMakeLists.txt index 9267d7b..a107409 100644 --- a/code/masks/CMakeLists.txt +++ b/code/masks/CMakeLists.txt @@ -1,8 +1,7 @@ MESSAGE (STATUS "processing RTToolbox boost mask") -SET(RTTB_Boost_ADDITIONAL_COMPONENT thread) RTTB_CREATE_MODULE(RTTBMask DEPENDS RTTBCore PACKAGE_DEPENDS BoostBinaries) IF (CMAKE_COMPILER_IS_GNUCC) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals") ENDIF() diff --git a/code/masks/rttbBoostMask.cpp b/code/masks/rttbBoostMask.cpp index 63c13ae..696ddc5 100644 --- a/code/masks/rttbBoostMask.cpp +++ b/code/masks/rttbBoostMask.cpp @@ -1,558 +1,558 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include #include #include #include #include #include "rttbBoostMask.h" #include "rttbNullPointerException.h" #include "rttbInvalidParameterException.h" #include "rttbBoostMaskGenerateMaskVoxelListThread.h" #include "rttbBoostMaskVoxelizationThread.h" namespace rttb { namespace masks { namespace boost { BoostMask::BoostMask(core::GeometricInfo::Pointer aDoseGeoInfo, core::Structure::Pointer aStructure, bool strict, unsigned int numberOfThreads) : _geometricInfo(aDoseGeoInfo), _structure(aStructure), _strict(strict), _numberOfThreads(numberOfThreads), _voxelizationThickness(0.0), _voxelInStructure(::boost::make_shared()) { _isUpToDate = false; if (_geometricInfo == nullptr) { throw rttb::core::NullPointerException("Error: Geometric info is nullptr!"); } else if (_structure == nullptr) { throw rttb::core::NullPointerException("Error: Structure is nullptr!"); } if (_numberOfThreads == 0) { _numberOfThreads = ::boost::thread::hardware_concurrency(); if (_numberOfThreads == 0) { throw rttb::core::InvalidParameterException("Error: detection of the number of hardware threads is not possible. Please specify number of threads for voxelization explicitly as parameter in BoostMask."); } } } BoostMask::MaskVoxelListPointer BoostMask::getRelevantVoxelVector() { if (!_isUpToDate) { calcMask(); } return _voxelInStructure; } void BoostMask::calcMask() { preprocessing(); voxelization(); generateMaskVoxelList(); _isUpToDate = true; } void BoostMask::preprocessing() { rttb::PolygonSequenceType polygonSequence = _structure->getStructureVector(); //Convert world coordinate polygons to the polygons with geometry coordinate rttb::PolygonSequenceType geometryCoordinatePolygonVector; rttb::PolygonSequenceType::iterator it; - rttb::DoubleVoxelGridIndex3D globalMaxGridIndex(std::numeric_limits::min(), + rttb::ContinuousVoxelGridIndex3D globalMaxGridIndex(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); - rttb::DoubleVoxelGridIndex3D globalMinGridIndex(_geometricInfo->getNumColumns(), + rttb::ContinuousVoxelGridIndex3D globalMinGridIndex(_geometricInfo->getNumColumns(), _geometricInfo->getNumRows(), 0); for (it = polygonSequence.begin(); it != polygonSequence.end(); ++it) { PolygonType rttbPolygon = *it; PolygonType geometryCoordinatePolygon; //1. convert polygon to geometry coordinate polygons //2. calculate global min/max //3. check if polygon is planar if (!preprocessingPolygon(rttbPolygon, geometryCoordinatePolygon, globalMinGridIndex, globalMaxGridIndex, errorConstant)) { throw rttb::core::Exception("TiltedMaskPlaneException"); } geometryCoordinatePolygonVector.push_back(geometryCoordinatePolygon); } rttb::VoxelGridIndex3D minIndex = VoxelGridIndex3D(GridIndexType(globalMinGridIndex(0) ), GridIndexType(globalMinGridIndex(1) ), GridIndexType(globalMinGridIndex(2) )); rttb::VoxelGridIndex3D maxIndex = VoxelGridIndex3D(GridIndexType(globalMaxGridIndex(0) ), GridIndexType(globalMaxGridIndex(1) ), GridIndexType(globalMaxGridIndex(2) )); _globalBoundingBox.push_back(minIndex); _globalBoundingBox.push_back(maxIndex); //convert rttb polygon sequence to a map of z index and a vector of boost ring 2d (without holes) _ringMap = convertRTTBPolygonSequenceToBoostRingMap(geometryCoordinatePolygonVector); } void BoostMask::voxelization() { if (_globalBoundingBox.size() < 2) { throw rttb::core::InvalidParameterException("Bounding box calculation failed! "); } BoostRingMap::iterator itMap; size_t mapSizeInAThread = _ringMap.size() / _numberOfThreads; unsigned int count = 0; unsigned int countThread = 0; BoostPolygonMap polygonMap; std::vector polygonMapVector; //check donut and convert to a map of z index and a vector of boost polygon 2d (with or without holes) for (itMap = _ringMap.begin(); itMap != _ringMap.end(); ++itMap) { //the vector of all boost 2d polygons with the same z grid index(donut polygon is accepted). BoostPolygonVector polygonVector = checkDonutAndConvert((*itMap).second); if (count == mapSizeInAThread && countThread < (_numberOfThreads - 1)) { polygonMapVector.push_back(polygonMap); polygonMap.clear(); count = 0; countThread++; } polygonMap.insert(std::pair((*itMap).first, polygonVector)); count++; } _voxelizationMap = ::boost::make_shared >(); polygonMapVector.push_back(polygonMap); //insert the last one //generate voxelization map, multi-threading ::boost::thread_group threads; auto aMutex = ::boost::make_shared<::boost::shared_mutex>(); for (const auto & i : polygonMapVector) { BoostMaskVoxelizationThread t(i, _globalBoundingBox, _voxelizationMap, aMutex, _strict); threads.create_thread(t); } threads.join_all(); } void BoostMask::generateMaskVoxelList() { if (_globalBoundingBox.size() < 2) { throw rttb::core::InvalidParameterException("Bounding box calculation failed! "); } //check homogeneous of the voxelization plane (the contours plane) if (!calcVoxelizationThickness(_voxelizationThickness)) { throw rttb::core::InvalidParameterException("Error: The contour plane should be homogeneous!"); } ::boost::thread_group threads; auto aMutex = ::boost::make_shared<::boost::shared_mutex>(); unsigned int sliceNumberInAThread = _geometricInfo->getNumSlices() / _numberOfThreads; //generate mask voxel list, multi-threading for (unsigned int i = 0; i < _numberOfThreads; ++i) { unsigned int beginSlice = i * sliceNumberInAThread; unsigned int endSlice; if (i < _numberOfThreads - 1) { endSlice = (i + 1) * sliceNumberInAThread; } else { endSlice = _geometricInfo->getNumSlices(); } BoostMaskGenerateMaskVoxelListThread t(_globalBoundingBox, _geometricInfo, _voxelizationMap, _voxelizationThickness, beginSlice, endSlice, _voxelInStructure, _strict, aMutex); threads.create_thread(t); } threads.join_all(); } bool BoostMask::preprocessingPolygon(const rttb::PolygonType& aRTTBPolygon, - rttb::PolygonType& geometryCoordinatePolygon, rttb::DoubleVoxelGridIndex3D& minimum, - rttb::DoubleVoxelGridIndex3D& maximum, double aErrorConstant) const + rttb::PolygonType& geometryCoordinatePolygon, rttb::ContinuousVoxelGridIndex3D& minimum, + rttb::ContinuousVoxelGridIndex3D& maximum, double aErrorConstant) const { double minZ = _geometricInfo->getNumSlices(); double maxZ = 0.0; for (auto worldCoordinatePoint : aRTTBPolygon) { //convert to geometry coordinate polygon - rttb::DoubleVoxelGridIndex3D geometryCoordinatePoint; - _geometricInfo->worldCoordinateToGeometryCoordinate(worldCoordinatePoint, geometryCoordinatePoint); + rttb::ContinuousVoxelGridIndex3D geometryCoordinatePoint; + _geometricInfo->worldCoordinateToContinuousIndex(worldCoordinatePoint, geometryCoordinatePoint); geometryCoordinatePolygon.push_back(geometryCoordinatePoint); //calculate the current global min/max //min and max for x if (geometryCoordinatePoint(0) < minimum(0)) { minimum(0) = geometryCoordinatePoint(0); } if (geometryCoordinatePoint(0) > maximum(0)) { maximum(0) = geometryCoordinatePoint(0); } //min and max for y if (geometryCoordinatePoint(1) < minimum(1)) { minimum(1) = geometryCoordinatePoint(1); } if (geometryCoordinatePoint(1) > maximum(1)) { maximum(1) = geometryCoordinatePoint(1); } //min and max for z if (geometryCoordinatePoint(2) < minimum(2)) { minimum(2) = geometryCoordinatePoint(2); } if (geometryCoordinatePoint(2) > maximum(2)) { maximum(2) = geometryCoordinatePoint(2); } //check planar if (geometryCoordinatePoint(2) < minZ) { minZ = geometryCoordinatePoint(2); } if (geometryCoordinatePoint(2) > maxZ) { maxZ = geometryCoordinatePoint(2); } } return (std::abs(maxZ - minZ) <= aErrorConstant); } BoostMask::BoostRing2D BoostMask::convertRTTBPolygonToBoostRing(const rttb::PolygonType& aRTTBPolygon) const { BoostMask::BoostRing2D polygon2D; BoostPoint2D firstPoint; for (unsigned int i = 0; i < aRTTBPolygon.size(); i++) { rttb::WorldCoordinate3D rttbPoint = aRTTBPolygon.at(i); BoostPoint2D boostPoint(rttbPoint[0], rttbPoint[1]); if (i == 0) { firstPoint = boostPoint; } ::boost::geometry::append(polygon2D, boostPoint); } ::boost::geometry::append(polygon2D, firstPoint); return polygon2D; } BoostMask::BoostRingMap BoostMask::convertRTTBPolygonSequenceToBoostRingMap( const rttb::PolygonSequenceType& aRTTBPolygonVector) const { rttb::PolygonSequenceType::const_iterator it; BoostMask::BoostRingMap aRingMap; for (it = aRTTBPolygonVector.begin(); it != aRTTBPolygonVector.end(); ++it) { rttb::PolygonType rttbPolygon = *it; double zIndex = rttbPolygon.at(0)[2];//get the first z index of the polygon bool isFirstZ = true; if (!aRingMap.empty()) { auto findIt = findNearestKey(aRingMap, zIndex, errorConstant); //if the z index is found (same slice), add the polygon to vector if (findIt != aRingMap.end()) { //BoostRingVector ringVector = ; (*findIt).second.push_back(convertRTTBPolygonToBoostRing(rttbPolygon)); isFirstZ = false; } } //if it is the first z index in the map, insert vector with the polygon if (isFirstZ) { BoostRingVector ringVector; ringVector.push_back(convertRTTBPolygonToBoostRing(rttbPolygon)); aRingMap.insert(std::pair(zIndex, ringVector)); } } return aRingMap; } BoostMask::BoostRingMap::iterator BoostMask::findNearestKey(BoostMask::BoostRingMap& aBoostRingMap, double aIndex, double aErrorConstant) const { auto find = aBoostRingMap.find(aIndex); //if find a key equivalent to aIndex, found if (find != aBoostRingMap.end()) { return find; } else { auto lowerBound = aBoostRingMap.lower_bound(aIndex); //if all keys go before aIndex, check the last key if (lowerBound == aBoostRingMap.end()) { lowerBound = --aBoostRingMap.end(); } //if the lower bound very close to aIndex, found if (std::abs((*lowerBound).first - aIndex) <= aErrorConstant) { return lowerBound; } else { //if the lower bound is the beginning, not found if (lowerBound == aBoostRingMap.begin()) { return aBoostRingMap.end(); } else { auto lowerBound1 = --lowerBound;//the key before the lower bound //if the key before the lower bound very close to a Index, found if (std::abs((*lowerBound1).first - aIndex) <= aErrorConstant) { return lowerBound1; } //else, not found else { return aBoostRingMap.end(); } } } } } BoostMask::BoostPolygonVector BoostMask::checkDonutAndConvert(const BoostMask::BoostRingVector& aRingVector) const { //check donut BoostMask::BoostRingVector::const_iterator it1; BoostMask::BoostRingVector::const_iterator it2; BoostMask::BoostPolygonVector boostPolygonVector; std::vector donutIndexVector;//store the outer and inner ring index BoostMask::BoostPolygonVector donutVector;//store new generated donut polygon //Get donut index and donut polygon unsigned int index1 = 0; for (it1 = aRingVector.begin(); it1 != aRingVector.end(); ++it1, index1++) { bool it1IsDonut = false; //check if the ring is already determined as a donut for (unsigned int i : donutIndexVector) { if (i == index1) { it1IsDonut = true; break; } } //if not jet, check now if (!it1IsDonut) { bool it2IsDonut = false; unsigned int index2 = 0; for (it2 = aRingVector.begin(); it2 != aRingVector.end(); ++it2, index2++) { if (it2 != it1) { BoostMask::BoostPolygon2D polygon2D; if (::boost::geometry::within(*it1, *it2)) { ::boost::geometry::append(polygon2D, *it2);//append an outer ring to the polygon ::boost::geometry::interior_rings(polygon2D).resize(1);//create an interior ring ::boost::geometry::append(polygon2D, *it1, 0);//append a ring to the interior ring it2IsDonut = true; } //if donut else if (::boost::geometry::within(*it2, *it1)) { ::boost::geometry::append(polygon2D, *it1);//append an outer ring to the polygon ::boost::geometry::interior_rings(polygon2D).resize(1);//create an interior ring ::boost::geometry::append(polygon2D, *it2, 0);//append a ring to the interior ring it2IsDonut = true; } if (it2IsDonut) { donutIndexVector.push_back(index1); donutIndexVector.push_back(index2); donutVector.push_back(polygon2D);//store donut polygon break;//Only store the first donut! } } } } } //Store no donut polygon to boostPolygonVector index1 = 0; for (it1 = aRingVector.begin(); it1 != aRingVector.end(); ++it1, index1++) { bool it1IsDonut = false; //check if the ring is the outer or inner of a donut for (unsigned int i : donutIndexVector) { if (i == index1) { it1IsDonut = true; break; } } if (!it1IsDonut) { BoostMask::BoostPolygon2D polygon2D; ::boost::geometry::append(polygon2D, *it1); boostPolygonVector.push_back(polygon2D);//insert the ring, which is not a part of donut } } //Append donut polygon to boostPolygonVector BoostMask::BoostPolygonVector::iterator itDonut; for (itDonut = donutVector.begin(); itDonut != donutVector.end(); ++itDonut) { boostPolygonVector.push_back(*itDonut);//append donuts } return boostPolygonVector; } bool BoostMask::calcVoxelizationThickness(double& aThickness) const { if (_voxelizationMap->size() <= 1) { aThickness = 1; return true; } double thickness = 0; auto it = _voxelizationMap->cbegin(); auto it2 = ++_voxelizationMap->cbegin(); for (; it != _voxelizationMap->cend() && it2 != _voxelizationMap->cend(); ++it, ++it2) { if (thickness == 0) { thickness = it2->first - it->first; } else { double curThickness = it2->first - it->first; //if not homogeneous (leave out double imprecisions), return false if (std::abs(thickness-curThickness)>errorConstant) { //return false; std::cout << "Two polygons are far from each other?" << std::endl; } } } if (thickness != 0) { aThickness = thickness; } else { aThickness = 1; } return true; } } } } diff --git a/code/masks/rttbBoostMask.h b/code/masks/rttbBoostMask.h index 1db206a..18b009c 100644 --- a/code/masks/rttbBoostMask.h +++ b/code/masks/rttbBoostMask.h @@ -1,195 +1,195 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #ifndef __BOOST_MASK_R_H #define __BOOST_MASK_R_H #include "rttbBaseType.h" #include "rttbStructure.h" #include "rttbGeometricInfo.h" #include "rttbMaskAccessorInterface.h" #include #include #include #include namespace rttb { namespace masks { namespace boost { /*! @class BoostMask * @brief Implementation of voxelization using boost::geometry. * @attention If "strict" is set to true, an exception will be thrown when the given structure has self intersection. * (A structure without self intersection means all contours of the structure have no self intersection, and * the polygons on the same slice have no intersection between each other, unless the case of a donut. A donut is accepted.) * If "strict" is set to false, debug information will be displayed when the given structure has self intersection. Self intersections will be ignored * and the mask will be calculated, however, it may cause errors in the mask results. */ class BoostMask { public: using MaskVoxelList = core::MaskAccessorInterface::MaskVoxelList; using MaskVoxelListPointer = core::MaskAccessorInterface::MaskVoxelListPointer; /*! @brief Constructor * @exception rttb::core::NullPointerException thrown if aDoseGeoInfo or aStructure is nullptr * @param aDoseGeoInfo the GeometricInfo * @param aStructure the structure set * @param strict indicates whether to allow self intersection in the structure. If it is set to true, an exception will be thrown when the given structure has self intersection. * @param numberOfThreads number of threads used for voxelization. default value 0 means automatic detection, using the number of Hardware thread/cores * @exception InvalidParameterException thrown if strict is true and the structure has self intersections */ BoostMask(core::GeometricInfo::Pointer aDoseGeoInfo, core::Structure::Pointer aStructure, bool strict = true, unsigned int numberOfThreads = 0); /*! @brief Generate mask and return the voxels in the mask * @exception rttb::core::InvalidParameterException thrown if the structure has self intersections */ MaskVoxelListPointer getRelevantVoxelVector(); private: using BoostPoint2D = ::boost::geometry::model::d2::point_xy; using BoostPolygon2D = ::boost::geometry::model::polygon< ::boost::geometry::model::d2::point_xy >; using BoostRing2D = ::boost::geometry::model::ring< ::boost::geometry::model::d2::point_xy >; using BoostRingVector = std::vector;//polygon without holes using BoostPolygonVector = std::vector;//polygon with or without holes using VoxelIndexVector = std::vector; typedef std::map BoostPolygonMap;//map of the z index with the vector of boost 2d polygon typedef std::map BoostRingMap;//map of the z index with the vector of boost 2d ring typedef ::boost::multi_array BoostArray2D; using BoostArray2DPointer = ::boost::shared_ptr; typedef ::boost::shared_ptr > BoostArrayMapPointer; core::GeometricInfo::Pointer _geometricInfo; core::Structure::Pointer _structure; bool _strict; /*! @brief The number of threads */ unsigned int _numberOfThreads; //@brief The thickness of the voxelization plane (the contour plane), in double dose grid index //@details for example, the first contour has the double grid index 0.1, the second 0.3, the third 0.5, then the thickness is 0.2 double _voxelizationThickness; //@brief vector of the MaskVoxel inside the structure MaskVoxelListPointer _voxelInStructure; /*! @brief The map of z index and a vector of boost ring 2d (without holes) * @details Key: the double z grid index * Value: the vector of boost ring 2d (without holes) */ BoostRingMap _ringMap; /*! @brief The min and max index of the global bounding box. * @details The first index has the minimum for x/y/z of the global bounding box. * The second index has the maximum for x/y/z of the global bounding index. */ VoxelIndexVector _globalBoundingBox; /*! @brief The voxelization map * @details key: the converted double z grid index of a contour plane * value: the 2d mask, array[i][j] = the mask value of the position (i,j) in the global bounding box, * i: 0 - (_globalBoundingBoxSize0-1), j: 0 - (_globalBoundingBoxSize1-1) */ BoostArrayMapPointer _voxelizationMap; /*! @brief If the mask is up to date */ bool _isUpToDate; /*! @brief Voxelization and generate mask */ void calcMask(); /*! @brief The preprocessing step, wich consists of the following logic and Sub setps: * @details For all contours in a struct: * 1) Transfer the contour polygons into boost::geometry structures * 1a) Convert the contur points from world coordinates into geometry coordinates. * 1b) get min and max for x/y/z of a contour * 2) Tilt check: if difference of z_min and z_max is larger then a tolerance value -> there is a tilt. Throw rttb::TiltedMaskPlaneException. * 3) Get struct-bounding-box: get x_min_struct, y_min_struct, x_max_struct, y_max_struct to define the bounding box that containes all contours of a struct in x-y-dimensions. */ void preprocessing(); /*! @brief The voxelization step, which computes the voxelization planes (in x/y) for all contours of an struct. * @details For each contour (that is in the z-Range of the reference geometry) of the struct: * 1) Allocate result array (voxelization plane) based on the bounding box (see Preprocessing Step 3) * 2) Generate voxelization plane for the contour (based on the x-y-raster of the reference geometry). * 3) Add result Array (key is the z-Value of the contour) */ void voxelization(); /*! @brief mask voxel Generation step which transfers the voxelization planes into the (z-)geometry of the reference geometry. * @details It consists of following Sub steps : * For all "slices" in the reference geometry : * 1) generate weight vector for all voxelization planes for a given z - value of a slice * Iterate over the bounding box of a struct.For each voxel : * 2) Compute weighted sum of all voxelization planes(use weight vector, step 1) * 2a) If sum > 0 : Add mask voxel for the current x / y(inner Loop) and z value(outer Loop). * 3) return mask voxel list. */ void generateMaskVoxelList(); /*! @brief Convert the rttb polygon with world coordinate to the rttb polygon with double geometry coordinate, calculate the current min/max * and check if the polygon is planar * @param minimum the current global minimum * @param maximum the current global maximum * @return Return true if the polygon is planar, which means that the minimal and maximal z-coordinate of the polygon is not larger than a error constant */ bool preprocessingPolygon(const rttb::PolygonType& aRTTBPolygon, - rttb::PolygonType& geometryCoordinatePolygon, rttb::DoubleVoxelGridIndex3D& minimum, - rttb::DoubleVoxelGridIndex3D& maximum, double aErrorConstant) const; + rttb::PolygonType& geometryCoordinatePolygon, rttb::ContinuousVoxelGridIndex3D& minimum, + rttb::ContinuousVoxelGridIndex3D& maximum, double aErrorConstant) const; /*! @brief Convert a rttb 3d polygon to a 2d boost ring*/ BoostRing2D convertRTTBPolygonToBoostRing(const rttb::PolygonType& aRTTBPolygon) const; /*! @brief Convert a rttb 3d polygon to a map of z index with a vector of boost 2d ring, because of tilt check use the first z index of the polygon as the map key*/ BoostRingMap convertRTTBPolygonSequenceToBoostRingMap(const rttb::PolygonSequenceType& aRTTBPolygonVector) const; /*! @brief Find the key with error constant to aIndex * @pre aBoostRingMap should not be empty * @return Return aBoostRingMap.end() if the key is not found */ BoostMask::BoostRingMap::iterator findNearestKey(BoostMask::BoostRingMap& aBoostRingMap, double aIndex, double aErrorConstant) const; /*! @brief If 2 rings in the vector build a donut, convert the 2 rings to a donut polygon, other rings unchanged*/ BoostPolygonVector checkDonutAndConvert(const BoostRingVector& aRingVector) const; /*! @brief Calculate the voxelization thickness. Return false, if the voxelization plane is not homogeneous */ bool calcVoxelizationThickness(double& aThickness) const; }; } } } #endif diff --git a/testing/apps/DoseAcc/DoseAccInvalidParametersTest.cpp b/testing/apps/DoseAcc/DoseAccInvalidParametersTest.cpp index d67f07f..6e77be6 100644 --- a/testing/apps/DoseAcc/DoseAccInvalidParametersTest.cpp +++ b/testing/apps/DoseAcc/DoseAccInvalidParametersTest.cpp @@ -1,97 +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. // //------------------------------------------------------------------------ #include #include "litCheckMacros.h" #include "boost/filesystem.hpp" namespace rttb { namespace testing { //path to the current running directory. DoseTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int DoseAccInvalidParametersTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string doseAccExecutable; if (argc > 1) { doseAccExecutable = argv[1]; } boost::filesystem::path callingPath(_callingAppPath); - std::string doseAccExeWithPath = callingPath.parent_path().string() + "/" + doseAccExecutable; + auto doseAccExecutablePath = callingPath.parent_path() / doseAccExecutable; + std::string doseAccExeWithPath = doseAccExecutablePath.string(); //call with too few parameters std::string toofewParametersCommand = doseAccExeWithPath; toofewParametersCommand += " test"; std::cout << "Command line call: " + toofewParametersCommand << std::endl; CHECK_EQUAL(system(toofewParametersCommand.c_str()) != 0, true); toofewParametersCommand = doseAccExeWithPath; toofewParametersCommand += " test test"; std::cout << "Command line call: " + toofewParametersCommand << std::endl; CHECK_EQUAL(system(toofewParametersCommand.c_str()) != 0, true); toofewParametersCommand = doseAccExeWithPath; toofewParametersCommand += " -d test"; toofewParametersCommand += " -e test"; std::cout << "Command line call: " + toofewParametersCommand << std::endl; CHECK_EQUAL(system(toofewParametersCommand.c_str()) != 0, true); std::string minimalCLI = doseAccExeWithPath + " test test test "; //call with invalid interpolator std::string invalidInterpolatorOption = minimalCLI; invalidInterpolatorOption += "-i wrongInterpolator"; std::cout << "Command line call: " + invalidInterpolatorOption << std::endl; CHECK_EQUAL(system(invalidInterpolatorOption.c_str()) != 0, true); //call with invalid operator std::string invalidOperatorOption = minimalCLI; invalidOperatorOption += "-p -"; std::cout << "Command line call: " + invalidOperatorOption << std::endl; CHECK_EQUAL(system(invalidOperatorOption.c_str()) != 0, true); //call with operator* and weight std::string invalidOperatorMultWithWeightOption = minimalCLI; invalidOperatorMultWithWeightOption += "-p * --weight1 2.0"; std::cout << "Command line call: " + invalidOperatorMultWithWeightOption << std::endl; CHECK_EQUAL(system(invalidOperatorMultWithWeightOption.c_str()) != 0, true); //call with invalid dose1 load option std::string invalidDose1LoadOption = minimalCLI; invalidDose1LoadOption += "-t invalidLoadStyleOption"; std::cout << "Command line call: " + invalidDose1LoadOption << std::endl; CHECK_EQUAL(system(invalidDose1LoadOption.c_str()) != 0, true); //call with invalid dose2 load option std::string invalidDose2LoadOption = minimalCLI; invalidDose2LoadOption += "-u invalidLoadStyleOption"; std::cout << "Command line call: " + invalidDose2LoadOption << std::endl; CHECK_EQUAL(system(invalidDose2LoadOption.c_str()) != 0, true); RETURN_AND_REPORT_TEST_SUCCESS; } } //namespace testing } //namespace rttb diff --git a/testing/apps/DoseAcc/DoseAccNeutralWeightTest.cpp b/testing/apps/DoseAcc/DoseAccNeutralWeightTest.cpp index b08592d..a816855 100644 --- a/testing/apps/DoseAcc/DoseAccNeutralWeightTest.cpp +++ b/testing/apps/DoseAcc/DoseAccNeutralWeightTest.cpp @@ -1,102 +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. // //------------------------------------------------------------------------ #include "litCheckMacros.h" #include "litImageTester.h" #include "boost/filesystem.hpp" #include "itkImage.h" #include "rttbITKIOHelper.h" namespace rttb { namespace testing { //path to the current running directory. DoseTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int DoseAccNeutralWeightTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string doseAccExecutable; std::string doseFilename; std::string referenceFilename; std::string reference2Filename; boost::filesystem::path callingPath(_callingAppPath); if (argc > 4) { doseAccExecutable = argv[1]; doseFilename = argv[2]; referenceFilename = argv[3]; reference2Filename = argv[4]; } - std::string doseAccExeWithPath = callingPath.parent_path().string() + "/" + doseAccExecutable; + auto doseAccExecutablePath = callingPath.parent_path() / doseAccExecutable; + std::string doseAccExeWithPath = doseAccExecutablePath.string(); const std::string defaultOutputAddFilename = "doseAccAddOutput.nrrd"; const std::string defaultOutputMultFilename = "doseAccMultOutput.nrrd"; std::string baseCommand = doseAccExeWithPath; baseCommand += " -d \"" + doseFilename + "\""; baseCommand += " -e \"" + doseFilename + "\""; std::string defaultDoseAccAddCommand = baseCommand + " -o " + defaultOutputAddFilename + " --weight1 0.0"; std::cout << "Command line call: " + defaultDoseAccAddCommand << std::endl; CHECK_EQUAL(system(defaultDoseAccAddCommand.c_str()), 0); CHECK_EQUAL(boost::filesystem::exists(defaultOutputAddFilename), true); std::string defaultDoseAccMultCommand = baseCommand + " -o " + defaultOutputMultFilename + " --operator \"*\""; std::cout << "Command line call: " + defaultDoseAccMultCommand << std::endl; CHECK_EQUAL(system(defaultDoseAccMultCommand.c_str()), 0); CHECK_EQUAL(boost::filesystem::exists(defaultOutputMultFilename), true); // Check result against reference typedef ::itk::Image TestImageType; TestImageType::Pointer referenceAddImage = io::itk::readITKDoubleImage(referenceFilename); TestImageType::Pointer outputAddImage = io::itk::readITKDoubleImage(defaultOutputAddFilename); lit::ImageTester tester; tester.setExpectedImage(referenceAddImage); tester.setActualImage(outputAddImage); tester.setCheckThreshold(1e-5); CHECK_TESTER(tester); TestImageType::Pointer referenceMultImage = io::itk::readITKDoubleImage(reference2Filename); TestImageType::Pointer outputMultImage = io::itk::readITKDoubleImage(defaultOutputMultFilename); tester.setExpectedImage(referenceMultImage); tester.setActualImage(outputMultImage); tester.setCheckThreshold(1e-5); CHECK_TESTER(tester); CHECK_EQUAL(std::remove(defaultOutputAddFilename.c_str()), 0); CHECK_EQUAL(std::remove(defaultOutputMultFilename.c_str()), 0); RETURN_AND_REPORT_TEST_SUCCESS; } } //namespace testing } //namespace rttb diff --git a/testing/apps/DoseAcc/DoseAccSimpleTest.cpp b/testing/apps/DoseAcc/DoseAccSimpleTest.cpp index 6fb8af3..0d42af1 100644 --- a/testing/apps/DoseAcc/DoseAccSimpleTest.cpp +++ b/testing/apps/DoseAcc/DoseAccSimpleTest.cpp @@ -1,86 +1,87 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include "litCheckMacros.h" #include "litImageTester.h" #include "boost/filesystem.hpp" #include "itkImage.h" #include "rttbITKIOHelper.h" namespace rttb { namespace testing { //path to the current running directory. DoseTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int DoseAccSimpleTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string doseAccExecutable; std::string dose1Filename; std::string dose2Filename; std::string referenceFilename; boost::filesystem::path callingPath(_callingAppPath); if (argc > 4) { doseAccExecutable = argv[1]; dose1Filename = argv[2]; dose2Filename = argv[3]; referenceFilename = argv[4]; } - std::string doseAccExeWithPath = callingPath.parent_path().string() + "/" + doseAccExecutable; + auto doseAccExecutablePath = callingPath.parent_path() / doseAccExecutable; + std::string doseAccExeWithPath = doseAccExecutablePath.string(); std::string defaultOutputFilename = "doseAccOutput.nrrd"; std::string baseCommand = doseAccExeWithPath; baseCommand += " -d \"" + dose1Filename + "\""; baseCommand += " -e \"" + dose2Filename + "\""; baseCommand += " -o " + defaultOutputFilename; std::string defaultDoseAccCommand = baseCommand + " --weight1 2.0"; std::cout << "Command line call: " + defaultDoseAccCommand << std::endl; CHECK_EQUAL(system(defaultDoseAccCommand.c_str()), 0); CHECK_EQUAL(boost::filesystem::exists(defaultOutputFilename), true); // Check result against reference typedef ::itk::Image TestImageType; TestImageType::Pointer referenceImage = io::itk::readITKDoubleImage(referenceFilename); TestImageType::Pointer outputImage = io::itk::readITKDoubleImage(defaultOutputFilename); lit::ImageTester tester; tester.setExpectedImage(referenceImage); tester.setActualImage(outputImage); tester.setCheckThreshold(0.0); CHECK_TESTER(tester); CHECK_EQUAL(std::remove(defaultOutputFilename.c_str()), 0); RETURN_AND_REPORT_TEST_SUCCESS; } } //namespace testing } //namespace rttb diff --git a/testing/apps/VoxelizerTool/VoxelizerToolDifferentCommandsTest.cpp b/testing/apps/VoxelizerTool/VoxelizerToolDifferentCommandsTest.cpp index c7f3cc3..61a0a6a 100644 --- a/testing/apps/VoxelizerTool/VoxelizerToolDifferentCommandsTest.cpp +++ b/testing/apps/VoxelizerTool/VoxelizerToolDifferentCommandsTest.cpp @@ -1,130 +1,150 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include "litCheckMacros.h" #include #include #include "itkImage.h" #include "itkImageFileReader.h" #include /*! @brief VoxelizerToolTest3. Test the output, multipleStructs and the booleanVoxelization parameter. */ namespace rttb { namespace testing { //path to the current running directory. VoxelizerTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int VoxelizerToolDifferentCommandsTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; - typedef itk::Image< double, 3 > ImageType; - typedef itk::ImageFileReader ReaderType; + typedef itk::Image< double, 3 > ImageType; + typedef itk::ImageFileReader ReaderType; std::string voxelizerToolExe; std::string tempDirectory; std::string structFile; std::string referenceFile; if (argc > 4) { voxelizerToolExe = argv[1]; tempDirectory = argv[2]; structFile = argv[3]; referenceFile = argv[4]; } std::vector commands; - commands.push_back("\"Niere.*\" -m -o Test.hdr"); - commands.push_back("\"Rueckenmark\" -o Boolean.hdr -z"); + commands.push_back("\"Niere.*\" -m -o Test.nrrd"); + commands.push_back("\"Rueckenmark\" -o Boolean.nrrd -z"); std::vector filenames; filenames.push_back("Test_Niere li"); filenames.push_back("Test_Niere re"); filenames.push_back("Boolean"); - std::vector > voxelsToTestInside; - std::vector > voxelsToTestOutside; - voxelsToTestInside.push_back(std::make_pair(ImageType::IndexType{ 48, 31, 18 }, 1.0)); //Niere li inside - voxelsToTestOutside.push_back(std::make_pair(ImageType::IndexType{ 19, 31, 18 }, 0.0)); //Niere li outside - voxelsToTestInside.push_back(std::make_pair(ImageType::IndexType{ 19, 31, 18 }, 1.0)); //Niere re inside - voxelsToTestOutside.push_back(std::make_pair(ImageType::IndexType{ 48, 31, 18 }, 0.0)); //Niere re outside - voxelsToTestInside.push_back(std::make_pair(ImageType::IndexType{ 35, 32, 30 }, 1.0)); //Rueckenmark inside - voxelsToTestOutside.push_back(std::make_pair(ImageType::IndexType{ 35, 30, 23 }, 0.0)); //Rueckenmark outside + std::vector structnames; + structnames.push_back("Boost"); + structnames.push_back("PTV"); + structnames.push_back("Ref.Pkt"); + structnames.push_back("Darm"); + structnames.push_back("Leber"); + structnames.push_back("Magen_DD"); + structnames.push_back("Niere li"); + structnames.push_back("Niere re"); + structnames.push_back("Rueckenmark"); + structnames.push_back("Aussenkontur"); + + std::vector > voxelsToTestInside; + std::vector > voxelsToTestOutside; + voxelsToTestInside.push_back(std::make_pair(ImageType::IndexType{ 48, 31, 18 }, 1.0)); //Niere li inside + voxelsToTestOutside.push_back(std::make_pair(ImageType::IndexType{ 19, 31, 18 }, 0.0)); //Niere li outside + voxelsToTestInside.push_back(std::make_pair(ImageType::IndexType{ 19, 31, 18 }, 1.0)); //Niere re inside + voxelsToTestOutside.push_back(std::make_pair(ImageType::IndexType{ 48, 31, 18 }, 0.0)); //Niere re outside + voxelsToTestInside.push_back(std::make_pair(ImageType::IndexType{ 35, 32, 30 }, 1.0)); //Rueckenmark inside + voxelsToTestOutside.push_back(std::make_pair(ImageType::IndexType{ 35, 30, 23 }, 0.0)); //Rueckenmark outside boost::filesystem::path callingPath(_callingAppPath); std::string voxelizerToolExeWithPath = callingPath.parent_path().string() + "/" + voxelizerToolExe; std::string baseCommand = voxelizerToolExeWithPath; baseCommand += " -s \"" + structFile + "\""; baseCommand += " -r \"" + referenceFile + "\""; baseCommand += " -e "; for (size_t i = 0; i < commands.size(); i++) { std::string command = baseCommand + commands.at(i); int returnValue = system(command.c_str()); std::cout << "Command line call: " + command << std::endl; CHECK_EQUAL(returnValue, 0); } for (size_t i = 0; i < filenames.size(); i++) { - const std::string HDRfileName = tempDirectory + "/" + filenames.at(i) + ".hdr"; - boost::filesystem::path HDRFile(HDRfileName); + const std::string NRRDfileName = tempDirectory + "/" + filenames.at(i) + ".nrrd"; + boost::filesystem::path NRRDFile(NRRDfileName); - const std::string IMGfileName = tempDirectory + "/" + filenames.at(i) + ".img"; - boost::filesystem::path IMGFile(IMGfileName); + CHECK_EQUAL(boost::filesystem::exists(NRRDFile), true); - CHECK_EQUAL(boost::filesystem::exists(HDRFile), true); - CHECK_EQUAL(boost::filesystem::exists(IMGFile), true); - - //check voxel values - if (boost::filesystem::exists(HDRFile)) - { - ReaderType::Pointer reader = ReaderType::New(); - reader->SetFileName(HDRfileName); - reader->Update(); + //check voxel values + if (boost::filesystem::exists(NRRDFile)) + { + ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(NRRDfileName); + reader->Update(); - ReaderType::OutputImageType::ConstPointer image = reader->GetOutput(); + ReaderType::OutputImageType::ConstPointer image = reader->GetOutput(); - ImageType::PixelType voxelValueInside = image->GetPixel(voxelsToTestInside.at(i).first); - ImageType::PixelType expectedVoxelValueInside = voxelsToTestInside.at(i).second; - CHECK_EQUAL(voxelValueInside, expectedVoxelValueInside); + ImageType::PixelType voxelValueInside = image->GetPixel(voxelsToTestInside.at(i).first); + ImageType::PixelType expectedVoxelValueInside = voxelsToTestInside.at(i).second; + CHECK_EQUAL(voxelValueInside, expectedVoxelValueInside); - ImageType::PixelType voxelValueOutside = image->GetPixel(voxelsToTestOutside.at(i).first); - ImageType::PixelType expectedVoxelValueOutside = voxelsToTestOutside.at(i).second; - CHECK_EQUAL(voxelValueOutside, expectedVoxelValueOutside); - } + ImageType::PixelType voxelValueOutside = image->GetPixel(voxelsToTestOutside.at(i).first); + ImageType::PixelType expectedVoxelValueOutside = voxelsToTestOutside.at(i).second; + CHECK_EQUAL(voxelValueOutside, expectedVoxelValueOutside); + } - if (boost::filesystem::exists(IMGFile)) + if (boost::filesystem::exists(NRRDFile)) { - boost::filesystem::remove(IMGFile); + boost::filesystem::remove(NRRDFile); } + } + + std::string allStructsCommand = voxelizerToolExeWithPath + " -s \"" + structFile + "\""; + allStructsCommand += " -r \"" + referenceFile + "\" -f"; + int allStructsReturnValue = system(allStructsCommand.c_str()); + std::cout << "Command line call: " + allStructsCommand << std::endl; + CHECK_EQUAL(allStructsReturnValue, 0); - if (boost::filesystem::exists(HDRFile)) + for (size_t i = 0; i < structnames.size(); i++) + { + const std::string structName = tempDirectory + "/output_" + structnames.at(i) + ".nrrd"; + boost::filesystem::path structFilePath(structName); + CHECK_EQUAL(boost::filesystem::exists(structFilePath), true); + if (boost::filesystem::exists(structFilePath)) { - boost::filesystem::remove(HDRFile); + boost::filesystem::remove(structFilePath); } } RETURN_AND_REPORT_TEST_SUCCESS; } } } diff --git a/testing/apps/VoxelizerTool/VoxelizerToolVoxelValueTest.cpp b/testing/apps/VoxelizerTool/VoxelizerToolVoxelValueTest.cpp index 6a2a124..9aa7d19 100644 --- a/testing/apps/VoxelizerTool/VoxelizerToolVoxelValueTest.cpp +++ b/testing/apps/VoxelizerTool/VoxelizerToolVoxelValueTest.cpp @@ -1,127 +1,120 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include "litCheckMacros.h" #include #include "itkImage.h" #include "itkImageFileReader.h" #include #include /*! @brief VoxelizerToolTest5. Search the coordinate at the Image and return the Voxel(Pixel) value. */ namespace rttb { namespace testing { //path to the current running directory. VoxelizerTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int VoxelizerToolVoxelValue(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; typedef itk::Image< double, 3 > ImageType; typedef itk::ImageFileReader ReaderType; std::string voxelizerToolExe; std::string tempDirectory; std::string structFile; std::string referenceFile; std::string structName; if (argc > 5) { voxelizerToolExe = argv[1]; tempDirectory = argv[2]; structFile = argv[3]; referenceFile = argv[4]; structName = argv[5]; } boost::filesystem::path callingPath(_callingAppPath); std::string voxelizerToolExeWithPath = callingPath.parent_path().string() + "/" + voxelizerToolExe; std::string command = voxelizerToolExeWithPath; command += " -s \"" + structFile + "\""; command += " -r \"" + referenceFile + "\""; command += " -e " + structName; - command += " -o testOutputVoxelValue.hdr"; + command += " -o testOutputVoxelValue.nrrd"; int returnValue = system(command.c_str()); CHECK_EQUAL(returnValue, 0); //image values taken in Mevislab //Index inside ImageType::IndexType voxelInside1 = {{20, 30, 30}}; ImageType::IndexType voxelInside2 = {{30, 10, 40}}; //Outside index ImageType::IndexType voxelOutside1 = {{40, 30, 30}}; ImageType::IndexType voxelOutside2 = {{10, 40, 30}}; //Border index ImageType::IndexType voxelBorder1 = {{12, 23, 27}}; ImageType::IndexType voxelBorder2 = {{34, 21, 31}}; std::vector voxelIndices = boost::assign::list_of(voxelInside1)(voxelInside2)( voxelOutside1)( voxelOutside2)(voxelBorder1)(voxelBorder2); std::vector expectedVoxelValues = boost::assign::list_of(1.0)(1.0)(0.0)(0.0)( 0.265865)(0.819613); - std::string filenameHDRWithVoxelization = tempDirectory + "/testOutputVoxelValue.hdr"; - std::string filenameIMGWithVoxelization = tempDirectory + "/testOutputVoxelValue.img"; + std::string filenameNRRDWithVoxelization = tempDirectory + "/testOutputVoxelValue.nrrd"; - CHECK(boost::filesystem::exists(filenameHDRWithVoxelization)); - CHECK(boost::filesystem::exists(filenameIMGWithVoxelization)); + CHECK(boost::filesystem::exists(filenameNRRDWithVoxelization)); - if (boost::filesystem::exists(filenameHDRWithVoxelization)) + if (boost::filesystem::exists(filenameNRRDWithVoxelization)) { ReaderType::Pointer reader = ReaderType::New(); - reader->SetFileName(filenameHDRWithVoxelization); + reader->SetFileName(filenameNRRDWithVoxelization); reader->Update(); ReaderType::OutputImageType::ConstPointer image = reader->GetOutput(); for (size_t i = 0; i < voxelIndices.size(); i++) { ImageType::PixelType voxelValue = image->GetPixel(voxelIndices.at(i)); ImageType::PixelType expectedVoxelValue = expectedVoxelValues.at(i); if (expectedVoxelValue == 0.0 || expectedVoxelValue == 1.0) { CHECK_EQUAL(voxelValue, expectedVoxelValue); } else { CHECK_CLOSE(voxelValue, expectedVoxelValue, 1e-4); } } - boost::filesystem::remove(filenameHDRWithVoxelization); - - if (boost::filesystem::exists(filenameIMGWithVoxelization)) - { - boost::filesystem::remove(filenameIMGWithVoxelization); - } + boost::filesystem::remove(filenameNRRDWithVoxelization); } RETURN_AND_REPORT_TEST_SUCCESS; } } } diff --git a/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerAllStructsTest.cpp b/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerAllStructsTest.cpp index 3167347..43c8f22 100644 --- a/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerAllStructsTest.cpp +++ b/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerAllStructsTest.cpp @@ -1,98 +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. // //------------------------------------------------------------------------ #include #include "litCheckMacros.h" #include #include /*! @brief VoxelizerToolTest. Tests a selection of structs. */ namespace rttb { namespace testing { //path to the current running directory. VoxelizerTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int VoxelizerToolVoxelizerAllStructsTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string voxelizerToolExe; std::string tempDirectory; std::string structFile; std::string referenceFile; if (argc > 4) { voxelizerToolExe = argv[1]; tempDirectory = argv[2]; structFile = argv[3]; referenceFile = argv[4]; } boost::filesystem::path callingPath(_callingAppPath); std::string voxelizerToolExeWithPath = callingPath.parent_path().string() + "/" + voxelizerToolExe; std::vector structNames; structNames.push_back("Niere.*"); structNames.push_back("Magen/DD"); structNames.push_back("PTV"); std::string baseCommand = voxelizerToolExeWithPath; baseCommand += " -s \"" + structFile + "\""; baseCommand += " -r \"" + referenceFile + "\""; - baseCommand += " -o outputAllStructs.hdr"; + baseCommand += " -o outputAllStructs.nrrd"; baseCommand += " -e \""; for (size_t i = 0; i < structNames.size(); i++) { std::string command = baseCommand + structNames.at(i) + "\""; std::cout << "Command line call: " + command << std::endl; int returnValue = system(command.c_str()); CHECK_EQUAL(returnValue, 0); - boost::filesystem::path HDRFile(tempDirectory); - HDRFile /= "outputAllStructs.hdr"; + boost::filesystem::path NRRDFile(tempDirectory); + NRRDFile /= "outputAllStructs.nrrd"; - boost::filesystem::path IMGFile(tempDirectory); - IMGFile /= "outputAllStructs.img"; - - CHECK_EQUAL( - boost::filesystem::exists(HDRFile), - true); CHECK_EQUAL( - boost::filesystem::exists(IMGFile), + boost::filesystem::exists(NRRDFile), true); - - if (boost::filesystem::exists(IMGFile)) - { - boost::filesystem::remove(IMGFile); - } - - if (boost::filesystem::exists(HDRFile)) + if (boost::filesystem::exists(NRRDFile)) { - boost::filesystem::remove(HDRFile); + boost::filesystem::remove(NRRDFile); } } RETURN_AND_REPORT_TEST_SUCCESS; } } } diff --git a/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerStructTest.cpp b/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerStructTest.cpp index a42560c..6b787b7 100644 --- a/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerStructTest.cpp +++ b/testing/apps/VoxelizerTool/VoxelizerToolVoxelizerStructTest.cpp @@ -1,98 +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. // //------------------------------------------------------------------------ #include #include "litCheckMacros.h" #include /*! @brief VoxelizerToolTest. Tests a struct with multiple files and a single file */ namespace rttb { namespace testing { //path to the current running directory. VoxelizerTool is in the same directory (Debug/Release) extern const char* _callingAppPath; int VoxelizerToolVoxelizerStructTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string voxelizerToolExe; std::string tempDirectory; std::string structFile; std::string referenceFile; - boost::filesystem::path HDRFile(tempDirectory); - HDRFile /= "outputSingleStruct.hdr"; - - boost::filesystem::path IMGFile(tempDirectory); - IMGFile /= "outputSingleStruct.img"; + boost::filesystem::path NRRDFile(tempDirectory); + NRRDFile /= "outputSingleStruct.nrrd"; if (argc > 4) { voxelizerToolExe = argv[1]; tempDirectory = argv[2]; structFile = argv[3]; referenceFile = argv[4]; } boost::filesystem::path callingPath(_callingAppPath); std::string voxelizerToolExeWithPath = callingPath.parent_path().string() + "/" + voxelizerToolExe; std::string structName = "Heart"; std::string baseCommand = voxelizerToolExeWithPath; baseCommand += " -s \"" + structFile + "\""; baseCommand += " -r \"" + referenceFile + "\""; - baseCommand += " -o outputSingleStruct.hdr"; + baseCommand += " -o outputSingleStruct.nrrd"; baseCommand += " -y itkDicom"; baseCommand += " -e \""; std::string command = baseCommand + structName + "\""; std::cout << "Command line call: " + command << std::endl; int returnValue = system(command.c_str()); CHECK_EQUAL(returnValue, 0); - CHECK_EQUAL(boost::filesystem::exists(HDRFile), true); - CHECK_EQUAL(boost::filesystem::exists(IMGFile), true); - - + CHECK_EQUAL(boost::filesystem::exists(NRRDFile), true); baseCommand = voxelizerToolExeWithPath; baseCommand += " -s \"" + structFile + "\""; - baseCommand += " -r \"" + HDRFile.string() + "\""; - baseCommand += " -o outputSingleStruct.hdr"; + baseCommand += " -r \"" + NRRDFile.string() + "\""; + baseCommand += " -o outputSingleStruct.nrrd"; baseCommand += " -y itk"; baseCommand += " -e \""; command = baseCommand + structName + "\""; std::cout << "Command line call: " + command << std::endl; returnValue = system(command.c_str()); CHECK_EQUAL(returnValue, 0); - if (boost::filesystem::exists(IMGFile)) { - boost::filesystem::remove(IMGFile); - } - - if (boost::filesystem::exists(HDRFile)) { - boost::filesystem::remove(HDRFile); + if (boost::filesystem::exists(NRRDFile)) { + boost::filesystem::remove(NRRDFile); } RETURN_AND_REPORT_TEST_SUCCESS; } } } diff --git a/testing/core/BaseTypeTest.cpp b/testing/core/BaseTypeTest.cpp index d51e2c7..6a24796 100644 --- a/testing/core/BaseTypeTest.cpp +++ b/testing/core/BaseTypeTest.cpp @@ -1,271 +1,271 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #include "litCheckMacros.h" #include "rttbBaseType.h" namespace rttb { namespace testing { /*! @brief BaseTypeTests - tests the API for the classes in baseType - 1) UnsignedIndex3D + 1) Index3D 2) WorldCoordinate3D 3) SpacingVectorType3D 4) OrientationMatrix 5) VoxelGridIndex3D 6) VoxelGridIndex2D */ int BaseTypeTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; - //1) UnsignedIndex3D - CHECK_NO_THROW(UnsignedIndex3D ui); - UnsignedIndex3D emptyUnsignedIndex3D; - CHECK_EQUAL(emptyUnsignedIndex3D.x(), 0); - CHECK_EQUAL(emptyUnsignedIndex3D.y(), 0); - CHECK_EQUAL(emptyUnsignedIndex3D.z(), 0); - - CHECK_NO_THROW(UnsignedIndex3D ui(5)); - UnsignedIndex3D sameValueUnsignedIndex(5); - CHECK_EQUAL(sameValueUnsignedIndex.x(), 5); - CHECK_EQUAL(sameValueUnsignedIndex.y(), 5); - CHECK_EQUAL(sameValueUnsignedIndex.z(), 5); - - CHECK_NO_THROW(UnsignedIndex3D ui(5, 8, 42)); - UnsignedIndex3D differentValueUnsignedIndex(5, 8, 42); - CHECK_EQUAL(differentValueUnsignedIndex.x(), 5); - CHECK_EQUAL(differentValueUnsignedIndex.y(), 8); - CHECK_EQUAL(differentValueUnsignedIndex.z(), 42); - - UnsignedIndex3D threeDimensionalUnsignedIndexSame(5, 8, 42); - UnsignedIndex3D threeDimensionalUnsignedIndexDifferent(1, 2, 3); - - CHECK(differentValueUnsignedIndex == threeDimensionalUnsignedIndexSame); - CHECK_EQUAL(differentValueUnsignedIndex == threeDimensionalUnsignedIndexDifferent, false); - CHECK_EQUAL(differentValueUnsignedIndex == sameValueUnsignedIndex, false); - CHECK_EQUAL(emptyUnsignedIndex3D == sameValueUnsignedIndex, false); + //1) Index3D + CHECK_NO_THROW(Index3D ui); + Index3D emptyIndex3D; + CHECK_EQUAL(emptyIndex3D.x(), 0); + CHECK_EQUAL(emptyIndex3D.y(), 0); + CHECK_EQUAL(emptyIndex3D.z(), 0); + + CHECK_NO_THROW(Index3D ui(5)); + Index3D sameValueIndex(5); + CHECK_EQUAL(sameValueIndex.x(), 5); + CHECK_EQUAL(sameValueIndex.y(), 5); + CHECK_EQUAL(sameValueIndex.z(), 5); + + CHECK_NO_THROW(Index3D ui(5, 8, 42)); + Index3D differentValueIndex(5, 8, 42); + CHECK_EQUAL(differentValueIndex.x(), 5); + CHECK_EQUAL(differentValueIndex.y(), 8); + CHECK_EQUAL(differentValueIndex.z(), 42); + + Index3D threeDimensionalIndexSame(5, 8, 42); + Index3D threeDimensionalIndexDifferent(1, 2, 3); + + CHECK(differentValueIndex == threeDimensionalIndexSame); + CHECK_EQUAL(differentValueIndex == threeDimensionalIndexDifferent, false); + CHECK_EQUAL(differentValueIndex == sameValueIndex, false); + CHECK_EQUAL(emptyIndex3D == sameValueIndex, false); //2) WorldCoordinate3D CHECK_NO_THROW(WorldCoordinate3D wc); WorldCoordinate3D emptyWC3D; CHECK_EQUAL(emptyWC3D.x(), 0); CHECK_EQUAL(emptyWC3D.y(), 0); CHECK_EQUAL(emptyWC3D.z(), 0); CHECK_NO_THROW(WorldCoordinate3D wc(6.5)); WorldCoordinate3D sameValueWC3D(6.5); CHECK_EQUAL(sameValueWC3D.x(), 6.5); CHECK_EQUAL(sameValueWC3D.y(), 6.5); CHECK_EQUAL(sameValueWC3D.z(), 6.5); CHECK_NO_THROW(WorldCoordinate3D(5.8, 0.1, -2.7)); WorldCoordinate3D differentValueWC3D(5.8, 0.1, -2.7); CHECK_EQUAL(differentValueWC3D.x(), 5.8); CHECK_EQUAL(differentValueWC3D.y(), 0.1); CHECK_EQUAL(differentValueWC3D.z(), -2.7); CHECK_EQUAL(differentValueWC3D.toString(), "5.800000 0.100000 -2.700000"); WorldCoordinate3D differentValueWC3Dsecond(1.5, -3.9, 0.7); WorldCoordinate3D resultWC3DCrossTrue(-10.46, -8.11, -22.77); CHECK_NO_THROW(differentValueWC3D.cross(differentValueWC3Dsecond)); auto resultWC3DCrossComputed = differentValueWC3D.cross(differentValueWC3Dsecond); CHECK_CLOSE(resultWC3DCrossComputed.x(), resultWC3DCrossTrue.x(), errorConstant); CHECK_CLOSE(resultWC3DCrossComputed.y(), resultWC3DCrossTrue.y(), errorConstant); CHECK_CLOSE(resultWC3DCrossComputed.z(), resultWC3DCrossTrue.z(), errorConstant); CHECK_NO_THROW(differentValueWC3Dsecond.cross(differentValueWC3D)); auto resultWC3DCrossComputedOrder = differentValueWC3Dsecond.cross(differentValueWC3D); CHECK_CLOSE(resultWC3DCrossComputed.x(), resultWC3DCrossComputedOrder.x()*-1, errorConstant); CHECK_CLOSE(resultWC3DCrossComputed.y(), resultWC3DCrossComputedOrder.y()*-1, errorConstant); CHECK_CLOSE(resultWC3DCrossComputed.z(), resultWC3DCrossComputedOrder.z()*-1, errorConstant); differentValueWC3D = differentValueWC3Dsecond; CHECK_EQUAL(differentValueWC3D.x(), 1.5); CHECK_EQUAL(differentValueWC3D.y(), -3.9); CHECK_EQUAL(differentValueWC3D.z(), 0.7); boost::numeric::ublas::vector wcUblas(3, 4.5); differentValueWC3D = wcUblas; CHECK_EQUAL(differentValueWC3D.x(), 4.5); CHECK_EQUAL(differentValueWC3D.y(), 4.5); CHECK_EQUAL(differentValueWC3D.z(), 4.5); WorldCoordinate3D WCresultMinus; CHECK_NO_THROW(WCresultMinus = differentValueWC3D- differentValueWC3Dsecond); CHECK_EQUAL(WCresultMinus.x(), differentValueWC3D.x()- differentValueWC3Dsecond.x()); CHECK_EQUAL(WCresultMinus.y(), differentValueWC3D.y() - differentValueWC3Dsecond.y()); CHECK_EQUAL(WCresultMinus.z(), differentValueWC3D.z() - differentValueWC3Dsecond.z()); WorldCoordinate3D WCresultPlus; CHECK_NO_THROW(WCresultPlus = differentValueWC3D + differentValueWC3Dsecond); CHECK_EQUAL(WCresultPlus.x(), differentValueWC3D.x() + differentValueWC3Dsecond.x()); CHECK_EQUAL(WCresultPlus.y(), differentValueWC3D.y() + differentValueWC3Dsecond.y()); CHECK_EQUAL(WCresultPlus.z(), differentValueWC3D.z() + differentValueWC3Dsecond.z()); WorldCoordinate3D sameAsWcUblas(4.5); CHECK_EQUAL(resultWC3DCrossTrue== differentValueWC3Dsecond, false); CHECK_EQUAL(differentValueWC3D == sameAsWcUblas, true); WorldCoordinate3D sameAsWcUblasAlmost(4.5+1e-6, 4.5-1e-6, 4.5+2e-7); CHECK_EQUAL(sameAsWcUblas.equalsAlmost(sameAsWcUblasAlmost), true); CHECK_EQUAL(sameAsWcUblas.equalsAlmost(resultWC3DCrossComputedOrder), false); //3) SpacingVectorType CHECK_NO_THROW(SpacingVectorType3D svt); SpacingVectorType3D emptySvt; CHECK_EQUAL(emptySvt.x(), 0); CHECK_EQUAL(emptySvt.y(), 0); CHECK_EQUAL(emptySvt.z(), 0); CHECK_NO_THROW(SpacingVectorType3D svt(1.5)); CHECK_THROW(SpacingVectorType3D svt(-1.5)); SpacingVectorType3D sameValueSvt(1.5); CHECK_EQUAL(sameValueSvt.x(), 1.5); CHECK_EQUAL(sameValueSvt.y(), 1.5); CHECK_EQUAL(sameValueSvt.z(), 1.5); CHECK_NO_THROW(SpacingVectorType3D svt(1.5, 1.5, 0.5)); CHECK_THROW(SpacingVectorType3D svt(1.5, -1.5, 0.5)); CHECK_THROW(SpacingVectorType3D svt(-1.5, -1.5, -0.5)); SpacingVectorType3D differentValuesSvt(1.5, 1.5, 0.5); CHECK_EQUAL(differentValuesSvt.x(), 1.5); CHECK_EQUAL(differentValuesSvt.y(), 1.5); CHECK_EQUAL(differentValuesSvt.z(), 0.5); SpacingVectorType3D differentValuesSvtChanged(0.5, 0.5, 1.5); CHECK_NO_THROW(SpacingVectorType3D svt(differentValuesSvtChanged)); SpacingVectorType3D svtNew(differentValuesSvtChanged); CHECK_EQUAL(svtNew.x(), 0.5); CHECK_EQUAL(svtNew.y(), 0.5); CHECK_EQUAL(svtNew.z(), 1.5); CHECK_NO_THROW(svtNew = differentValuesSvt); CHECK_EQUAL(svtNew.x(), 1.5); CHECK_EQUAL(svtNew.y(), 1.5); CHECK_EQUAL(svtNew.z(), 0.5); CHECK_NO_THROW(svtNew = differentValueWC3D); CHECK_EQUAL(svtNew.x(), 4.5); CHECK_EQUAL(svtNew.y(), 4.5); CHECK_EQUAL(svtNew.z(), 4.5); boost::numeric::ublas::vector ublasVector(3, 0.5); CHECK_NO_THROW(svtNew = ublasVector); CHECK_EQUAL(svtNew.x(), 0.5); CHECK_EQUAL(svtNew.y(), 0.5); CHECK_EQUAL(svtNew.z(), 0.5); SpacingVectorType3D sameAsUblasVector(0.5); CHECK_EQUAL(svtNew== differentValuesSvtChanged, false); CHECK_EQUAL(svtNew == sameAsUblasVector, true); SpacingVectorType3D almostSameAsUblasVector(0.5+1e-6, 0.5-1e-6,0.5+2e-7); CHECK(svtNew.equalsAlmost(almostSameAsUblasVector)); CHECK_EQUAL(differentValuesSvtChanged.toString(), "0.500000 0.500000 1.500000"); //4) OrientationMatrix CHECK_NO_THROW(OrientationMatrix om); OrientationMatrix om; CHECK_EQUAL(om(0, 0), 1.0); CHECK_EQUAL(om(1, 1), 1.0); CHECK_EQUAL(om(2, 2), 1.0); CHECK_EQUAL(om(0, 1), 0.0); CHECK_EQUAL(om(0, 2), 0.0); CHECK_EQUAL(om(1, 0), 0.0); CHECK_EQUAL(om(1, 2), 0.0); CHECK_EQUAL(om(2, 0), 0.0); CHECK_EQUAL(om(2, 1), 0.0); CHECK_NO_THROW(OrientationMatrix omValue(0.1)); OrientationMatrix omValue(0.1); for (unsigned int i = 0; i < 3; i++) { for (unsigned int j = 0; j < 3; j++) { CHECK_EQUAL(omValue(i, j), 0.1); } } OrientationMatrix omValueAlmost(0.1+1e-6); CHECK_EQUAL(omValue.equalsAlmost(omValueAlmost), true); CHECK_EQUAL(omValue.equalsAlmost(om), false); OrientationMatrix omSame; CHECK_EQUAL(om == omSame, true); CHECK_EQUAL(omValue == omValueAlmost, false); //5) VoxelGridIndex3D CHECK_NO_THROW(VoxelGridIndex3D vgi); VoxelGridIndex3D vgi; CHECK_EQUAL(vgi.x(), 0); CHECK_EQUAL(vgi.y(), 0); CHECK_EQUAL(vgi.z(), 0); CHECK_NO_THROW(VoxelGridIndex3D vgiValue(2)); VoxelGridIndex3D vgiValue(2); CHECK_EQUAL(vgiValue.x(), 2); CHECK_EQUAL(vgiValue.y(), 2); CHECK_EQUAL(vgiValue.z(), 2); CHECK_NO_THROW(VoxelGridIndex3D vgiValueDifferent(2,3,5)); VoxelGridIndex3D vgiValueDifferent(2,3,5); CHECK_EQUAL(vgiValueDifferent.x(), 2); CHECK_EQUAL(vgiValueDifferent.y(), 3); CHECK_EQUAL(vgiValueDifferent.z(), 5); CHECK_EQUAL(vgiValueDifferent.toString(), "2 3 5"); - CHECK_NO_THROW(vgiValueDifferent = threeDimensionalUnsignedIndexSame); + CHECK_NO_THROW(vgiValueDifferent = threeDimensionalIndexSame); CHECK_EQUAL(vgiValueDifferent.x(), 5); CHECK_EQUAL(vgiValueDifferent.y(), 8); CHECK_EQUAL(vgiValueDifferent.z(), 42); VoxelGridIndex3D vgiValueDifferentSame(5, 8, 42); CHECK_EQUAL(vgi==vgiValue, false); CHECK_EQUAL(vgiValueDifferentSame == vgiValueDifferent, true); //6) VoxelGridIndex2D CHECK_NO_THROW(VoxelGridIndex2D vgi); VoxelGridIndex2D vgi2D; CHECK_EQUAL(vgi2D.x(), 0); CHECK_EQUAL(vgi2D.y(), 0); CHECK_NO_THROW(VoxelGridIndex2D vgi2DValue(2)); VoxelGridIndex2D vgi2DValue(2); CHECK_EQUAL(vgi2DValue.x(), 2); CHECK_EQUAL(vgi2DValue.y(), 2); CHECK_NO_THROW(VoxelGridIndex2D vgi2DValueDifferent(2, 3)); VoxelGridIndex2D vgi2DValueDifferent(2, 3); CHECK_EQUAL(vgi2DValueDifferent.x(), 2); CHECK_EQUAL(vgi2DValueDifferent.y(), 3); CHECK_EQUAL(vgi2DValueDifferent.toString(), "2 3"); VoxelGridIndex2D vgi2DValueDifferentSame(2, 3); CHECK_EQUAL(vgi2D == vgi2DValueDifferent, false); CHECK_EQUAL(vgi2DValueDifferent == vgi2DValueDifferentSame, true); RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb \ No newline at end of file diff --git a/testing/core/CreateTestStructure.cpp b/testing/core/CreateTestStructure.cpp index d310ac4..0682d37 100644 --- a/testing/core/CreateTestStructure.cpp +++ b/testing/core/CreateTestStructure.cpp @@ -1,681 +1,681 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ // 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 (size_t i = 0; i < aVoxelVector.size(); i++) { VoxelGridIndex3D voxelIndex; voxelIndex(0) = (aVoxelVector.at(i)).x(); voxelIndex(1) = (aVoxelVector.at(i)).y(); voxelIndex(2) = sliceNumber; WorldCoordinate3D p1; _geoInfo.indexToWorldCoordinate(voxelIndex, p1); polygon.push_back(p1); std::cout << "(" << p1.x() << "," << p1.y() << "," << p1.z() << ")" << "; "; } std::cout << std::endl; return polygon; } PolygonType CreateTestStructure::createPolygonCenter(std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { VoxelGridIndex3D voxelIndex; voxelIndex(0) = (aVoxelVector.at(i)).x(); voxelIndex(1) = (aVoxelVector.at(i)).y(); voxelIndex(2) = sliceNumber; WorldCoordinate3D p1; _geoInfo.indexToWorldCoordinate(voxelIndex, p1); WorldCoordinate3D p; p(0) = p1.x() ; p(1) = p1.y() ; //This can be done directly for x/y coordinates, but not for z. Thus, we do it in this function. p(2) = p1.z() - _geoInfo.getSpacing().z() / 2; polygon.push_back(p); } return polygon; } PolygonType CreateTestStructure::createPolygonCenterOnPlaneCenter(std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { VoxelGridIndex3D voxelIndex; - DoubleVoxelGridIndex3D indexDouble = DoubleVoxelGridIndex3D((aVoxelVector.at(i)).x(), (aVoxelVector.at(i)).y(), + ContinuousVoxelGridIndex3D indexDouble = ContinuousVoxelGridIndex3D((aVoxelVector.at(i)).x(), (aVoxelVector.at(i)).y(), sliceNumber); WorldCoordinate3D p1; - _geoInfo.geometryCoordinateToWorldCoordinate(indexDouble, p1); + _geoInfo.continuousIndexToWorldCoordinate(indexDouble, p1); polygon.push_back(p1); } return polygon; } PolygonType CreateTestStructure::createPolygonBetweenUpperLeftAndCenter( std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { 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 p; p(0) = p1.x() + delta.x() / 4; p(1) = p1.y() + delta.y() / 4; p(2) = p1.z(); polygon.push_back(p); std::cout << "(" << p.x() << "," << p.y() << "," << p.z() << ")" << "; "; } std::cout << std::endl; return polygon; } PolygonType CreateTestStructure::createPolygonBetweenLowerRightAndCenter( std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { 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 p; p(0) = p1.x() + delta.x() * 0.75; p(1) = p1.y() + delta.y() * 0.75; p(2) = p1.z(); polygon.push_back(p); std::cout << "(" << p.x() << "," << p.y() << "," << p.z() << ")" << "; "; } std::cout << std::endl; return polygon; } PolygonType CreateTestStructure::createPolygonLeftEdgeMiddle(std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { 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 p; p(0) = p1.x(); p(1) = p1.y() + delta.y() * 0.5; p(2) = p1.z(); polygon.push_back(p); std::cout << "(" << p.x() << "," << p.y() << "," << p.z() << ")" << "; "; } std::cout << std::endl; return polygon; } PolygonType CreateTestStructure::createPolygonUpperCenter(std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { 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 p; p(0) = p1.x() + delta.x() * 0.5; p(1) = p1.y(); p(2) = p1.z(); polygon.push_back(p); std::cout << "(" << p.x() << "," << p.y() << "," << p.z() << ")" << "; "; } std::cout << std::endl; return polygon; } PolygonType CreateTestStructure::createPolygonIntermediatePoints(std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; for (size_t i = 0; i < aVoxelVector.size(); i++) { VoxelGridIndex3D voxelIndex; VoxelGridIndex3D voxelIndex2; voxelIndex(0) = (aVoxelVector.at(i)).x(); voxelIndex(1) = (aVoxelVector.at(i)).y(); voxelIndex(2) = sliceNumber; if (i < (aVoxelVector.size() - 1)) { voxelIndex2(0) = (aVoxelVector.at(i + 1)).x(); voxelIndex2(1) = (aVoxelVector.at(i + 1)).y(); voxelIndex2(2) = sliceNumber; } else { voxelIndex2(0) = (aVoxelVector.at(0)).x(); voxelIndex2(1) = (aVoxelVector.at(0)).y(); voxelIndex2(2) = sliceNumber; } WorldCoordinate3D p1; _geoInfo.indexToWorldCoordinate(voxelIndex, p1); SpacingVectorType3D delta = _geoInfo.getSpacing(); WorldCoordinate3D p2; _geoInfo.indexToWorldCoordinate(voxelIndex2, p2); SpacingVectorType3D delta2 = _geoInfo.getSpacing(); WorldCoordinate3D wp1; wp1(0) = p1.x() + delta.x() * 0.75; wp1(1) = p1.y() + delta.y() * 0.75; wp1(2) = p1.z(); WorldCoordinate3D wp2; wp2(0) = p2.x() + delta.x() * 0.75; wp2(1) = p2.y() + delta.y() * 0.75; wp2(2) = p2.z(); polygon.push_back(wp1); double diffX = (wp2.x() - wp1.x()) / 1000.0; double diffY = (wp2.y() - wp1.y()) / 1000.0; WorldCoordinate3D wp_intermediate; wp_intermediate(0) = 0; wp_intermediate(1) = 0; for (int j = 0; j < 1000; j++) { wp_intermediate(0) = wp1.x() + j * diffX; wp_intermediate(1) = wp1.y() + j * diffY; polygon.push_back(wp_intermediate); } } std::cout << std::endl; return polygon; } PolygonType CreateTestStructure::createPolygonCircle(std::vector aVoxelVector, GridIndexType sliceNumber) { PolygonType polygon; if (aVoxelVector.size() > 0) { unsigned 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 (unsigned int j = 0; j <= 1000; j++) { double y_offset = sqrt((radius * radius) - ((frac_radius * j) * (frac_radius * j))); wp_intermediate(0) = wp1.x() + frac_radius * j; wp_intermediate(1) = wp1.y() - (y_offset * correct_y); polygon.push_back(wp_intermediate); } for (unsigned int j = 1000; j <= 1000; j--) { double y_offset = sqrt((radius * radius) - ((frac_radius * j) * (frac_radius * j))); wp_intermediate(0) = wp1.x() + frac_radius * j; wp_intermediate(1) = wp1.y() + (y_offset * correct_y); polygon.push_back(wp_intermediate); } for (unsigned int j = 0; j <= 1000; j++) { double y_offset = sqrt((radius * radius) - ((frac_radius * j) * (frac_radius * j))); wp_intermediate(0) = wp1.x() - frac_radius * j; wp_intermediate(1) = wp1.y() + y_offset * correct_y; polygon.push_back(wp_intermediate); } for (unsigned int j = 1000; j <= 1000; j--) { double y_offset = sqrt((radius * radius) - ((frac_radius * j) * (frac_radius * j))); wp_intermediate(0) = wp1.x() + frac_radius * j; 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/GeometricInfoTest.cpp b/testing/core/GeometricInfoTest.cpp index 2e2ec50..70b2f75 100644 --- a/testing/core/GeometricInfoTest.cpp +++ b/testing/core/GeometricInfoTest.cpp @@ -1,555 +1,555 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ #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 4) test set/getSpacing 5) test set/getNumColumns/Rows/Slices 6) test get/setOrientationMatrix 8) test operators "==" 9) test equalsAlmost 10) test world to index coordinate conversion 11) test isInside and index to world coordinate conversion - 12) test with simple Geometry: isInside, geometryCoordinateToWorldCoordinate(), worldCoordinateToGeometryCoordinate(), indexToWorldCoordinate() + 12) test with simple Geometry: isInside, continuousIndexToWorldCoordinate(), worldCoordinateToContinuousIndex(), indexToWorldCoordinate() 13) test getNumberOfVoxels 14) Test convert, validID and validIndex */ 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()); //4) test set/getSpacing //negative spacing does not make sense! /*!@is related to #2028 Should SpacingTypeVector/GridVolumeType/OrientationMatrix be forced to be non-negative?*/ SpacingVectorType3D expectedSpacing(4.15, 2.35, 100); 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()); //8) test operators "==" core::GeometricInfo geoInfo2; CHECK_EQUAL(geoInfo, geoInfo); CHECK(!(geoInfo == geoInfo2)); CHECK_EQUAL(geoInfo.getOrientationMatrix(), testOM); CHECK(!(geoInfo.getOrientationMatrix() == testNullOM)); //9) test equalsALmost OrientationMatrix testOM2 = testOM; SpacingVectorType3D testSPV2 = expectedSpacing; WorldCoordinate3D testIPP2 = testIPP; core::GeometricInfo testGI2, testGIEmpty; testGI2.setImagePositionPatient(testIPP2); testGI2.setOrientationMatrix(testOM2); testGI2.setSpacing(testSPV2); double smallValue = 0.000000001; testOM(0, 0) += smallValue; testSPV2(2) += smallValue; testIPP2(1) += smallValue; core::GeometricInfo testGI2similar; testGI2similar.setImagePositionPatient(testIPP2); testGI2similar.setOrientationMatrix(testOM2); testGI2similar.setSpacing(testSPV2); CHECK_EQUAL(testGI2.equalsAlmost(testGI2similar), true); CHECK_EQUAL(testGI2similar.equalsAlmost(testGI2), true); CHECK_EQUAL(testGI2.equalsAlmost(testGI2similar, smallValue * 0.001), false); CHECK_EQUAL(testGIEmpty.equalsAlmost(testGI2), false); CHECK_EQUAL(testGI2.equalsAlmost(testGIEmpty), false); //10) test world to index coordinate conversion //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. 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); //11) test isInside and index to world coordinate conversion //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))); WorldCoordinate3D testWorldCoordinate; - DoubleVoxelGridIndex3D testDoubleIndex; + ContinuousVoxelGridIndex3D testDoubleIndex; - DoubleVoxelGridIndex3D doubleIndex1 = DoubleVoxelGridIndex3D(0.1, 0, -0.3); + ContinuousVoxelGridIndex3D doubleIndex1 = ContinuousVoxelGridIndex3D(0.1, 0, -0.3); const WorldCoordinate3D expectedDoubleIndex1(20.1, 100, -1000.3); - DoubleVoxelGridIndex3D doubleIndex2 = DoubleVoxelGridIndex3D(11, 6, 15); //outside + ContinuousVoxelGridIndex3D doubleIndex2 = ContinuousVoxelGridIndex3D(11, 6, 15); //outside const WorldCoordinate3D expectedDoubleIndex2(31, 106, -985); - DoubleVoxelGridIndex3D doubleIndex3 = DoubleVoxelGridIndex3D(10.1, 5.0, + ContinuousVoxelGridIndex3D doubleIndex3 = ContinuousVoxelGridIndex3D(10.1, 5.0, 3.0); // outside: Grid dimension = [10,5,3] const WorldCoordinate3D expectedDoubleIndex3(30.1, 105, -997); - DoubleVoxelGridIndex3D doubleIndex4 = DoubleVoxelGridIndex3D(0.0, 0.0, 0.0); + ContinuousVoxelGridIndex3D doubleIndex4 = ContinuousVoxelGridIndex3D(0.0, 0.0, 0.0); const WorldCoordinate3D expectedDoubleIndex4 = geoInfo.getImagePositionPatient(); //test double index to world coordinate - geoInfo.geometryCoordinateToWorldCoordinate(doubleIndex1, testWorldCoordinate); + geoInfo.continuousIndexToWorldCoordinate(doubleIndex1, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, expectedDoubleIndex1); - geoInfo.geometryCoordinateToWorldCoordinate(doubleIndex2, testWorldCoordinate); + geoInfo.continuousIndexToWorldCoordinate(doubleIndex2, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, expectedDoubleIndex2); - geoInfo.geometryCoordinateToWorldCoordinate(doubleIndex3, testWorldCoordinate); + geoInfo.continuousIndexToWorldCoordinate(doubleIndex3, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, expectedDoubleIndex3); - geoInfo.geometryCoordinateToWorldCoordinate(doubleIndex4, testWorldCoordinate); + geoInfo.continuousIndexToWorldCoordinate(doubleIndex4, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, expectedDoubleIndex4); - geoInfo.worldCoordinateToGeometryCoordinate(expectedDoubleIndex4, testDoubleIndex); + geoInfo.worldCoordinateToContinuousIndex(expectedDoubleIndex4, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndex4); - geoInfo.worldCoordinateToGeometryCoordinate(expectedDoubleIndex3, testDoubleIndex); + geoInfo.worldCoordinateToContinuousIndex(expectedDoubleIndex3, testDoubleIndex); CHECK_CLOSE(testDoubleIndex(0), doubleIndex3(0), errorConstant); CHECK_CLOSE(testDoubleIndex(1), doubleIndex3(1), errorConstant); CHECK_CLOSE(testDoubleIndex(2), doubleIndex3(2), errorConstant); - geoInfo.worldCoordinateToGeometryCoordinate(expectedDoubleIndex2, testDoubleIndex); + geoInfo.worldCoordinateToContinuousIndex(expectedDoubleIndex2, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndex2); - geoInfo.worldCoordinateToGeometryCoordinate(expectedDoubleIndex1, testDoubleIndex); + geoInfo.worldCoordinateToContinuousIndex(expectedDoubleIndex1, testDoubleIndex); CHECK_CLOSE(testDoubleIndex(0), doubleIndex1(0), errorConstant); CHECK_CLOSE(testDoubleIndex(1), doubleIndex1(1), errorConstant); CHECK_CLOSE(testDoubleIndex(2), doubleIndex1(2), errorConstant); VoxelGridIndex3D testIntIndex; geoInfo.worldCoordinateToIndex(expectedDoubleIndex4, testIntIndex); CHECK_EQUAL(testIntIndex, insideTest1); geoInfo.worldCoordinateToIndex(expectedDoubleIndex1, testIntIndex); CHECK_EQUAL(testIntIndex, insideTest1); geoInfo.worldCoordinateToIndex(expectedDoubleIndex3, testIntIndex); CHECK_EQUAL(testIntIndex, expectedVoxelDims); //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 //no half voxel shift anymore because we changed indexToWorldCoordinate/worldCoordinateToIndex insideTest1 = VoxelGridIndex3D(0, 0, 0); //origin (inside) const WorldCoordinate3D expectedIndex1(20, 100, -1000); insideTest2 = VoxelGridIndex3D(6, 0, 2); //inside const WorldCoordinate3D expectedIndex2(23, 94, -1000); insideTest3 = VoxelGridIndex3D(11, 6, 15); //outside const WorldCoordinate3D expectedIndex3(25.5, 55, -994); insideTest4 = VoxelGridIndex3D(10, 5, 3); // outside: Grid dimension = [10,5,3] const WorldCoordinate3D expectedIndex4(25, 91, -995); 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); - //12) test with simple Geometry: isInside, geometryCoordinateToWorldCoordinate(), worldCoordinateToGeometryCoordinate(), indexToWorldCoordinate() + //12) test with simple Geometry: isInside, continuousIndexToWorldCoordinate(), worldCoordinateToContinuousIndex(), indexToWorldCoordinate() core::GeometricInfo geoInfoSimple; ImageSize rttbSimpleSize = ImageSize(10, 10, 10); geoInfoSimple.setImageSize(rttbSimpleSize); SpacingVectorType3D spacingSimple(1, 1, 1); geoInfoSimple.setSpacing(spacingSimple); OrientationMatrix OMOnes; geoInfoSimple.setOrientationMatrix(OMOnes); - const DoubleVoxelGridIndex3D doubleIndexPixelOutside1 = DoubleVoxelGridIndex3D(-0.501, 0.0, 0.0); - const DoubleVoxelGridIndex3D doubleIndexPixelOutside2 = DoubleVoxelGridIndex3D(0.0, 9.501, 0.0); - const DoubleVoxelGridIndex3D doubleIndexPixelZero1 = DoubleVoxelGridIndex3D(0.0, 0.0, 0.0); - const DoubleVoxelGridIndex3D doubleIndexPixelZero2 = DoubleVoxelGridIndex3D(-0.5, -0.5, -0.5); - const DoubleVoxelGridIndex3D doubleIndexPixelZero3 = DoubleVoxelGridIndex3D(0.499999, 0.499999, 0.499999); - const DoubleVoxelGridIndex3D doubleIndexPixelOne1 = DoubleVoxelGridIndex3D(1.0, 0.0, 0.0); - const DoubleVoxelGridIndex3D doubleIndexPixelOne2 = DoubleVoxelGridIndex3D(0.5, 0.499999, 0.499999); - const DoubleVoxelGridIndex3D doubleIndexPixelOne3 = DoubleVoxelGridIndex3D(1.49, -0.5, -0.5); - const DoubleVoxelGridIndex3D doubleIndexPixelLast1 = DoubleVoxelGridIndex3D(9.0, 9.0, 9.0); - const DoubleVoxelGridIndex3D doubleIndexPixelLast2 = DoubleVoxelGridIndex3D(9.4999, 9.4999, 9.4999); - const DoubleVoxelGridIndex3D doubleIndexPixelLast3 = DoubleVoxelGridIndex3D(8.501, 8.501, 8.501); + const ContinuousVoxelGridIndex3D doubleIndexPixelOutside1 = ContinuousVoxelGridIndex3D(-0.501, 0.0, 0.0); + const ContinuousVoxelGridIndex3D doubleIndexPixelOutside2 = ContinuousVoxelGridIndex3D(0.0, 9.501, 0.0); + const ContinuousVoxelGridIndex3D doubleIndexPixelZero1 = ContinuousVoxelGridIndex3D(0.0, 0.0, 0.0); + const ContinuousVoxelGridIndex3D doubleIndexPixelZero2 = ContinuousVoxelGridIndex3D(-0.5, -0.5, -0.5); + const ContinuousVoxelGridIndex3D doubleIndexPixelZero3 = ContinuousVoxelGridIndex3D(0.499999, 0.499999, 0.499999); + const ContinuousVoxelGridIndex3D doubleIndexPixelOne1 = ContinuousVoxelGridIndex3D(1.0, 0.0, 0.0); + const ContinuousVoxelGridIndex3D doubleIndexPixelOne2 = ContinuousVoxelGridIndex3D(0.5, 0.499999, 0.499999); + const ContinuousVoxelGridIndex3D doubleIndexPixelOne3 = ContinuousVoxelGridIndex3D(1.49, -0.5, -0.5); + const ContinuousVoxelGridIndex3D doubleIndexPixelLast1 = ContinuousVoxelGridIndex3D(9.0, 9.0, 9.0); + const ContinuousVoxelGridIndex3D doubleIndexPixelLast2 = ContinuousVoxelGridIndex3D(9.4999, 9.4999, 9.4999); + const ContinuousVoxelGridIndex3D doubleIndexPixelLast3 = ContinuousVoxelGridIndex3D(8.501, 8.501, 8.501); const VoxelGridIndex3D indexPixelOutside = VoxelGridIndex3D(11, 0, 0); const VoxelGridIndex3D indexPixelZero = VoxelGridIndex3D(0, 0, 0); const VoxelGridIndex3D indexPixelOne = VoxelGridIndex3D(1, 0, 0); const VoxelGridIndex3D indexPixelLast = VoxelGridIndex3D(9, 9, 9); const WorldCoordinate3D worldCoordinateOutside1(doubleIndexPixelOutside1(0), doubleIndexPixelOutside1(1), doubleIndexPixelOutside1(2)); const WorldCoordinate3D worldCoordinateOutside2(doubleIndexPixelOutside2(0), doubleIndexPixelOutside2(1), doubleIndexPixelOutside2(2)); const WorldCoordinate3D worldCoordinatePixelZero1(doubleIndexPixelZero1(0), doubleIndexPixelZero1(1), doubleIndexPixelZero1(2)); const WorldCoordinate3D worldCoordinatePixelZero2(doubleIndexPixelZero2(0), doubleIndexPixelZero2(1), doubleIndexPixelZero2(2)); const WorldCoordinate3D worldCoordinatePixelZero3(doubleIndexPixelZero3(0), doubleIndexPixelZero3(1), doubleIndexPixelZero3(2)); const WorldCoordinate3D worldCoordinatePixelOne1(doubleIndexPixelOne1(0), doubleIndexPixelOne1(1), doubleIndexPixelOne1(2)); const WorldCoordinate3D worldCoordinatePixelOne2(doubleIndexPixelOne2(0), doubleIndexPixelOne2(1), doubleIndexPixelOne2(2)); const WorldCoordinate3D worldCoordinatePixelOne3(doubleIndexPixelOne3(0), doubleIndexPixelOne3(1), doubleIndexPixelOne3(2)); const WorldCoordinate3D worldCoordinatePixelLast1(doubleIndexPixelLast1(0), doubleIndexPixelLast1(1), doubleIndexPixelLast1(2)); const WorldCoordinate3D worldCoordinatePixelLast2(doubleIndexPixelLast2(0), doubleIndexPixelLast2(1), doubleIndexPixelLast2(2)); const WorldCoordinate3D worldCoordinatePixelLast3(doubleIndexPixelLast3(0), doubleIndexPixelLast3(1), doubleIndexPixelLast3(2)); bool isInside; - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelOutside1, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelOutside1, testWorldCoordinate); CHECK(!isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelOutside2, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelOutside2, testWorldCoordinate); CHECK(!isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelZero1, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelZero1, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelZero1); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelZero2, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelZero2, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelZero2); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelZero3, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelZero3, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelZero3); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelOne1, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelOne1, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelOne1); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelOne2, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelOne2, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelOne2); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelOne3, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelOne3, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelOne3); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelLast1, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelLast1, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelLast1); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelLast2, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelLast2, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelLast2); CHECK(isInside); - isInside = geoInfoSimple.geometryCoordinateToWorldCoordinate(doubleIndexPixelLast3, testWorldCoordinate); + isInside = geoInfoSimple.continuousIndexToWorldCoordinate(doubleIndexPixelLast3, testWorldCoordinate); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelLast3); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinateOutside1, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinateOutside1, testDoubleIndex); CHECK(!isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinateOutside2, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinateOutside2, testDoubleIndex); CHECK(!isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelZero1, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelZero1, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelZero1); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelZero2, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelZero2, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelZero2); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelZero3, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelZero3, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelZero3); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelOne1, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelOne1, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelOne1); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelOne2, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelOne2, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelOne2); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelOne3, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelOne3, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelOne3); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelLast1, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelLast1, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelLast1); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelLast2, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelLast2, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelLast2); CHECK(isInside); - isInside = geoInfoSimple.worldCoordinateToGeometryCoordinate(worldCoordinatePixelLast3, testDoubleIndex); + isInside = geoInfoSimple.worldCoordinateToContinuousIndex(worldCoordinatePixelLast3, testDoubleIndex); CHECK_EQUAL(testDoubleIndex, doubleIndexPixelLast3); CHECK(isInside); isInside = geoInfoSimple.indexToWorldCoordinate(indexPixelOutside, testWorldCoordinate); CHECK(!isInside); isInside = geoInfoSimple.indexToWorldCoordinate(indexPixelZero, testWorldCoordinate); CHECK(isInside); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelZero1); isInside = geoInfoSimple.indexToWorldCoordinate(indexPixelOne, testWorldCoordinate); CHECK(isInside); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelOne1); isInside = geoInfoSimple.indexToWorldCoordinate(indexPixelLast, testWorldCoordinate); CHECK(isInside); CHECK_EQUAL(testWorldCoordinate, worldCoordinatePixelLast1); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinateOutside1, testIntIndex); CHECK(!isInside); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinateOutside2, testIntIndex); CHECK(!isInside); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelZero1, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelZero); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelZero2, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelZero); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelZero3, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelZero); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelOne1, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelOne); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelOne2, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelOne); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelOne3, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelOne); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelLast1, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelLast); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelLast2, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelLast); isInside = geoInfoSimple.worldCoordinateToIndex(worldCoordinatePixelLast3, testIntIndex); CHECK(isInside); CHECK_EQUAL(testIntIndex, indexPixelLast); //13) test getNumberOfVoxels CHECK_EQUAL(expectedVoxelDims(0)*expectedVoxelDims(1)*expectedVoxelDims(2), geoInfo.getNumberOfVoxels()); //14) 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/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz b/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz index 3b6e1ff..d7610aa 100644 Binary files a/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz and b/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz differ diff --git a/utilities/ArgumentParsingLib/CMakeLists.txt b/utilities/ArgumentParsingLib/CMakeLists.txt index 88555f0..f6d7ffa 100644 --- a/utilities/ArgumentParsingLib/CMakeLists.txt +++ b/utilities/ArgumentParsingLib/CMakeLists.txt @@ -1,28 +1,28 @@ cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(ArgumentParsingLib) # Disallow compilers that don't support the necessary C++11 features (since the property CXX_STANDARD doesn't work with MSVC) if(MSVC AND MSVC_VERSION LESS 1800) message(FATAL_ERROR "The C++11 features in this project require Visual Studio 2013 or higher") endif() set(BUILD_TESTS false CACHE BOOL "Build tests") set(MAIN_DIR main) set(TEST_DIR test) # Sources AND tests both need boost, so include it here IF (WIN32) SET(Boost_USE_STATIC_LIBS ON) ELSE() SET(Boost_USE_STATIC_LIBS OFF) ENDIF() find_package(Boost REQUIRED COMPONENTS program_options) -include_directories(${Boost_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIRS}) add_subdirectory(${MAIN_DIR}) add_subdirectory(${TEST_DIR}) # Create a file by which this project can be found CONFIGURE_FILE(${ArgumentParsingLib_SOURCE_DIR}/ArgumentParsingLibConfig.cmake.in ${ArgumentParsingLib_BINARY_DIR}/ArgumentParsingLibConfig.cmake @ONLY IMMEDIATE) \ No newline at end of file