diff --git a/apps/DoseTool/DoseTool.cpp b/apps/DoseTool/DoseTool.cpp index 24d8b73..a12b793 100644 --- a/apps/DoseTool/DoseTool.cpp +++ b/apps/DoseTool/DoseTool.cpp @@ -1,169 +1,170 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 1315 $ (last changed revision) // @date $Date: 2016-04-12 14:18:24 +0200 (Di, 12 Apr 2016) $ (last change date) // @author $Author: floca $ (last changed by) */ #include "DoseToolApplicationData.h" #include "DoseToolHelper.h" #include "DoseToolCmdLineParser.h" #include "boost/shared_ptr.hpp" #include "boost/make_shared.hpp" #include "RTToolboxConfigure.h" #include "rttbException.h" #include "rttbDoseLoader.cpp" +#include "rttbStructLoader.cpp" /*! \file DoseTool.cpp main function for DoseTool */ int main(int argc, const char** argv) { int result = 0; rttb::apps::doseTool::ApplicationData appData; boost::shared_ptr argParser; const std::string appCategory = "RT-Toolbox App"; const std::string appName = "DoseTool"; const std::string appDesc = "An App to calculate the dose statistics and compute the DVH. The GUI for this app is currently under development and in an experimental state."; const std::string appContributor = "SIDT@DKFZ"; const std::string appVersion = RTTB_FULL_VERSION_STRING; 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::doseTool::populateAppData(argParser, appData); std::cout << std::endl << "*******************************************" << std::endl; std::cout << "Dose file: " << appData._doseFileName << std::endl; std::cout << "Struct file: " << appData._structFileName << std::endl; std::cout << "Struct name: " << appData._structNameRegex << std::endl; if (appData._computeDoseStatistics) { std::cout << "Dose statistic output file: " << appData._doseStatisticOutputFileName << std::endl; std::cout << "Compute complex statistics: " << appData._computeComplexDoseStatistics << std::endl; if (appData._computeComplexDoseStatistics) { std::cout << "Prescribed dose: " << appData._prescribedDose << std::endl; } std::cout << "Allow self intersections: " << appData._allowSelfIntersection << std::endl; } if (appData._computeDVH) { std::cout << "DVH output file: " << appData._dvhOutputFilename << std::endl; } std::cout << std::endl << "read dose file... "; try { appData._dose = rttb::io::utils::loadDose(appData._doseFileName, appData._doseLoadStyle); std::cout << "done." << std::endl; } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 1; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 1; } catch (...) { std::cerr << "Error!!! unknown error while reading dose image." << std::endl; return 1; } //loading of structure file not necessary in ITK case as it can be used directly as mask input. if (appData._structLoadStyle.front() != "itk") { try { - appData._struct = rttb::apps::doseTool::loadStruct(appData._structFileName, + appData._struct = rttb::io::utils::loadStruct(appData._structFileName, appData._structLoadStyle, appData._structNameRegex); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (const std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 2; } catch (...) { std::cerr << "Error!!! unknown error while reading struct image." << std::endl; return 2; } } try { rttb::apps::doseTool::processData(appData); } catch (rttb::core::Exception& e) { std::cerr << "RTTB Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 3; } catch (std::exception& e) { std::cerr << "Error!!!" << std::endl; std::cerr << e.what() << std::endl; return 3; } catch (...) { std::cerr << "Error!!! unknown error while processing the data or writing the image." << std::endl; return 3; } return result; } diff --git a/apps/DoseTool/DoseToolHelper.cpp b/apps/DoseTool/DoseToolHelper.cpp index a28ce0d..c57a3ae 100644 --- a/apps/DoseTool/DoseToolHelper.cpp +++ b/apps/DoseTool/DoseToolHelper.cpp @@ -1,295 +1,254 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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 "boost/property_tree/xml_parser.hpp" #include "boost/filesystem.hpp" -#include "rttbExceptionMacros.h" - #include "DoseToolApplicationData.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbITKImageFileMaskAccessorGenerator.h" #include "rttbDoseStatistics.h" #include "rttbDVH.h" #include "rttbDVHCalculator.h" #include "rttbDVHXMLFileWriter.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbBoostMaskAccessor.h" #include "rttbGenericMaskedDoseIterator.h" #include "rttbDoseStatisticsXMLWriter.h" #include "rttbVOIindexIdentifier.h" -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.setStructureLabelFilterActive(true); - generator.setFilterRegEx(structNameRegex); - } - - return generator.generateStructureSet(); -} - - 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()); appData._structNames.push_back(appData._structNameRegex); } else { auto 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 calculateDoseStatistics( rttb::core::DoseIteratorInterface::DoseIteratorPointer doseIterator, bool calculateComplexDoseStatistics, rttb::DoseTypeGy prescribedDose) { rttb::algorithms::DoseStatisticsCalculator doseStatsCalculator(doseIterator); if (calculateComplexDoseStatistics) { return doseStatsCalculator.calculateDoseStatistics(prescribedDose); } else { return doseStatsCalculator.calculateDoseStatistics(); } } rttb::core::DVH::DVHPointer calculateDVH( rttb::core::DoseIteratorInterface::DoseIteratorPointer doseIterator, rttb::IDType structUID, rttb::IDType doseUID) { rttb::core::DVHCalculator calc(doseIterator, structUID, doseUID); rttb::core::DVH::DVHPointer dvh = calc.generateDVH(); return dvh; } 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(); } /*! @brief Writes the dose statistics as XML to a file @details adds a .... part to the RTTB generated xml where the used files and struct names are stored. */ void writeDoseStatisticsFile( 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)); } void writeDVHFile(rttb::core::DVH::DVHPointer dvh, const std::string& filename) { rttb::DVHType typeCum = { rttb::DVHType::Cumulative }; rttb::io::other::DVHXMLFileWriter dvhWriter(filename, typeCum); dvhWriter.writeDVH(dvh); } 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... "; rttb::IDType structUID; rttb::IDType doseUID; //Generate random UID if (appData._structLoadStyle.front() == "itk"){ structUID = "struct42"; doseUID = "dose42"; } else { structUID = appData._struct->getUID(); doseUID = appData._dose->getUID(); } core::DVH::DVHPointer dvh = calculateDVH(spDoseIterator, structUID, doseUID); 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; } } } diff --git a/apps/DoseTool/DoseToolHelper.h b/apps/DoseTool/DoseToolHelper.h index e83095e..09fd25e 100644 --- a/apps/DoseTool/DoseToolHelper.h +++ b/apps/DoseTool/DoseToolHelper.h @@ -1,89 +1,68 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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) */ #ifndef __DOSETOOL_HELPER_H #define __DOSETOOL_HELPER_H #include "rttbDoseAccessorInterface.h" #include "rttbStructureSetGeneratorInterface.h" #include "rttbDoseIteratorInterface.h" #include "rttbMaskAccessorInterface.h" namespace rttb { namespace apps { namespace doseTool { class ApplicationData; - typedef std::vector LoadingStyleArgType; - - /*! @brief loads a struct from a file based on the loadingStyle. - You may pass a structure name regex. If is not empty, it will be used to filter structure in the - loading process. Only structures with a name matching the reg ex will be loaded. This speeds up the - loading process significantly if you need only one structure out of a structure set. - @exception Throws an rttb::Exception if loading fails - @details voxelized itk images are read in generateMask() directly - */ - core::StructureSetGeneratorInterface::StructureSetPointer loadStruct(const std::string& fileName, - const LoadingStyleArgType& args, const std::string& structNameRegex = ""); - - /*! @brief loads a dicom struct from a file. - You may pass a structure name regex. If is not empty, it will be used to filter structure in the - loading process. Only structures with a name matching the reg ex will be loaded. This speeds up the - loading process significantly if you need only one structure out of a structure set. - @exception Throws an rttb::Exception if loading fails - @sa DicomFileStructureSetGenerator - */ - core::StructureSetGeneratorInterface::StructureSetPointer loadDicomStruct( - const std::string& fileName, const std::string& structNameRegex = ""); /*! @brief Contains the business logic of processing all information to calculate the dose statistics and writing them to an xml file. @details Uses appData for the input data and the correct configuration. */ void processData(ApplicationData& appData); /*! @brief Generates a mask from the struct file by using the boostAccessor. In case of itk image, it directly loads the voxelized image. */ std::vector generateMasks( ApplicationData& appData); /*algorithms::DoseStatistics::DoseStatisticsPointer calculateDoseStatistics( core::DoseIteratorInterface::DoseIteratorPointer doseIterator, bool calculateComplexDoseStatistics, DoseTypeGy prescribedDose); core::DVH::DVHPointer calculateDVH(core::DoseIteratorInterface::DoseIteratorPointer doseIterator, IDType structUID, IDType doseUID);*/ core::DoseIteratorInterface::DoseIteratorPointer generateMaskedDoseIterator( core::MaskAccessorInterface::MaskAccessorPointer maskAccessorPtr, core::DoseAccessorInterface::DoseAccessorPointer doseAccessorPtr); std::string assembleFilenameWithStruct(const std::string& originalFilename, const std::string& structName); } } } #endif diff --git a/code/io/utils/files.cmake b/code/io/utils/files.cmake index e5632e4..df811f2 100644 --- a/code/io/utils/files.cmake +++ b/code/io/utils/files.cmake @@ -1,6 +1,7 @@ SET(CPP_FILES rttbDoseLoader.cpp + rttbStructLoader.cpp ) SET(H_FILES ) diff --git a/code/io/utils/rttbStructLoader.cpp b/code/io/utils/rttbStructLoader.cpp new file mode 100644 index 0000000..8d55856 --- /dev/null +++ b/code/io/utils/rttbStructLoader.cpp @@ -0,0 +1,86 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 1674 $ (last changed revision) +// @date $Date: 2017-01-27 10:34:46 +0100 (Fr, 27 Jan 2017) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + +#ifndef __RTTB_STRUCT_LOADER_H +#define __RTTB_STRUCT_LOADER_H + +#include "rttbDicomFileStructureSetGenerator.h" + +#include "rttbExceptionMacros.h" + +namespace rttb +{ + namespace io + { + namespace utils + { + typedef std::vector LoadingStyleArgType; + + /*! @brief loads a dicom struct from a file. + You may pass a structure name regex. If is not empty, it will be used to filter structure in the + loading process. Only structures with a name matching the reg ex will be loaded. This speeds up the + loading process significantly if you need only one structure out of a structure set. + @exception Throws an rttb::Exception if loading fails + @sa DicomFileStructureSetGenerator + */ + rttb::core::StructureSetGeneratorInterface::StructureSetPointer + loadDicomStruct( + const std::string& fileName, const std::string& structNameRegex = "") + { + rttb::io::dicom::DicomFileStructureSetGenerator generator(fileName); + + if (!structNameRegex.empty()) + { + generator.setStructureLabelFilterActive(true); + generator.setFilterRegEx(structNameRegex); + } + + return generator.generateStructureSet(); + } + + /*! @brief loads a struct from a file based on the loadingStyle. + You may pass a structure name regex. If is not empty, it will be used to filter structure in the + loading process. Only structures with a name matching the reg ex will be loaded. This speeds up the + loading process significantly if you need only one structure out of a structure set. + @exception Throws an rttb::Exception if loading fails + @details voxelized itk images are read in generateMask() directly + */ + rttb::core::StructureSetGeneratorInterface::StructureSetPointer loadStruct( + const std::string& fileName, const LoadingStyleArgType& args, const std::string& structNameRegex = "") + { + rttb::core::StructureSetGeneratorInterface::StructureSetPointer result; + + if (args.empty() || args[0] == "dicom") + { + result = rttb::io::utils::loadDicomStruct(fileName, structNameRegex); + } + else + { + rttbDefaultExceptionStaticMacro(<< "Unknown io style selected. Cannot load data. Selected style: " + << args[0]); + } + + return result; + } + } + } +} +#endif