diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 6d9192a..23758a1 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -1,69 +1,70 @@ MESSAGE(STATUS "processing RTToolbox apps") IF (WIN32) IF (MSVC_VERSION LESS 1800) MESSAGE(FATAL_ERROR "Some features of RTToolbox requires C++11 features that require Visual Studio 2013 or higher.") ENDIF(MSVC_VERSION LESS 1800) ENDIF(WIN32) + #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 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." OFF) IF(BUILD_App_DoseAcc) ADD_SUBDIRECTORY(DoseAcc) IF(RTTB_VIRTUOS_SUPPORT AND BUILD_IO_Virtuos) ADD_SUBDIRECTORY(DoseAcc4V) ENDIF() ENDIF() OPTION(BUILD_App_DoseMap "Determine if the application DoseMap will be generated." OFF) IF(BUILD_App_DoseMap AND BUILD_IO_Virtuos) ADD_SUBDIRECTORY(DoseMap) ENDIF() OPTION(BUILD_App_DoseTool "Determine if the application DoseTool will be generated." OFF) IF(BUILD_App_DoseTool) ADD_SUBDIRECTORY(DoseTool) IF(RTTB_VIRTUOS_SUPPORT AND BUILD_IO_Virtuos) ADD_SUBDIRECTORY(DoseTool4V) ENDIF() ENDIF(BUILD_App_DoseTool) OPTION(BUILD_App_VoxelizerTool "Determine if the application VoxelizerTool will be generated." OFF) IF(BUILD_App_VoxelizerTool) ADD_SUBDIRECTORY(VoxelizerTool) ENDIF(BUILD_App_VoxelizerTool) OPTION(BUILD_App_BioModelCalc "Determine if the demo application BioModelCalc will be generated." OFF) IF(BUILD_App_BioModelCalc) ADD_SUBDIRECTORY(BioModelCalc) IF(RTTB_VIRTUOS_SUPPORT AND BUILD_IO_Virtuos) ADD_SUBDIRECTORY(BioModelCalc4V) ENDIF() ENDIF(BUILD_App_BioModelCalc) \ No newline at end of file diff --git a/apps/DoseTool/DoseToolHelper.cpp b/apps/DoseTool/DoseToolHelper.cpp index a6a9166..152a20a 100644 --- a/apps/DoseTool/DoseToolHelper.cpp +++ b/apps/DoseTool/DoseToolHelper.cpp @@ -1,336 +1,337 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 1374 $ (last changed revision) // @date $Date: 2016-05-30 14:15:42 +0200 (Mo, 30 Mai 2016) $ (last change date) // @author $Author: hentsch $ (last changed by) */ #include "DoseToolHelper.h" #include "boost/make_shared.hpp" #include "boost/shared_ptr.hpp" #include "boost/property_tree/ptree.hpp" +#include #include "boost/filesystem.hpp" #include "rttbExceptionMacros.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbDicomHelaxFileDoseAccessorGenerator.h" #include "rttbITKImageFileAccessorGenerator.h" #include "rttbStructureSetGeneratorInterface.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbITKImageFileMaskAccessorGenerator.h" #include "rttbDVHCalculator.h" #include "rttbDVHXMLFileWriter.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbBoostMaskAccessor.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbDoseStatisticsXMLWriter.h" #include "rttbVOIindexIdentifier.h" rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadDose(const std::string& fileName, const rttb::apps::doseTool::ApplicationData::LoadingStyleArgType& args) { rttb::core::DoseAccessorInterface::DoseAccessorPointer result; std::cout << std::endl << "read dose file... "; if (args.empty() || args[0] == "dicom") { std::cout << "use RTTB dicom IO... "; result = loadDicomDose(fileName); } else if (args[0] == "helax") { std::cout << "use RTTB Helax IO... "; result = loadHelaxDose(fileName); } else if (args[0] == "itk") { std::cout << "use RTTB itk IO... "; result = loadITKDose(fileName); } else { rttbDefaultExceptionStaticMacro( << "Unknown io style selected. Cannot load data. Selected style: " << args[0]); } std::cout << "done." << std::endl; return result; } rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadDicomDose(const std::string& fileName) { rttb::io::dicom::DicomFileDoseAccessorGenerator generator(fileName); return generator.generateDoseAccessor(); } rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadHelaxDose(const std::string& path) { rttb::io::helax::DicomHelaxFileDoseAccessorGenerator generator(path); return generator.generateDoseAccessor(); } rttb::core::DoseAccessorInterface::DoseAccessorPointer rttb::apps::doseTool::loadITKDose(const std::string& fileName) { rttb::io::itk::ITKImageFileAccessorGenerator generator(fileName); return generator.generateDoseAccessor(); } rttb::core::StructureSetGeneratorInterface::StructureSetPointer rttb::apps::doseTool::loadStruct( const std::string& fileName, const ApplicationData::LoadingStyleArgType& args, const std::string& structNameRegex) { rttb::core::StructureSetGeneratorInterface::StructureSetPointer result; std::cout << std::endl << "read struct file... "; if (args.empty() || args[0] == "dicom") { std::cout << "use RTTB dicom IO... "; result = loadDicomStruct(fileName, structNameRegex); } else { rttbDefaultExceptionStaticMacro( << "Unknown io style selected. Cannot load data. Selected style: " << args[0]); } std::cout << "done." << std::endl; return result; } rttb::core::StructureSetGeneratorInterface::StructureSetPointer rttb::apps::doseTool::loadDicomStruct( const std::string& fileName, const std::string& structNameRegex) { rttb::io::dicom::DicomFileStructureSetGenerator generator(fileName); if (!structNameRegex.empty()) { generator.setStructureLableFilterActive(true); generator.setFilterRegEx(structNameRegex); } return generator.generateStructureSet(); } void rttb::apps::doseTool::processData(rttb::apps::doseTool::ApplicationData& appData) { std::cout << std::endl << "generating masks... "; std::vector maskAccessorPtrVector = generateMasks( appData); std::cout << "done." << std::endl; for (size_t i = 0; i < maskAccessorPtrVector.size(); i++) { core::DoseIteratorInterface::DoseIteratorPointer spDoseIterator(generateMaskedDoseIterator( maskAccessorPtrVector.at(i), appData._dose)); if (appData._computeDoseStatistics) { std::cout << std::endl << "computing dose statistics... "; algorithms::DoseStatistics::DoseStatisticsPointer statistics = calculateDoseStatistics( spDoseIterator, appData._computeComplexDoseStatistics, appData._prescribedDose); std::cout << "done." << std::endl; std::cout << std::endl << "writing dose statistics to file... "; std::string outputFilename; if (appData._multipleStructsMode) { outputFilename = assembleFilenameWithStruct(appData._doseStatisticOutputFileName, appData._structNames.at(i)); } else { outputFilename = appData._doseStatisticOutputFileName; } writeDoseStatisticsFile(statistics, outputFilename, appData._structNames.at(i), appData); std::cout << "done." << std::endl; } if (appData._computeDVH) { std::cout << std::endl << "computing DVH... "; core::DVH::DVHPointer dvh = calculateDVH(spDoseIterator, appData._struct->getUID(), appData._dose->getUID()); std::cout << "done." << std::endl; std::cout << std::endl << "writing DVH to file... "; std::string outputFilename; if (appData._multipleStructsMode) { outputFilename = assembleFilenameWithStruct(appData._dvhOutputFilename, appData._structNames.at(i)); } else { outputFilename = appData._dvhOutputFilename; } writeDVHFile(dvh, outputFilename); std::cout << "done." << std::endl; } } } std::vector rttb::apps::doseTool::generateMasks( rttb::apps::doseTool::ApplicationData& appData) { std::vector maskAccessorPtrVector; if (appData._structLoadStyle.front() == "itk") { maskAccessorPtrVector.push_back(rttb::io::itk::ITKImageFileMaskAccessorGenerator( appData._structFileName).generateMaskAccessor()); } else { std::vector foundIndices = rttb::masks::VOIindexIdentifier::getIndicesByVoiRegex( appData._struct, appData._structNameRegex); std::vector relevantIndices; if (appData._multipleStructsMode) { relevantIndices = foundIndices; } else { if (!foundIndices.empty()) { relevantIndices.push_back(foundIndices.front()); } } appData._structIndices = relevantIndices; bool strict = !appData._allowSelfIntersection; for (size_t i = 0; i < appData._structIndices.size(); i++) { maskAccessorPtrVector.push_back( boost::make_shared (appData._struct->getStructure(appData._structIndices.at(i)), appData._dose->getGeometricInfo(), strict)); maskAccessorPtrVector.at(i)->updateMask(); appData._structNames.push_back(appData._struct->getStructure(appData._structIndices.at( i))->getLabel()); } } return maskAccessorPtrVector; } rttb::core::DoseIteratorInterface::DoseIteratorPointer rttb::apps::doseTool::generateMaskedDoseIterator( rttb::core::MaskAccessorInterface::MaskAccessorPointer maskAccessorPtr, rttb::core::DoseAccessorInterface::DoseAccessorPointer doseAccessorPtr) { boost::shared_ptr maskedDoseIterator = boost::make_shared(maskAccessorPtr, doseAccessorPtr); rttb::core::DoseIteratorInterface::DoseIteratorPointer doseIterator(maskedDoseIterator); return doseIterator; } rttb::algorithms::DoseStatistics::DoseStatisticsPointer rttb::apps::doseTool::calculateDoseStatistics( core::DoseIteratorInterface::DoseIteratorPointer doseIterator, bool calculateComplexDoseStatistics, DoseTypeGy prescribedDose) { rttb::algorithms::DoseStatisticsCalculator doseStatsCalculator(doseIterator); if (calculateComplexDoseStatistics) { return doseStatsCalculator.calculateDoseStatistics(prescribedDose); } else { return doseStatsCalculator.calculateDoseStatistics(); } } rttb::core::DVH::DVHPointer rttb::apps::doseTool::calculateDVH( core::DoseIteratorInterface::DoseIteratorPointer doseIterator, IDType structUID, IDType doseUID) { rttb::core::DVHCalculator calc(doseIterator, structUID, doseUID); rttb::core::DVH::DVHPointer dvh = calc.generateDVH(); return dvh; } void rttb::apps::doseTool::writeDoseStatisticsFile( rttb::algorithms::DoseStatistics::DoseStatisticsPointer statistics, const std::string& filename, const std::string& structName, rttb::apps::doseTool::ApplicationData& appData) { boost::property_tree::ptree originalTree = rttb::io::other::writeDoseStatistics(statistics); //add config part to xml originalTree.add("statistics.config.requestedStructRegex", appData._structNameRegex); originalTree.add("statistics.config.structName", structName); originalTree.add("statistics.config.doseUID", appData._dose->getUID()); originalTree.add("statistics.config.doseFile", appData._doseFileName); originalTree.add("statistics.config.structFile", appData._structFileName); boost::property_tree::ptree reorderedTree, configTree, resultsTree; configTree = originalTree.get_child("statistics.config"); resultsTree = originalTree.get_child("statistics.results"); reorderedTree.add_child("statistics.config", configTree); reorderedTree.add_child("statistics.results", resultsTree); boost::property_tree::write_xml(filename, reorderedTree, std::locale(), boost::property_tree::xml_writer_make_settings('\t', 1)); } std::string rttb::apps::doseTool::assembleFilenameWithStruct(const std::string& originalFilename, const std::string& structName) { boost::filesystem::path originalFile(originalFilename); std::string newFilename = originalFile.stem().string() + "_" + structName + originalFile.extension().string(); boost::filesystem::path newFile(originalFile.parent_path() / newFilename); return newFile.string(); } void rttb::apps::doseTool::writeDVHFile(core::DVH::DVHPointer dvh, const std::string& filename) { DVHType typeCum = { DVHType::Cumulative }; rttb::io::other::DVHXMLFileWriter dvhWriter(filename, typeCum); dvhWriter.writeDVH(dvh); } diff --git a/code/io/other/rttbDoseStatisticsXMLReader.cpp b/code/io/other/rttbDoseStatisticsXMLReader.cpp index 06c61f9..bed370d 100644 --- a/code/io/other/rttbDoseStatisticsXMLReader.cpp +++ b/code/io/other/rttbDoseStatisticsXMLReader.cpp @@ -1,178 +1,178 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 1328 $ (last changed revision) // @date $Date: 2016-04-22 09:50:01 +0200 (Fr, 22 Apr 2016) $ (last change date) // @author $Author: hentsch $ (last changed by) */ #include #include #include "rttbDoseStatisticsXMLReader.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace io { namespace other { typedef boost::shared_ptr DoseStatisticsPtr; DoseStatisticsXMLReader::DoseStatisticsXMLReader(const std::string& filename) : _filename(filename), _newFile(true){ } DoseStatisticsXMLReader::~DoseStatisticsXMLReader(){ } void DoseStatisticsXMLReader::setFilename(const std::string& filename){ _filename = filename; _newFile = true; } DoseStatisticsPtr DoseStatisticsXMLReader::generateDoseStatistic(){ if (_newFile){ this->createDoseStatistic(); } return _doseStatistic; } void DoseStatisticsXMLReader::createDoseStatistic(){ boost::property_tree::ptree pt; // Load the XML file into the property tree. If reading fails // (cannot open file, parse error), an exception is thrown. try { read_xml(_filename, pt); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw rttb::core::InvalidParameterException("DoseStatistics file name invalid: could not open the xml file!"); } // data to fill the parameter for the DoseStatistics std::string name; std::string datum; unsigned int x; std::pair voxelid; std::vector < std::pair> vec; //parameter for the DoseSTatistics double minimum; double maximum; double numVoxels; double volume; double referenceDose = -1; double mean; double stdDeviation; boost::shared_ptr > > minimumVoxelPositions = nullptr; boost::shared_ptr > > maximumVoxelPositions = nullptr; std::map Dx; std::map Vx; std::map MOHx; std::map MOCx; std::map MaxOHx; std::map MinOCx; BOOST_FOREACH(boost::property_tree::ptree::value_type & data, pt.get_child("statistics.results")){ datum = data.second.data(); BOOST_FOREACH(boost::property_tree::ptree::value_type & middel, data.second){ BOOST_FOREACH(boost::property_tree::ptree::value_type & innernode, middel.second){ std::string mia = innernode.first; if (innernode.first == "name"){ name = innernode.second.data(); } else if (innernode.first == "voxelGridID"){ datum.erase(std::remove(datum.begin(), datum.end(), '\t'), datum.end()); datum.erase(std::remove(datum.begin(), datum.end(), '\n'), datum.end()); voxelid.first = boost::lexical_cast(datum); voxelid.second = boost::lexical_cast(innernode.second.data()); vec.push_back(voxelid); } else if (innernode.first == "x"){ x = boost::lexical_cast(innernode.second.data()); } } } // fill with the extracted data if (name == "numberOfVoxels"){ numVoxels = boost::lexical_cast(datum); } else if (name == "volume"){ volume = boost::lexical_cast(datum); } else if (name == "referenceDose"){ referenceDose = boost::lexical_cast(datum); } else if (name == "mean"){ mean = boost::lexical_cast(datum); } else if (name == "standardDeviation"){ stdDeviation = boost::lexical_cast(datum); } else if (name == "minimum"){ minimum = boost::lexical_cast(datum); if (!vec.empty()){ minimumVoxelPositions = boost::make_shared>>(vec); vec.clear(); } } else if (name == "maximum"){ maximum = boost::lexical_cast(datum); if (!vec.empty()){ maximumVoxelPositions = boost::make_shared>>(vec); vec.clear(); } } else if (name == "Dx"){ Dx[boost::lexical_cast(x)*volume / 100] = boost::lexical_cast(datum); } else if (name == "Vx"){ Vx[boost::lexical_cast(x)*referenceDose / 100] = boost::lexical_cast(datum); } else if (name == "MOHx"){ MOHx[boost::lexical_cast(x)*volume / 100] = boost::lexical_cast(datum); } else if (name == "MOCx"){ MOCx[boost::lexical_cast(x)*volume / 100] = boost::lexical_cast(datum); } else if (name == "MaxOHx"){ MaxOHx[boost::lexical_cast(x)*volume / 100] = boost::lexical_cast(datum); } else if (name == "MinOCx"){ MinOCx[boost::lexical_cast(x)*volume / 100] = boost::lexical_cast(datum); } } // make DoseStatistcs _doseStatistic = boost::make_shared( minimum, maximum, mean, stdDeviation, numVoxels, volume, minimumVoxelPositions, maximumVoxelPositions ,Dx, Vx, MOHx, MOCx, MaxOHx, MinOCx, referenceDose); } - }//end namespace other +}//end namespace other }//end namespace io }//end namespace rttb diff --git a/code/io/other/rttbDoseStatisticsXMLReader.h b/code/io/other/rttbDoseStatisticsXMLReader.h index ad16414..681e348 100644 --- a/code/io/other/rttbDoseStatisticsXMLReader.h +++ b/code/io/other/rttbDoseStatisticsXMLReader.h @@ -1,72 +1,71 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 1250 $ (last changed revision) // @date $Date: 2016-02-18 15:25:55 +0100 (Do, 18 Feb 2016) $ (last change date) // @author $Author: zhangl $ (last changed by) */ #ifndef __DOSE_STATISTICS_XML_READER_H #define __DOSE_STATISTICS_XML_READER_H #include "rttbDoseStatistics.h" #include "rttbBaseType.h" /*boost includes*/ #include #include #include #include namespace rttb { namespace io { namespace other { class DoseStatisticsXMLReader{ public: typedef boost::shared_ptr DoseStatisticsPtr; DoseStatisticsXMLReader(const std::string& filename); ~DoseStatisticsXMLReader(); void setFilename(const std::string& filename); /*! @brief Generate a Model, createModel() will be called @return Return new shared pointer of a Model. @exception InvalidParameterException Thrown if _filename invalid */ DoseStatisticsPtr generateDoseStatistic(); private: std::string _filename; bool _newFile; DoseStatisticsPtr _doseStatistic; /*! @brief Create new Model object using the info from model xml file @exception InvalidParameterException Thrown if _filename invalid */ void createDoseStatistic(); }; - } } } #endif diff --git a/code/io/other/rttbDoseStatisticsXMLWriter.cpp b/code/io/other/rttbDoseStatisticsXMLWriter.cpp index 3a56464..1def0f2 100644 --- a/code/io/other/rttbDoseStatisticsXMLWriter.cpp +++ b/code/io/other/rttbDoseStatisticsXMLWriter.cpp @@ -1,288 +1,308 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ +#include "rttbDoseStatisticsXMLWriter.h" + #include -#include +#include -#include "rttbDoseStatisticsXMLWriter.h" #include "rttbInvalidParameterException.h" -#include "rttbDataNotAvailableException.h" +#include "rttbNullPointerException.h" + namespace rttb { namespace io { namespace other { static const std::string xmlattrXTag = ".x"; static const std::string xmlattrNameTag = ".name"; static const std::string statisticsTag = "statistics.results"; static const std::string propertyTag = "property"; static const std::string columnSeparator = "@"; boost::property_tree::ptree writeDoseStatistics(DoseStatisticsPtr aDoseStatistics) { - using boost::property_tree::ptree; ptree pt; + if (aDoseStatistics == nullptr){ + throw core::NullPointerException("dose statistics is nullptr!"); + } + ptree numberOfVoxelsNode = createNodeWithNameAttribute(aDoseStatistics->getNumberOfVoxels(), "numberOfVoxels"); pt.add_child(statisticsTag + "." + propertyTag, numberOfVoxelsNode); ptree volumeNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getVolume()), "volume"); pt.add_child(statisticsTag + "." + propertyTag, volumeNode); ptree referenceNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getReferenceDose()), "referenceDose"); pt.add_child(statisticsTag + "." + propertyTag, referenceNode); ptree minimumNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getMinimum()), "minimum"); auto minimumPositions = aDoseStatistics->getMinimumVoxelPositions(); std::vector >::iterator pairItMin = minimumPositions->begin(); int count = 0; for (; pairItMin != minimumPositions->end() && count < 100; ++pairItMin) //output max. 100 minimum { VoxelGridID voxelID = pairItMin->second; ptree voxelMinPositions; voxelMinPositions.add("voxelGridID", voxelID); minimumNode.add_child("voxel", voxelMinPositions); count++; } pt.add_child(statisticsTag + "." + propertyTag, minimumNode); ptree maximumNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getMaximum()), "maximum"); auto maximumPositions = aDoseStatistics->getMaximumVoxelPositions(); std::vector >::iterator pairItMax = maximumPositions->begin(); count = 0; for (; pairItMax != maximumPositions->end() && count < 100; ++pairItMax) //output max. 100 maximum { VoxelGridID voxelID = pairItMax->second; ptree voxelMaxPositions; voxelMaxPositions.add("voxelGridID", voxelID); maximumNode.add_child("voxel", voxelMaxPositions); count++; } pt.add_child(statisticsTag + "." + propertyTag, maximumNode); ptree meanNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getMean()), "mean"); pt.add_child(statisticsTag + "." + propertyTag, meanNode); ptree sdNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getStdDeviation()), "standardDeviation"); pt.add_child(statisticsTag + "." + propertyTag, sdNode); ptree varianceNode = createNodeWithNameAttribute(static_cast(aDoseStatistics->getVariance()), "variance"); pt.add_child(statisticsTag + "." + propertyTag, varianceNode); double absoluteVolume = aDoseStatistics->getVolume(); double referenceDose = aDoseStatistics->getReferenceDose(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType AllVx = aDoseStatistics->getAllVx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllDx = aDoseStatistics->getAllDx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOHx = aDoseStatistics->getAllMOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOCx = aDoseStatistics->getAllMOCx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMaxOHx = aDoseStatistics->getAllMaxOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMinOCx = aDoseStatistics->getAllMinOCx(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType::iterator vxIt; rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType::iterator it; for (it = AllDx.begin(); it != AllDx.end(); ++it) { - ptree DxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "Dx", - static_cast((*it).first / absoluteVolume * 100)); + ptree DxNode = createNodeWithNameAndXAttribute(static_cast(it->second), "Dx", + round(convertToPercent(it->first, absoluteVolume))); pt.add_child(statisticsTag + "." + propertyTag, DxNode); } for (vxIt = AllVx.begin(); vxIt != AllVx.end(); ++vxIt) { - ptree VxNode = createNodeWithNameAndXAttribute(static_cast((*vxIt).second), "Vx", - static_cast((*vxIt).first / referenceDose * 100)); + ptree VxNode = createNodeWithNameAndXAttribute(static_cast(vxIt->second), "Vx", + round(convertToPercent(vxIt->first, referenceDose))); pt.add_child(statisticsTag + "." + propertyTag, VxNode); } for (it = AllMOHx.begin(); it != AllMOHx.end(); ++it) { - ptree mohxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MOHx", - static_cast((*it).first / absoluteVolume * 100)); + ptree mohxNode = createNodeWithNameAndXAttribute(static_cast(it->second), "MOHx", + round(convertToPercent(it->first, absoluteVolume))); pt.add_child(statisticsTag + "." + propertyTag, mohxNode); } for (it = AllMOCx.begin(); it != AllMOCx.end(); ++it) { - ptree mocxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MOCx", - static_cast((*it).first / absoluteVolume * 100)); + ptree mocxNode = createNodeWithNameAndXAttribute(static_cast(it->second), "MOCx", + round(convertToPercent(it->first, absoluteVolume))); pt.add_child(statisticsTag + "." + propertyTag, mocxNode); } for (it = AllMaxOHx.begin(); it != AllMaxOHx.end(); ++it) { - ptree maxOhxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MaxOHx", - static_cast((*it).first / absoluteVolume * 100)); + ptree maxOhxNode = createNodeWithNameAndXAttribute(static_cast(it->second), "MaxOHx", + round(convertToPercent(it->first, absoluteVolume))); pt.add_child(statisticsTag + "." + propertyTag, maxOhxNode); } for (it = AllMinOCx.begin(); it != AllMinOCx.end(); ++it) { - ptree minOCxNode = createNodeWithNameAndXAttribute(static_cast((*it).second), "MinOCx", - static_cast((*it).first / absoluteVolume * 100)); + ptree minOCxNode = createNodeWithNameAndXAttribute(static_cast(it->second), "MinOCx", + round(convertToPercent(it->first, absoluteVolume))); pt.add_child(statisticsTag + "." + propertyTag, minOCxNode); } return pt; } void writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, FileNameString aFileName) { boost::property_tree::ptree pt = writeDoseStatistics(aDoseStatistics); try { boost::property_tree::xml_parser::write_xml(aFileName, pt, std::locale(), boost::property_tree::xml_writer_make_settings('\t', 1)); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw core::InvalidParameterException("Write xml failed: xml_parser_error!"); } } XMLString writerDoseStatisticsToString(DoseStatisticsPtr aDoseStatistics) { boost::property_tree::ptree pt = writeDoseStatistics(aDoseStatistics); std::stringstream sstr; try { boost::property_tree::xml_parser::write_xml(sstr, pt, boost::property_tree::xml_writer_make_settings('\t', 1)); } catch (boost::property_tree::xml_parser_error& /*e*/) { throw core::InvalidParameterException("Write xml to string failed: xml_parser_error!"); } return sstr.str(); } StatisticsString writerDoseStatisticsToTableString(DoseStatisticsPtr aDoseStatistics) { - + if (aDoseStatistics == nullptr){ + throw core::NullPointerException("dose statistics is nullptr!"); + } std::stringstream sstr; sstr << static_cast(aDoseStatistics->getVolume() * 1000) << columnSeparator; // cm3 to mm3 sstr << static_cast(aDoseStatistics->getMaximum()) << columnSeparator; sstr << static_cast(aDoseStatistics->getMinimum()) << columnSeparator; sstr << static_cast(aDoseStatistics->getMean()) << columnSeparator; sstr << static_cast(aDoseStatistics->getStdDeviation()) << columnSeparator; sstr << static_cast(aDoseStatistics->getVariance()) << columnSeparator; rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType AllVx = aDoseStatistics->getAllVx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllDx = aDoseStatistics->getAllDx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOHx = aDoseStatistics->getAllMOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMOCx = aDoseStatistics->getAllMOCx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMaxOHx = aDoseStatistics->getAllMaxOHx(); rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType AllMinOCx = aDoseStatistics->getAllMinOCx(); rttb::algorithms::DoseStatistics::DoseToVolumeFunctionType::iterator vxIt; rttb::algorithms::DoseStatistics::VolumeToDoseFunctionType::iterator it; for (it = AllDx.begin(); it != AllDx.end(); ++it) { sstr << static_cast((*it).second) << columnSeparator; } for (vxIt = AllVx.begin(); vxIt != AllVx.end(); ++vxIt) { // *1000 because of conversion cm3 to mm3 sstr << static_cast((*vxIt).second * 1000) << columnSeparator; } for (it = AllMOHx.begin(); it != AllMOHx.end(); ++it) { sstr << static_cast((*it).second) << columnSeparator; } for (it = AllMOCx.begin(); it != AllMOCx.end(); ++it) { sstr << static_cast((*it).second) << columnSeparator; } for (it = AllMaxOHx.begin(); it != AllMaxOHx.end(); ++it) { sstr << static_cast((*it).second) << columnSeparator; } for (it = AllMinOCx.begin(); it != AllMinOCx.end(); ++it) { sstr << static_cast((*it).second) << columnSeparator; } return sstr.str(); } boost::property_tree::ptree createNodeWithNameAttribute(DoseTypeGy doseValue, const std::string& attributeName) { boost::property_tree::ptree node; node.put("", doseValue); node.put(xmlattrNameTag, attributeName); return node; } boost::property_tree::ptree createNodeWithNameAndXAttribute(DoseTypeGy doseValue, const std::string& attributeName, int xValue) { boost::property_tree::ptree node; node.put("", doseValue); node.put(xmlattrNameTag, attributeName); node.put(xmlattrXTag, xValue); return node; } + + double convertToPercent(double value, double maximum) + { + return (value / maximum) * 100; + } + + int round(double value) + { + return value < 0.0 ? static_cast(ceil(value - 0.5)) : static_cast(floor(value + 0.5)); + } + + + }//end namespace other }//end namespace io }//end namespace rttb \ No newline at end of file diff --git a/code/io/other/rttbDoseStatisticsXMLWriter.h b/code/io/other/rttbDoseStatisticsXMLWriter.h index 47a2fd9..c300306 100644 --- a/code/io/other/rttbDoseStatisticsXMLWriter.h +++ b/code/io/other/rttbDoseStatisticsXMLWriter.h @@ -1,105 +1,104 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DOSE_STATISTICS_XML_WRITER_H #define __DOSE_STATISTICS_XML_WRITER_H - #include "rttbDoseStatistics.h" -#include "rttbBaseType.h" -/*boost includes*/ #include -#include -#include #include #include "RTTBOtherIOExports.h" namespace rttb { namespace io { namespace other { typedef boost::shared_ptr DoseStatisticsPtr; - typedef rttb::algorithms::DoseStatistics::ResultListPointer ResultListPointer; /*! @brief Write statistics to boost::property_tree::ptree. @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error @note The precision is float */ boost::property_tree::ptree RTTBOtherIO_EXPORT writeDoseStatistics(DoseStatisticsPtr aDoseStatistics); /*! @brief Write statistics to String. @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error @note The precision is float */ XMLString RTTBOtherIO_EXPORT writerDoseStatisticsToString(DoseStatisticsPtr aDoseStatistics); /*! @brief Write statistics to xml file, including numberOfVoxels, volume, minimum, maximum, mean, standard deviation, variance, D2,D5,D10,D90,D95,D98, V2,V5,V10,V90,V95,V98, MOH2,MOH5,MOH10, MOC2,MOC5,MOC10, MaxOH2,MaxOH5,MaxOH10, MinOC2,MinOC5,MinOC10. @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @param aFileName To write dose statistics @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error @note The precision is float */ void RTTBOtherIO_EXPORT writeDoseStatistics(DoseStatisticsPtr aDoseStatistics, FileNameString aFileName); boost::property_tree::ptree RTTBOtherIO_EXPORT createNodeWithNameAttribute(DoseTypeGy doseValue, const std::string& attributeName); boost::property_tree::ptree RTTBOtherIO_EXPORT createNodeWithNameAndXAttribute(DoseTypeGy doseValue, const std::string& attributeName, int xValue); /*! @brief Write statistics to String to generate a table: "Volume mm3@Max@Min@Mean@Std.Dev.@Variance@D2@D5@D10@D90@D95@D98@V2@V5@V10@V90@V95@V98@MOH2@MOH5@MOH10@MOC2@MOC5@MOC10@MaxOH2@MaxOH5@MaxOH10@MinOC2@MinOC5@MinOC10" @param aReferenceDose A reference dose for the calculation of Vx @param aDoseStatistics DoseStatistics to write @pre aReferenceDose must >0 @exception InvalidParameterException Thrown if aReferenceDose<=0 or xml_parse_error @note is used for the Mevislab-Linking of RTTB @note The precision is float */ StatisticsString RTTBOtherIO_EXPORT writerDoseStatisticsToTableString(DoseStatisticsPtr aDoseStatistics); + + double RTTBOtherIO_EXPORT convertToPercent(double value, double maximum); + /*! @brief rounds a value to the next int (since std::round is available only in C++11) + */ + int RTTBOtherIO_EXPORT round(double value); } } } #endif diff --git a/testing/io/other/DoseStatisticsIOTest.cpp b/testing/io/other/DoseStatisticsIOTest.cpp index 5364cc1..2f5befa 100644 --- a/testing/io/other/DoseStatisticsIOTest.cpp +++ b/testing/io/other/DoseStatisticsIOTest.cpp @@ -1,123 +1,135 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html [^] // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #include #include -#include -#include +#include #include "litCheckMacros.h" -#include "rttbBaseType.h" #include "rttbDoseStatistics.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbDoseStatisticsXMLWriter.h" #include "rttbDoseStatisticsXMLReader.h" #include "rttbGenericDoseIterator.h" #include "rttbDoseIteratorInterface.h" #include "rttbInvalidParameterException.h" #include "rttbNullPointerException.h" #include "../../core/DummyDoseAccessor.h" #include "CompareDoseStatistic.h" namespace rttb { namespace testing { - /*! @brief OtherIOTest - test the IO for dose statistics - 1) test exception - 2) test writing statistics to xml file + /*! @brief DoseStatisticsIOTest - test the DoseStatisticsIO for dose statistics + 1) test writing statistics to xml file + 2) test reading statistics from XML file and compare DoseStatistics + 3) test writing statistics to string */ int DoseStatisticsIOTest(int argc, char* argv[]) { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; typedef boost::shared_ptr DoseStatisticsCalculatorPtr; PREPARE_DEFAULT_TEST_REPORTING; /* generate dummy dose */ boost::shared_ptr spTestDoseAccessor = boost::make_shared(); DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); boost::shared_ptr spTestDoseIterator = boost::make_shared(spDoseAccessor); DoseIteratorPointer spDoseIterator(spTestDoseIterator); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator); auto myDoseStatsSimple = myDoseStatsCalculator.calculateDoseStatistics(); auto myDoseStatsComplex = myDoseStatsCalculator.calculateDoseStatistics(true); - /* test writing statistics to xml file */ + //1) test writing statistics to xml file + CHECK_THROW_EXPLICIT(io::other::writeDoseStatistics(nullptr, "aFilename.txt"), core::NullPointerException); + FileNameString filenameSimple = "testStatisticsSimple.xml"; CHECK_NO_THROW(io::other::writeDoseStatistics(myDoseStatsSimple, filenameSimple)); + CHECK(boost::filesystem::exists(filenameSimple)); + + FileNameString filenameComplex = "testStatisticsComplex.xml"; + CHECK_NO_THROW(io::other::writeDoseStatistics(myDoseStatsComplex, filenameComplex)); + CHECK(boost::filesystem::exists(filenameComplex)); + + //2) test reading statistics from XML file and compare DoseStatistics io::other::DoseStatisticsXMLReader readerSimple= io::other::DoseStatisticsXMLReader(filenameSimple); boost::shared_ptr rereadSimplyDose; CHECK_NO_THROW(rereadSimplyDose = readerSimple.generateDoseStatistic()); CHECK(checkEqualDoseStatistic(myDoseStatsSimple, rereadSimplyDose)); - FileNameString filenameComplex = "testStatisticsComplex.xml"; - CHECK_NO_THROW(io::other::writeDoseStatistics(myDoseStatsComplex, filenameComplex)); - io::other::DoseStatisticsXMLReader readerComplex = io::other::DoseStatisticsXMLReader(filenameComplex); boost::shared_ptr rereadComplexDose; CHECK_NO_THROW(rereadComplexDose = readerComplex.generateDoseStatistic()); CHECK(checkEqualDoseStatistic(myDoseStatsComplex, rereadComplexDose)); + io::other::DoseStatisticsXMLReader readerInvalidFilename = io::other::DoseStatisticsXMLReader("invalidFilename.xml"); + CHECK_THROW_EXPLICIT(readerInvalidFilename.generateDoseStatistic(), core::InvalidParameterException); + //delete files again + CHECK_EQUAL(std::remove(filenameSimple.c_str()), 0); + CHECK_EQUAL(std::remove(filenameComplex.c_str()),0); - /* test writing statistics to string */ + //3) test writing statistics to string boost::property_tree::ptree ptSimple = io::other::writeDoseStatistics(myDoseStatsSimple); XMLString strSimple = io::other::writerDoseStatisticsToString(myDoseStatsSimple); + CHECK_THROW_EXPLICIT(io::other::writerDoseStatisticsToString(nullptr), core::NullPointerException); + boost::property_tree::ptree ptComplex = io::other::writeDoseStatistics(myDoseStatsComplex); XMLString strComplex = io::other::writerDoseStatisticsToString(myDoseStatsComplex); std::stringstream sstrSimple; boost::property_tree::xml_parser::write_xml(sstrSimple, ptSimple, boost::property_tree::xml_writer_make_settings('\t', 1)); CHECK_EQUAL(strSimple, sstrSimple.str()); std::stringstream sstrComplex; boost::property_tree::xml_parser::write_xml(sstrComplex, ptComplex, boost::property_tree::xml_writer_make_settings('\t', 1)); CHECK_EQUAL(strComplex, sstrComplex.str()); RETURN_AND_REPORT_TEST_SUCCESS; } }//testing }//rttb diff --git a/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz b/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz new file mode 100644 index 0000000..01dffb5 Binary files /dev/null and b/utilities/ArgumentParsingLib/ArgumentParsingLib.tar.gz differ