diff --git a/code/algorithms/rttbDoseStatisticsCalculator.cpp b/code/algorithms/rttbDoseStatisticsCalculator.cpp index e259464..013999c 100644 --- a/code/algorithms/rttbDoseStatisticsCalculator.cpp +++ b/code/algorithms/rttbDoseStatisticsCalculator.cpp @@ -1,409 +1,421 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. 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 "rttbDoseStatisticsCalculator.h" #include #include #include #include #include "rttbNullPointerException.h" #include "rttbInvalidDoseException.h" #include "rttbInvalidParameterException.h" #include namespace rttb { namespace algorithms { DoseStatisticsCalculator::DoseStatisticsCalculator(DoseIteratorPointer aDoseIterator) { if (aDoseIterator == NULL) { throw core::NullPointerException("DoseIterator must not be NULL"); } else { _doseIterator = aDoseIterator; } _simpleDoseStatisticsCalculated = false; _complexDoseStatisticsCalculated = false; _multiThreading = false; _mutex = ::boost::make_shared<::boost::shared_mutex>(); } DoseStatisticsCalculator::~DoseStatisticsCalculator() { } DoseStatisticsCalculator::DoseIteratorPointer DoseStatisticsCalculator::getDoseIterator() const { return _doseIterator; } DoseStatisticsCalculator::DoseStatisticsPointer DoseStatisticsCalculator::calculateDoseStatistics( bool computeComplexMeasures, unsigned int maxNumberMinimaPositions, unsigned int maxNumberMaximaPositions) { if (!_doseIterator) { throw core::NullPointerException("_doseIterator must not be NULL!"); } //"simple" dose statistics are mandatory calculateSimpleDoseStatistics(maxNumberMinimaPositions, maxNumberMaximaPositions); if (computeComplexMeasures) { //more complex dose statistics are optional with default maximum dose and default relative x values calculateComplexDoseStatistics(_statistics->getMaximum(), std::vector(), std::vector()); } return _statistics; } DoseStatisticsCalculator::DoseStatisticsPointer DoseStatisticsCalculator::calculateDoseStatistics( DoseTypeGy referenceDose, unsigned int maxNumberMinimaPositions, unsigned int maxNumberMaximaPositions) { if (!_doseIterator) { throw core::NullPointerException("_doseIterator must not be NULL!"); } if (referenceDose <= 0) { throw rttb::core::InvalidParameterException("Reference dose must be > 0 !"); } //simple dose statistics calculateSimpleDoseStatistics(maxNumberMinimaPositions, maxNumberMaximaPositions); //more complex dose statistics with given reference dose and default x values calculateComplexDoseStatistics(referenceDose, std::vector(), std::vector()); return _statistics; } DoseStatisticsCalculator::DoseStatisticsPointer DoseStatisticsCalculator::calculateDoseStatistics( const std::vector& precomputeDoseValues, const std::vector& precomputeVolumeValues, DoseTypeGy referenceDose, unsigned int maxNumberMinimaPositions, unsigned int maxNumberMaximaPositions) { if (!_doseIterator) { throw core::NullPointerException("_doseIterator must not be NULL!"); } //"simple" dose statistics calculateSimpleDoseStatistics(maxNumberMinimaPositions, maxNumberMaximaPositions); if (referenceDose <= 0) { //more complex dose statistics with default maximum dose and relative x values calculateComplexDoseStatistics(_statistics->getMaximum(), precomputeDoseValues, precomputeVolumeValues); } else { //more complex dose statistics with given reference dose and relative x values calculateComplexDoseStatistics(referenceDose, precomputeDoseValues, precomputeVolumeValues); } return _statistics; } void DoseStatisticsCalculator::calculateSimpleDoseStatistics(unsigned int maxNumberMinimaPositions, unsigned int maxNumberMaximaPositions) { _doseVector.clear(); _voxelProportionVector.clear(); std::multimap doseValueVSIndexMap; std::vector voxelProportionVectorTemp; DoseStatisticType maximumDose = 0; DoseStatisticType minimumDose = std::numeric_limits::max(); DoseStatisticType meanDose; DoseStatisticType stdDeviationDose; DoseTypeGy sum = 0; VolumeType numVoxels = 0.0; DoseTypeGy squareSum = 0; VolumeType volume = 0; _doseIterator->reset(); int i = 0; DoseTypeGy doseValue = 0; while (_doseIterator->isPositionValid()) { doseValue = _doseIterator->getCurrentDoseValue(); if (i == 0) { minimumDose = doseValue; volume = _doseIterator->getCurrentVoxelVolume(); } rttb::FractionType voxelProportion = _doseIterator->getCurrentRelevantVolumeFraction(); sum += doseValue * voxelProportion; numVoxels += voxelProportion; squareSum += doseValue * doseValue * voxelProportion; if (doseValue > maximumDose) { maximumDose = doseValue; } else if (doseValue < minimumDose) { minimumDose = doseValue; } voxelProportionVectorTemp.push_back(voxelProportion); doseValueVSIndexMap.insert(std::pair(doseValue, i)); i++; _doseIterator->next(); } if (numVoxels != 0) { meanDose = sum / numVoxels; //standard deviation is defined only for n>=2 if (numVoxels >= 2) { //uncorrected variance is calculated DoseStatisticType varianceDose = (squareSum / numVoxels - meanDose * meanDose); if (varianceDose < errorConstant) { stdDeviationDose = 0; } else { stdDeviationDose = pow(varianceDose, 0.5); } } else { stdDeviationDose = 0; } } //sort dose values and corresponding volume fractions in member variables for (auto it = doseValueVSIndexMap.begin(); it != doseValueVSIndexMap.end(); ++it) { _doseVector.push_back((float)(*it).first); _voxelProportionVector.push_back(voxelProportionVectorTemp.at((*it).second)); } volume *= numVoxels; _statistics = boost::make_shared(minimumDose, maximumDose, meanDose, stdDeviationDose, numVoxels, volume); _simpleDoseStatisticsCalculated = true; ResultListPointer minimumVoxelPositions = computeMinimumPositions(maxNumberMinimaPositions); ResultListPointer maximumVoxelPositions = computeMaximumPositions(maxNumberMaximaPositions); _statistics->setMinimumVoxelPositions(minimumVoxelPositions); _statistics->setMaximumVoxelPositions(maximumVoxelPositions); } void DoseStatisticsCalculator::calculateComplexDoseStatistics(DoseTypeGy referenceDose, const std::vector& precomputeDoseValues, const std::vector& precomputeVolumeValues) { if (!_simpleDoseStatisticsCalculated) { throw core::InvalidDoseException("simple DoseStatistics have to be computed in order to call calculateComplexDoseStatistics()"); } std::vector precomputeDoseValuesNonConst = precomputeDoseValues; std::vector precomputeVolumeValuesNonConst = precomputeVolumeValues; //set default values if (precomputeDoseValues.empty()) { std::vector defaultPrecomputeDoseValues = boost::assign::list_of(0.02)(0.05)(0.1)(0.9)( 0.95)(0.98); precomputeDoseValuesNonConst = defaultPrecomputeDoseValues; } if (precomputeVolumeValues.empty()) { std::vector defaultPrecomputeVolumeValues = boost::assign::list_of(0.02)(0.05)(0.1)(0.9)( 0.95)(0.98); precomputeVolumeValuesNonConst = defaultPrecomputeVolumeValues; } _Vx = ::boost::make_shared(precomputeDoseValuesNonConst, referenceDose, _doseIterator); _Vx->compute(); _Dx = ::boost::make_shared(precomputeVolumeValuesNonConst, _statistics->getVolume(), this->_doseVector, this->_voxelProportionVector, this->_doseIterator->getCurrentVoxelVolume(), _statistics->getMinimum()); _Dx->compute(); _MOHx = ::boost::make_shared(precomputeVolumeValuesNonConst, _statistics->getVolume(), this->_doseVector, this->_voxelProportionVector, this->_doseIterator->getCurrentVoxelVolume()); _MOHx->compute(); _MOCx = ::boost::make_shared(precomputeVolumeValuesNonConst, _statistics->getVolume(), this->_doseVector, this->_voxelProportionVector, this->_doseIterator->getCurrentVoxelVolume()); _MOCx->compute(); _MaxOHx = ::boost::make_shared(precomputeVolumeValuesNonConst, _statistics->getVolume(), this->_doseVector, this->_voxelProportionVector, this->_doseIterator->getCurrentVoxelVolume()); _MaxOHx->compute(); _MinOCx = ::boost::make_shared(precomputeVolumeValuesNonConst, _statistics->getVolume(), this->_doseVector, this->_voxelProportionVector, this->_doseIterator->getCurrentVoxelVolume(), _statistics->getMinimum(), _statistics->getMaximum()); _MinOCx->compute(); _statistics->setVx(_Vx->getMeasureCollection()); _statistics->setDx(_Dx->getMeasureCollection()); _statistics->setMOHx(_MOHx->getMeasureCollection()); _statistics->setMOCx(_MOCx->getMeasureCollection()); _statistics->setMaxOHx(_MaxOHx->getMeasureCollection()); _statistics->setMinOCx(_MinOCx->getMeasureCollection()); _statistics->setReferenceDose(referenceDose); _complexDoseStatisticsCalculated = true; } - void DoseStatisticsCalculator::calculateAdditionalComplexDoseStatisticMeasures(const std::vector& values) + void DoseStatisticsCalculator::addPrecomputeValues(const std::vector& values) { if (!_complexDoseStatisticsCalculated) { - throw core::InvalidDoseException("Complex DoseStatistics have to be computed in order to call calculateAdditionalComplexDoseStatisticMeasures()"); + throw core::InvalidDoseException("Complex DoseStatistics have to be computed in order to call addPrecomputeDoseValues()"); } - _Vx->computeAdditionalValues(values); - _Dx->computeAdditionalValues(values); - _MOHx->computeAdditionalValues(values); - _MOCx->computeAdditionalValues(values); - _MaxOHx->computeAdditionalValues(values); - _MinOCx->computeAdditionalValues(values); + _Vx->addPrecomputeDoseValues(values); + _Dx->addPrecomputeVolumeValues(values); + _MOHx->addPrecomputeVolumeValues(values); + _MOCx->addPrecomputeVolumeValues(values); + _MaxOHx->addPrecomputeVolumeValues(values); + _MinOCx->addPrecomputeVolumeValues(values); + } + void DoseStatisticsCalculator::recalculateDoseStatistics() + { + if (!_complexDoseStatisticsCalculated) + { + throw core::InvalidDoseException("Complex DoseStatistics have to be computed in order to call recalculateDoseStatistics()"); + } + _Vx->compute(); + _Dx->compute(); + _MOHx->compute(); + _MOCx->compute(); + _MaxOHx->compute(); + _MinOCx->compute(); } - DoseStatisticsCalculator::ResultListPointer DoseStatisticsCalculator::computeMaximumPositions( unsigned int maxNumberMaxima) const { if (!_simpleDoseStatisticsCalculated) { throw core::InvalidDoseException("simple DoseStatistics have to be computed in order to call computeMaximumPositions()"); } ResultListPointer maxVoxelVector = boost::make_shared > >(); unsigned int count = 0; this->_doseIterator->reset(); DoseTypeGy doseValue = 0; while (_doseIterator->isPositionValid() && count < maxNumberMaxima) { doseValue = _doseIterator->getCurrentDoseValue(); if (doseValue == _statistics->getMaximum()) { VoxelGridID currentID = _doseIterator->getCurrentVoxelGridID(); std::pair voxel(doseValue, currentID); maxVoxelVector->push_back(voxel); count++; } _doseIterator->next(); } return maxVoxelVector; } DoseStatisticsCalculator::ResultListPointer DoseStatisticsCalculator::computeMinimumPositions( unsigned int maxNumberMinima) const { if (!_simpleDoseStatisticsCalculated) { throw core::InvalidDoseException("simple DoseStatistics have to be computed in order to call computeMinimumPositions()"); } ResultListPointer minVoxelVector = boost::make_shared > >(); /*! @todo: Architecture Annotation: Finding the positions for the minimum only once reduces computation time, but will require sensible use by the programmers. To be save the output vector minVoxelVector will be always cleared here to garantee that no false values are presented. This change may be revoced to increase computation speed later on (only compute if(minVoxelVector->size()==0)). */ unsigned int count = 0; this->_doseIterator->reset(); DoseTypeGy doseValue = 0; while (_doseIterator->isPositionValid() && count < maxNumberMinima) { doseValue = _doseIterator->getCurrentDoseValue(); if (doseValue == _statistics->getMinimum()) { VoxelGridID currentID = _doseIterator->getCurrentVoxelGridID(); std::pair voxel(doseValue, currentID); minVoxelVector->push_back(voxel); count++; } _doseIterator->next(); } return minVoxelVector; } void DoseStatisticsCalculator::setMultiThreading(const bool choice) { _multiThreading = choice; } }//end namespace algorithms }//end namespace rttb diff --git a/code/algorithms/rttbDoseStatisticsCalculator.h b/code/algorithms/rttbDoseStatisticsCalculator.h index 2bf38c9..138fc61 100644 --- a/code/algorithms/rttbDoseStatisticsCalculator.h +++ b/code/algorithms/rttbDoseStatisticsCalculator.h @@ -1,194 +1,199 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. 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_CALCULATOR_H #define __DOSE_STATISTICS_CALCULATOR_H #include #include #include #include "rttbDoseIteratorInterface.h" #include "rttbDoseStatistics.h" #include "RTTBAlgorithmsExports.h" #include "rttbDxVolumeToDoseMeasureCollectionCalculator.h" #include "rttbVxDoseToVolumeMeasureCollectionCalculator.h" #include "rttbMOHxVolumeToDoseMeasureCollectionCalculator.h" #include "rttbMOCxVolumeToDoseMeasureCollectionCalculator.h" #include "rttbMaxOHxVolumeToDoseMeasureCollectionCalculator.h" #include "rttbMinOCxVolumeToDoseMeasureCollectionCalculator.h" namespace rttb { namespace algorithms { /*! @class DoseStatisticsCalculator @brief Class for calculating different statistical values from a RT dose distribution @details These values range from standard statistical values such as minimum, maximum and mean to more complex dose specific measures such as Vx (volume irradiated with a dose >=x), Dx (minimal dose delivered to x% of the VOI) or MOHx (mean in the hottest volume). For a complete list, see calculateDoseStatistics(). @note the complex dose statistics are precomputed and cannot be computed "on the fly" lateron! The doses/volumes that should be used for precomputation have to be set in calculateDoseStatistics() */ class RTTBAlgorithms_EXPORT DoseStatisticsCalculator { public: typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; typedef DoseStatistics::ResultListPointer ResultListPointer; typedef DoseStatistics::DoseStatisticsPointer DoseStatisticsPointer; private: DoseIteratorPointer _doseIterator; /*! @brief Contains relevant dose values sorted in descending order. */ std::vector _doseVector; /*! @brief Contains the corresponding voxel proportions to the values in doseVector. */ std::vector _voxelProportionVector; /*! @brief The doseStatistics are stored here. */ DoseStatisticsPointer _statistics; bool _simpleDoseStatisticsCalculated; bool _complexDoseStatisticsCalculated; bool _multiThreading; ::boost::shared_ptr _mutex; ::boost::shared_ptr _Vx; ::boost::shared_ptr _Dx; ::boost::shared_ptr _MOHx; ::boost::shared_ptr _MOCx; ::boost::shared_ptr _MaxOHx; ::boost::shared_ptr _MinOCx; /*! @brief Calculates the positions where the dose has its maximum @param maxNumberMaximaPositions the maximal amount of computed positions @pre maximumDose must be defined in _statistics with the correct value */ ResultListPointer computeMaximumPositions(unsigned int maxNumberMaximaPositions) const; /*! @brief Calculates the positions where the dose has its minimum @param maxNumberMinimaPositions the maximal amount of computed positions (they are read sequentially using the iterator until maxNumberMinimaPositions have been read, other positions are not considered) @pre minimumDose must be defined in _statistics with the correct value */ ResultListPointer computeMinimumPositions(unsigned int maxNumberMinimaPositions) const; /*! @brief Calculates simple dose statistics (min, mean, max, stdDev, minDosePositions, maxDosePositions) @param maxNumberMinimaPositions the maximal amount of computed positions where the dose has its minimum that is computed @param maxNumberMaximaPositions the maximal amount of computed positions where the dose has its maximum that is computed */ void calculateSimpleDoseStatistics(unsigned int maxNumberMinimaPositions, unsigned int maxNumberMaximaPositions); /*! @brief Calculates complex dose statistics (Dx, Vx, MOHx, MOCx, MaxOHx, MinOCx) @warning computations can take quite long (>1 min) for large structures as many statistics are precomputed */ void calculateComplexDoseStatistics(DoseTypeGy referenceDose, const std::vector& precomputeDoseValues, const std::vector& precomputeVolumeValues); public: ~DoseStatisticsCalculator(); /*! @brief Constructor @param aDoseIterator the dose to be analyzed */ DoseStatisticsCalculator(DoseIteratorPointer aDoseIterator); DoseIteratorPointer getDoseIterator() const; /*! @brief Compute simple or complex dose statistics with default relative x values and the maximum dose as default reference dose (for Vx computation) @details The following statistics are calculated always (i.e. also if computeComplexMeasures=false):
  • minimum dose
  • mean dose
  • maximum dose
  • standard deviation dose
  • voxel positions of minimum dose
  • voxel positions of maximum dose
Additionally, these statistics are computed if computeComplexMeasures=true:
  • Dx (the minimal dose delivered to a volume >= x)
  • Vx (the volume irradiated with a dose >= x)
  • MOHx (mean dose of the hottest x volume)
  • MOCx (mean dose of the coldest x volume)
  • MaxOHx (Maximum outside of the hottest x volume)
  • MinOCx (Minimum outside of the coldest x volume)
Default x values for Vx are 0.02, 0.05, 0.1, 0.9, 0.95 and 0.98, with respect to maxDose. Default x values for Dx, MOHx, MOCx, MaxOHx and MinOCx are 0.02, 0.05, 0.1, 0.9, 0.95 and 0.98, with respect to volume. @param computeComplexMeasures should complex statistics be calculated? If it is true, the complex dose statistics will be calculated with default relative x values and the maximum dose as reference dose @param maxNumberMinimaPositions the maximal amount of computed positions where the dose has its minimum that is computed @param maxNumberMaximaPositions the maximal amount of computed positions where the dose has its maximum that is computed @warning If computeComplexMeasures==true, computations can take quite long (>1 min) for large structures as many statistics are precomputed @note The complex dose statistics are precomputed and cannot be computed "on the fly" lateron! Only the default x values can be requested in DoseStatistics! */ DoseStatisticsPointer calculateDoseStatistics(bool computeComplexMeasures = false, unsigned int maxNumberMinimaPositions = 10, unsigned int maxNumberMaximaPositions = 10); /*! @brief Compute complex dose statistics with given reference dose and default relative x values @param referenceDose the reference dose to compute Vx, normally it should be the prescribed dose @param maxNumberMinimaPositions the maximal amount of computed positions where the dose has its minimum that is computed @param maxNumberMaximaPositions the maximal amount of computed positions where the dose has its maximum that is computed @exception InvalidParameterException thrown if referenceDose <= 0 @warning Computations can take quite long (>1 min) for large structures as many statistics are precomputed @note The complex dose statistics are precomputed and cannot be computed "on the fly" lateron! Only the default x values can be requested in DoseStatistics! */ DoseStatisticsPointer calculateDoseStatistics(DoseTypeGy referenceDose, unsigned int maxNumberMinimaPositions = 10, unsigned int maxNumberMaximaPositions = 10); /*! @brief Compute complex dose statistics with given relative x values and reference dose @param precomputeDoseValues the relative dose values for Vx precomputation, e.g. 0.02, 0.05, 0.95... @param precomputeVolumeValues the relative volume values for Dx, MOHx, MOCx, MaxOHx and MinOCx precomputation, e.g. 0.02, 0.05, 0.95... @param referenceDose the reference dose to compute Vx, normally it should be the prescribed dose. Default value is the maximum dose. @param maxNumberMinimaPositions the maximal amount of computed positions where the dose has its minimum that is computed @param maxNumberMaximaPositions the maximal amount of computed positions where the dose has its maximum that is computed @warning Computations can take quite long (>1 min) for large structures as many statistics are precomputed @note The complex dose statistics are precomputed and cannot be computed "on the fly" lateron! The doses/volumes that should be used for precomputation have to be set by in precomputeDoseValues and precomputeVolumeValues. Only these values can be requested in DoseStatistics! */ DoseStatisticsPointer calculateDoseStatistics(const std::vector& precomputeDoseValues, const std::vector& precomputeVolumeValues, DoseTypeGy referenceDose = -1, unsigned int maxNumberMinimaPositions = 10, unsigned int maxNumberMaximaPositions = 10); - /*! @brief Calculates additonal DoseMeasures for all complext Dose Statistics + /*! @brief Adds additonal precompute values for all complext Dose Statistics @exception InvalidDoseException if complexDoseStatistics are not already calculated */ - void calculateAdditionalComplexDoseStatisticMeasures(const std::vector& values); + void addPrecomputeValues(const std::vector& values); + + /*! @brief Recalculates the DoseMeasures for all complext Dose Statistics + @exception InvalidDoseException if complexDoseStatistics are not already calculated + */ + void recalculateDoseStatistics(); void setMultiThreading(bool choice); }; } } #endif diff --git a/code/algorithms/rttbDoseToVolumeMeasureCollection.cpp b/code/algorithms/rttbDoseToVolumeMeasureCollection.cpp index 025a46e..895124a 100644 --- a/code/algorithms/rttbDoseToVolumeMeasureCollection.cpp +++ b/code/algorithms/rttbDoseToVolumeMeasureCollection.cpp @@ -1,91 +1,95 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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) */ #include "rttbDoseToVolumeMeasureCollection.h" #include "rttbInvalidParameterException.h" #include "rttbDataNotAvailableException.h" namespace rttb { namespace algorithms { DoseToVolumeMeasureCollection::DoseToVolumeMeasureCollection(complexStatistics name, DoseTypeGy referenceDose) : _name(name), _values(std::map()), _referenceDose(referenceDose) {} void DoseToVolumeMeasureCollection::setReferenceDose(DoseTypeGy referenceDose) { this->_referenceDose = referenceDose; } void DoseToVolumeMeasureCollection::insertValue(DoseTypeGy dose, VolumeType volume) { this->_values.insert(std::pair(dose, volume)); } VolumeType DoseToVolumeMeasureCollection::getValue(DoseTypeGy xVolumeAbsolute) const { VolumeType dummy; return getSpecificValue(_values, xVolumeAbsolute, false, dummy); } VolumeType DoseToVolumeMeasureCollection::getValue(DoseTypeGy xDoseAbsolute, bool findNearestValue, DoseTypeGy & nearestXDose) const { return getSpecificValue(_values, xDoseAbsolute, findNearestValue, nearestXDose); } VolumeType DoseToVolumeMeasureCollection::getValueRelative(DoseTypeGy xDoseRelative) const { if (_referenceDose != -1 && xDoseRelative >= 0 && xDoseRelative <= 1) { DoseTypeGy xDoseAbsolute = xDoseRelative * _referenceDose; DoseTypeGy dummy; return getSpecificValue(_values, xDoseAbsolute, false, dummy); } else { throw rttb::core::InvalidParameterException("Reference dose must be > 0 and 0 <= relative Dose <= 1"); } } VolumeType DoseToVolumeMeasureCollection::getValueRelative(DoseTypeGy xDoseRelative, bool findNearestValue, DoseTypeGy & nearestXDose) const { if (_referenceDose != -1 && xDoseRelative >= 0 && xDoseRelative <= 1) { DoseTypeGy xDoseAbsolute = xDoseRelative * _referenceDose; return getSpecificValue(_values, xDoseAbsolute, findNearestValue, nearestXDose); } else { throw rttb::core::InvalidParameterException("Reference dose must be > 0 and 0 <= relative Dose <= 1"); } } DoseToVolumeMeasureCollection::DoseToVolumeFunctionType DoseToVolumeMeasureCollection::getAllValues() const { return this->_values; } + bool DoseToVolumeMeasureCollection::isInCollection(DoseTypeGy value) const { + return this->_values.find(value) != this->_values.end(); + } + bool operator==(const DoseToVolumeMeasureCollection& volumeToDoseMesureCollection, const DoseToVolumeMeasureCollection& otherVolumeToDoseMesureCollection) { if (volumeToDoseMesureCollection.getName() == otherVolumeToDoseMesureCollection.getName() && volumeToDoseMesureCollection.getAllValues() == otherVolumeToDoseMesureCollection.getAllValues()) { return true; } return false; } } } diff --git a/code/algorithms/rttbDoseToVolumeMeasureCollection.h b/code/algorithms/rttbDoseToVolumeMeasureCollection.h index cc197ab..77f9066 100644 --- a/code/algorithms/rttbDoseToVolumeMeasureCollection.h +++ b/code/algorithms/rttbDoseToVolumeMeasureCollection.h @@ -1,72 +1,74 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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 __DOSE_TO_VOLUME_MEASURE_COLLECTION_H #define __DOSE_TO_VOLUME_MEASURE_COLLECTION_H #include "rttbMeasureCollection.h" namespace rttb { namespace algorithms { /*! @class DoseToVolumeMeasureCollection @brief This class handels the access to the DoseToVolumeMeasureCollection elements for a specific complex statistic @note _referenceDose has to be set to use getValueRelative() otherwise an exception is thrown */ class RTTBAlgorithms_EXPORT DoseToVolumeMeasureCollection : public MeasureCollection { public: typedef std::map DoseToVolumeFunctionType; private: complexStatistics _name; DoseTypeGy _referenceDose; DoseToVolumeFunctionType _values; public: DoseToVolumeMeasureCollection(complexStatistics name, DoseTypeGy referenceDose = -1); /*! @brief This has to be set >=0 to use getValueRelative() */ void setReferenceDose(DoseTypeGy referenceDose); void insertValue(DoseTypeGy dose, VolumeType volume); /*! @brief Gets the volume irradiated with a dose >= x, depending on the complexStatistics name. @return Return absolute volume in absolute cm^3. @exception InvalidDoseException if the vector values is empty or _referenceDose is -1 @exception NoDataException if the requested Dose is not in the vector */ VolumeType getValue(DoseTypeGy xVolumeAbsolute) const; VolumeType getValue(DoseTypeGy xVolumeAbsolute, bool findNearestValue, DoseTypeGy& nearestXDose) const; VolumeType getValueRelative(DoseTypeGy xDoseRelative) const; VolumeType getValueRelative(DoseTypeGy xDoseRelative, bool findNearestValue, DoseTypeGy& nearestXDose) const; DoseToVolumeFunctionType getAllValues() const; + bool isInCollection(DoseTypeGy value) const; + friend bool operator==(const DoseToVolumeMeasureCollection& volumeToDoseMesureCollection, const DoseToVolumeMeasureCollection& otherVolumeToDoseMesureCollection); }; } } #endif diff --git a/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.cpp b/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.cpp index b0bf60b..c40bc95 100644 --- a/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.cpp +++ b/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.cpp @@ -1,80 +1,85 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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) */ #include "rttbDoseToVolumeMeasureCollectionCalculator.h" #include #include "rttbInvalidParameterException.h" #include //#include namespace rttb { namespace algorithms { DoseToVolumeMeasureCollectionCalculator::DoseToVolumeMeasureCollectionCalculator(const std::vector& precomputeDoseValues, const DoseTypeGy referenceDose, const core::DoseIteratorInterface::DoseIteratorPointer doseIterator, DoseToVolumeMeasureCollection::complexStatistics name, bool multiThreading) : _measureCollection(::boost::make_shared(name)), _precomputeDoseValues(precomputeDoseValues), _referenceDose(referenceDose), _doseIterator(doseIterator), _multiThreading(multiThreading) {} void DoseToVolumeMeasureCollectionCalculator::compute() - { - computeAdditionalValues(_precomputeDoseValues); - } - - void DoseToVolumeMeasureCollectionCalculator::computeAdditionalValues(const std::vector& values) { std::vector threads; - for (size_t i = 0; i < values.size(); ++i) + for (size_t i = 0; i < _precomputeDoseValues.size(); ++i) { - if (values.at(i) > 1 || values.at(i) < 0) { - throw rttb::core::InvalidParameterException("Values must be between 1 and 0!"); - } - double xAbsolute = values.at(i) * _referenceDose; - if (_multiThreading) - { - throw rttb::core::InvalidParameterException("MultiThreading is not implemented yet."); - //threads.push_back(boost::thread(&DoseToVolumeMeasureCollectionCalculator::insertIntoMeasureCollection, this , xAbsolute, computeSpecificValue(xAbsolute))); - } - else - { - insertIntoMeasureCollection(xAbsolute, this->computeSpecificValue(xAbsolute)); + + double xAbsolute = _precomputeDoseValues.at(i) * _referenceDose; + if (!_measureCollection->isInCollection(xAbsolute)) { + if (_multiThreading) + { + throw rttb::core::InvalidParameterException("MultiThreading is not implemented yet."); + //threads.push_back(boost::thread(&DoseToVolumeMeasureCollectionCalculator::insertIntoMeasureCollection, this , xAbsolute, computeSpecificValue(xAbsolute))); + } + else + { + insertIntoMeasureCollection(xAbsolute, this->computeSpecificValue(xAbsolute)); + } } } for (unsigned int i = 0; i& values) + { + for (size_t i = 0; i < values.size(); ++i) { + if (values.at(i) > 1 || values.at(i) < 0) { + throw rttb::core::InvalidParameterException("Values must be between 1 and 0!"); + } + _precomputeDoseValues.push_back(values.at(i)); + } + } DoseStatistics::DoseToVolumeMeasureCollectionPointer DoseToVolumeMeasureCollectionCalculator::getMeasureCollection() { return _measureCollection; } void DoseToVolumeMeasureCollectionCalculator::insertIntoMeasureCollection(DoseTypeGy xAbsolute, VolumeType resultVolume) { _measureCollection->insertValue(xAbsolute, resultVolume); } } } diff --git a/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.h b/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.h index 31e15d9..7b545b0 100644 --- a/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.h +++ b/code/algorithms/rttbDoseToVolumeMeasureCollectionCalculator.h @@ -1,90 +1,90 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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 __DOSE_TO_VOLUME_MEASURE_COLLECTION_CALCULATOR_H #define __DOSE_TO_VOLUME_MEASURE_COLLECTION_CALCULATOR_H #include #include #include "rttbBaseType.h" #include "RTTBAlgorithmsExports.h" #include "rttbDoseToVolumeMeasureCollection.h" #include "rttbDoseIteratorInterface.h" #include #include "rttbDoseStatistics.h" namespace rttb { namespace algorithms { /*! @class DoseToVolumeMeasureCollectionCalculator @brief Class for calculating DoseToVolume measures @details This class is used as a basis for DoseToVolumeMeasureCollectionCalculators. It implements the compute() method that is the same for every inheriting subclass, it accesses computeSpecificValue, which has to be implemented for each different complex statistic. */ class RTTBAlgorithms_EXPORT DoseToVolumeMeasureCollectionCalculator { public: typedef std::map VolumeToDoseFunctionType; protected: core::DoseIteratorInterface::DoseIteratorPointer _doseIterator; private: DoseTypeGy _referenceDose; DoseStatistics::DoseToVolumeMeasureCollectionPointer _measureCollection; std::vector _precomputeDoseValues; bool _multiThreading; public: - /*! @brief Computes the measureCollection. Algorithm for the specific complex Statistic has to be implemented in the corresponding subclass. + /*! @brief Computes not already comupted values for the measureCollection. Algorithm for the specific complex Statistic has to be implemented in the corresponding subclass. */ void compute(); - /*! @brief Computes additonal DoseMeasures with the given values vector. + /*! @brief Adds additional values to the _precomputeDoseValues vector. @exception InvalidParameterException If values vector contains values that are not between 0 and 1 */ - void computeAdditionalValues(const std::vector& values); + void addPrecomputeDoseValues(const std::vector& values); DoseStatistics::DoseToVolumeMeasureCollectionPointer getMeasureCollection(); protected: DoseToVolumeMeasureCollectionCalculator(const std::vector& precomputeDoseValues, const DoseTypeGy referenceDose, const core::DoseIteratorInterface::DoseIteratorPointer doseIterator, DoseToVolumeMeasureCollection::complexStatistics name, bool multiThreading); void insertIntoMeasureCollection(DoseTypeGy xAbsolute, VolumeType resultVolume); /*! @brief Computes the specific VolumeType depending on the complext statistic @note has to be overwritten */ virtual VolumeType computeSpecificValue(double xAbsolute) const = 0; }; } } #endif diff --git a/code/algorithms/rttbVolumeToDoseMeasureCollection.cpp b/code/algorithms/rttbVolumeToDoseMeasureCollection.cpp index e098492..226256e 100644 --- a/code/algorithms/rttbVolumeToDoseMeasureCollection.cpp +++ b/code/algorithms/rttbVolumeToDoseMeasureCollection.cpp @@ -1,98 +1,102 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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) */ #include "rttbVolumeToDoseMeasureCollection.h" #include "rttbInvalidParameterException.h" #include "rttbDataNotAvailableException.h" namespace rttb { namespace algorithms { VolumeToDoseMeasureCollection::VolumeToDoseMeasureCollection(complexStatistics name, VolumeType volume) : _name(name), _values(std::map()), _volume(volume) {} void VolumeToDoseMeasureCollection::setVolume(VolumeType volume) { this->_volume = volume; } void VolumeToDoseMeasureCollection::insertValue(VolumeType volume, DoseTypeGy dose) { this->_values.insert(std::pair(volume, dose)); } DoseTypeGy VolumeToDoseMeasureCollection::getValue(VolumeType xVolumeAbsolute) const { VolumeType dummy; return getSpecificValue(_values, xVolumeAbsolute, false, dummy); } DoseTypeGy VolumeToDoseMeasureCollection::getValue(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType & nearestXVolume) const { return getSpecificValue(_values, xVolumeAbsolute, findNearestValue, nearestXVolume); } DoseTypeGy VolumeToDoseMeasureCollection::getValueRelative(VolumeType xVolumeRelative) const { if (_volume == -1) { throw rttb::core::DataNotAvailableException("Volume is not set"); } if (xVolumeRelative >= 0 && xVolumeRelative <= 1) { DoseTypeGy xVolumeAbsolute = xVolumeRelative * _volume; VolumeType dummy; return getSpecificValue(_values, xVolumeAbsolute, false, dummy); } else { throw rttb::core::InvalidParameterException("Relative Volume must be >= 0 and <=1"); } } DoseTypeGy VolumeToDoseMeasureCollection::getValueRelative(VolumeType xVolumeRelative, bool findNearestValue, VolumeType & nearestXVolume) const { if (_volume == -1) { throw rttb::core::DataNotAvailableException("Volume is not set"); } if (xVolumeRelative >= 0 && xVolumeRelative <= 1) { DoseTypeGy xVolumeAbsolute = xVolumeRelative * _volume; return getSpecificValue(_values, xVolumeAbsolute, findNearestValue, nearestXVolume); } else { throw rttb::core::InvalidParameterException("Relative Volume must be >= 0 and <=1"); } } VolumeToDoseMeasureCollection::VolumeToDoseFunctionType VolumeToDoseMeasureCollection::getAllValues() const { return this->_values; } + bool VolumeToDoseMeasureCollection::isInCollection(VolumeType value) const { + return this->_values.find(value) != this->_values.end(); + } + bool operator==(const VolumeToDoseMeasureCollection& volumeToDoseMesureCollection,const VolumeToDoseMeasureCollection& otherVolumeToDoseMesureCollection) { if (volumeToDoseMesureCollection.getName() == otherVolumeToDoseMesureCollection.getName() && volumeToDoseMesureCollection.getAllValues() == otherVolumeToDoseMesureCollection.getAllValues()) { return true; } return false; } } } diff --git a/code/algorithms/rttbVolumeToDoseMeasureCollection.h b/code/algorithms/rttbVolumeToDoseMeasureCollection.h index 9e11628..fcad8d7 100644 --- a/code/algorithms/rttbVolumeToDoseMeasureCollection.h +++ b/code/algorithms/rttbVolumeToDoseMeasureCollection.h @@ -1,72 +1,74 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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 __VOLUME_TO_DOSE_MEASURE_COLLECTION_H #define __VOLUME_TO_DOSE_MEASURE_COLLECTION_H #include "rttbMeasureCollection.h" namespace rttb { namespace algorithms { /*! @class VolumeToDoseMeasureCollection @brief This class handels the access to the VolumeToDoseMeasureCollection elements for a specific complex statistic @note _volume has to be set to use getValueRelative() otherwise an exception is thrown */ class RTTBAlgorithms_EXPORT VolumeToDoseMeasureCollection : public MeasureCollection { public: typedef std::map VolumeToDoseFunctionType; private: VolumeType _volume; complexStatistics _name; VolumeToDoseFunctionType _values; public: VolumeToDoseMeasureCollection(complexStatistics name, VolumeType volume = -1); /*! @brief This has to be set >=0 to use getValueRelative() */ void setVolume(VolumeType volume); void insertValue(VolumeType volume, DoseTypeGy dose); /*! @brief Gets the x of the current volume, depending on the complexStatistics name. @return Return dose value in Gy. @exception InvalidDoseException if the vector values is empty @exception DataNotAvailableException if _volume is not set */ DoseTypeGy getValue(VolumeType xVolumeAbsolute) const; DoseTypeGy getValue(VolumeType xVolumeAbsolute, bool findNearestValue, VolumeType& nearestXDose) const; DoseTypeGy getValueRelative(VolumeType xDoseRelative) const; DoseTypeGy getValueRelative(VolumeType xDoseRelative, bool findNearestValue, VolumeType& nearestXDose) const; VolumeToDoseFunctionType getAllValues() const; + + bool isInCollection(VolumeType value) const; friend bool operator==(const VolumeToDoseMeasureCollection& volumeToDoseMesureCollection, const VolumeToDoseMeasureCollection& otherVolumeToDoseMesureCollection); }; } } #endif diff --git a/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.cpp b/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.cpp index cd11ecf..1e61659 100644 --- a/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.cpp +++ b/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.cpp @@ -1,80 +1,84 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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) */ #include "rttbVolumeToDoseMeasureCollectionCalculator.h" #include #include "rttbInvalidParameterException.h" #include //#include namespace rttb { namespace algorithms { VolumeToDoseMeasureCollectionCalculator::VolumeToDoseMeasureCollectionCalculator(const std::vector& precomputeVolumeValues, const VolumeType volume, const std::vector& doseVector, const std::vector& voxelProportionVector, const DoseVoxelVolumeType currentVoxelVolume, VolumeToDoseMeasureCollection::complexStatistics name, bool multiThreading) : _measureCollection(::boost::make_shared(name)), _precomputeVolumeValues(precomputeVolumeValues), _volume(volume), _doseVector(doseVector), _voxelProportionVector(voxelProportionVector), _currentVoxelVolume(currentVoxelVolume), _multiThreading(multiThreading) {} void VolumeToDoseMeasureCollectionCalculator::compute() - { - computeAdditionalValues(_precomputeVolumeValues); - } - void VolumeToDoseMeasureCollectionCalculator::computeAdditionalValues(const std::vector& values) { std::vector threads; - for (size_t i = 0; i < values.size(); ++i) + for (size_t i = 0; i < _precomputeVolumeValues.size(); ++i) { - if (values.at(i) > 1 || values.at(i) < 0) { - throw rttb::core::InvalidParameterException("Values must be between 1 and 0!"); - } - double xAbsolute = values.at(i) * _volume; - if (_multiThreading) - { - throw rttb::core::InvalidParameterException("MultiThreading is not implemented yet."); - //threads.push_back(boost::thread(&VolumeToDoseMeasureCollectionCalculator::insertIntoMeasureCollection, this, xAbsolute, computeSpecificValue(xAbsolute))); - } - else - { - insertIntoMeasureCollection(xAbsolute, this->computeSpecificValue(xAbsolute)); - } + double xAbsolute = _precomputeVolumeValues.at(i) * _volume; + if (!_measureCollection->isInCollection(xAbsolute)) { + if (_multiThreading) + { + throw rttb::core::InvalidParameterException("MultiThreading is not implemented yet."); + //threads.push_back(boost::thread(&VolumeToDoseMeasureCollectionCalculator::insertIntoMeasureCollection, this, xAbsolute, computeSpecificValue(xAbsolute))); + } + else + { + insertIntoMeasureCollection(xAbsolute, this->computeSpecificValue(xAbsolute)); + } + } } for (unsigned int i = 0; i& values) + { + for (size_t i = 0; i < values.size(); ++i) { + if (values.at(i) > 1 || values.at(i) < 0) { + throw rttb::core::InvalidParameterException("Values must be between 1 and 0!"); + } + _precomputeVolumeValues.push_back(values.at(i)); + } + } DoseStatistics::VolumeToDoseMeasureCollectionPointer VolumeToDoseMeasureCollectionCalculator::getMeasureCollection() { return _measureCollection; } void VolumeToDoseMeasureCollectionCalculator::insertIntoMeasureCollection(VolumeType xAbsolute, DoseTypeGy resultDose) { _measureCollection->insertValue(xAbsolute, resultDose); } } } diff --git a/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.h b/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.h index a25a665..f0f8cb0 100644 --- a/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.h +++ b/code/algorithms/rttbVolumeToDoseMeasureCollectionCalculator.h @@ -1,83 +1,83 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision: 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 __VOLUME_TO_DOSE_MEASURE_COLLECTION_CALCULATOR_H #define __VOLUME_TO_DOSE_MEASURE_COLLECTION_CALCULATOR_H #include "rttbVolumeToDoseMeasureCollection.h" #include #include "rttbDoseStatistics.h" namespace rttb { namespace algorithms { /*! @class VolumeToDoseMeasureCollectionCalculator @brief Class for calculating VolumeToDose measures @details This class is used as a basis for VolumeToDoseMeasureCollectionCalculator. It implements the compute() method that is the same for every inheriting subclass, it accesses computeSpecificValue, which has to be implemented for each different complex statistic. */ class RTTBAlgorithms_EXPORT VolumeToDoseMeasureCollectionCalculator { public: typedef std::map VolumeToDoseFunctionType; protected: std::vector _doseVector; DoseVoxelVolumeType _currentVoxelVolume; std::vector _voxelProportionVector; private: VolumeType _volume; DoseStatistics::VolumeToDoseMeasureCollectionPointer _measureCollection; std::vector _precomputeVolumeValues; bool _multiThreading; public: - /*! @brief Computes the measureCollection. Algorithm for the specific complex Statistic has to be implemented in the corresponding subclass. + /*! @brief Computes not already comupted values for the measureCollection. Algorithm for the specific complex Statistic has to be implemented in the corresponding subclass. */ void compute(); - /*! @brief Computes additonal DoseMeasures with the given values vector. + /*! @brief Adds additional values to the _precomputeVolumeValues vector. @exception InvalidParameterException If values vector contains values that are not between 0 and 1 */ - void computeAdditionalValues(const std::vector& values); + void addPrecomputeVolumeValues(const std::vector& values); DoseStatistics::VolumeToDoseMeasureCollectionPointer getMeasureCollection(); protected: VolumeToDoseMeasureCollectionCalculator(const std::vector& precomputeVolumeValues, const VolumeType volume, const std::vector& doseVector, const std::vector& voxelProportionVector, const DoseVoxelVolumeType currentVoxelVolume, VolumeToDoseMeasureCollection::complexStatistics name, bool multiThreading); void insertIntoMeasureCollection(VolumeType xAbsolute, DoseTypeGy resultDose); /*! @brief Computes the specific DoseTypeGy depending on the complext statistic @note has to be overwritten */ virtual DoseTypeGy computeSpecificValue(double xAbsolute) const = 0; }; } } #endif diff --git a/testing/algorithms/DoseStatisticsCalculatorTest.cpp b/testing/algorithms/DoseStatisticsCalculatorTest.cpp index 9fe1985..b7362bd 100644 --- a/testing/algorithms/DoseStatisticsCalculatorTest.cpp +++ b/testing/algorithms/DoseStatisticsCalculatorTest.cpp @@ -1,386 +1,387 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include "litCheckMacros.h" #include "rttbBaseType.h" #include "rttbGenericDoseIterator.h" #include "rttbDoseIteratorInterface.h" #include "rttbNullPointerException.h" #include "rttbDoseStatisticsCalculator.h" #include "rttbInvalidDoseException.h" #include "rttbInvalidParameterException.h" #include "rttbDataNotAvailableException.h" #include "rttbDicomFileDoseAccessorGenerator.h" #include "rttbDicomFileStructureSetGenerator.h" #include "rttbVOIindexIdentifier.h" #include "rttbBoostMaskAccessor.h" #include "rttbGenericMaskedDoseIterator.h" #include "../io/other/CompareDoseStatistic.h" #include "../../code/io/other/rttbDoseStatisticsXMLReader.h" #include "../core/DummyDoseAccessor.h" namespace rttb { namespace testing { typedef core::GenericDoseIterator::DoseAccessorPointer DoseAccessorPointer; typedef core::DoseIteratorInterface::DoseIteratorPointer DoseIteratorPointer; typedef rttb::algorithms::DoseStatistics::ResultListPointer ResultListPointer; typedef rttb::algorithms::DoseStatistics::DoseStatisticsPointer DoseStatisticsPointer; /*! @brief DoseStatisticsCalculatorTest - test the API of DoseStatisticsCalculator 1) test constructors 2) test setDoseIterator 3) test calculateDoseSatistics 4) get statistical values */ int DoseStatisticsCalculatorTest(int argc, char* argv[]) { PREPARE_DEFAULT_TEST_REPORTING; std::string referenceXMLFilename; std::string doseFilename, structFilename; boost::shared_ptr spTestDoseAccessor = boost::make_shared(); DoseAccessorPointer spDoseAccessor(spTestDoseAccessor); const std::vector* doseVals = spTestDoseAccessor->getDoseVector(); boost::shared_ptr spTestDoseIterator = boost::make_shared(spDoseAccessor); DoseIteratorPointer spDoseIterator(spTestDoseIterator); DoseIteratorPointer spDoseIteratorNull; if (argc > 3) { referenceXMLFilename = argv[1]; doseFilename = argv[2]; structFilename = argv[3]; } //1) test constructors // the values cannot be accessed from outside, therefore correct default values are not tested CHECK_THROW_EXPLICIT(rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator( spDoseIteratorNull), core::NullPointerException); CHECK_NO_THROW(rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator)); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator(spDoseIterator); //2) test setDoseIterator //3) test calculateDoseStatistics DoseStatisticsPointer theStatistics; //simple dose statistics CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics()); CHECK_EQUAL(theStatistics->getMinimumVoxelPositions()->empty(), false); CHECK_EQUAL(theStatistics->getMaximumVoxelPositions()->empty(), false); CHECK_EQUAL(theStatistics->getVx().getAllValues().empty(), true); CHECK_EQUAL(theStatistics->getDx().getAllValues().empty(), true); CHECK_EQUAL(theStatistics->getVx().getAllValues().empty(), true); CHECK_EQUAL(theStatistics->getMaxOHx().getAllValues().empty(), true); CHECK_EQUAL(theStatistics->getMOHx().getAllValues().empty(), true); CHECK_EQUAL(theStatistics->getMOCx().getAllValues().empty(), true); CHECK_EQUAL(theStatistics->getMinOCx().getAllValues().empty(), true); //check default values for computeComplexMeasures=true DoseStatisticsPointer theStatisticsDefault; myDoseStatsCalculator.setMultiThreading(true); CHECK_NO_THROW(theStatisticsDefault = myDoseStatsCalculator.calculateDoseStatistics(true)); CHECK_NO_THROW(theStatisticsDefault->getVx().getValue(0.02 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx().getValue(0.05 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx().getValue(0.1 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx().getValue(0.9 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx().getValue(0.95 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getVx().getValue(0.98 * theStatisticsDefault->getMaximum())); CHECK_NO_THROW(theStatisticsDefault->getDx().getValue(0.02 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx().getValue(0.05 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx().getValue(0.1 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx().getValue(0.9 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx().getValue(0.95 * theStatisticsDefault->getVolume())); CHECK_NO_THROW(theStatisticsDefault->getDx().getValue(0.98 * theStatisticsDefault->getVolume())); //check manually set reference dose and the default x values CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(100.0)); CHECK_THROW_EXPLICIT(theStatistics->getVx().getValue(0.1 * theStatistics->getMaximum()), core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getVx().getValue(0.1 * 100.0)); CHECK_NO_THROW(theStatistics->getDx().getValue(0.1 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx().getValue(0.9 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getMOHx().getValue(0.95 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getMOCx().getValue(0.98 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getReferenceDose(), 100.0); //check manually set x values std::vector precomputeDoseValues, precomputeVolumeValues, additionalValues, faultyValues; precomputeDoseValues.push_back(0.01); precomputeDoseValues.push_back(0.02); precomputeDoseValues.push_back(0.05); precomputeVolumeValues.push_back(0.9); precomputeVolumeValues.push_back(0.95); precomputeVolumeValues.push_back(0.99); additionalValues.push_back(0.03); additionalValues.push_back(0.04); faultyValues.push_back(2); CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(precomputeDoseValues, precomputeVolumeValues)); CHECK_NO_THROW(theStatistics->getVx().getValue(0.01 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getVx().getValue(0.02 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getVx().getValue(0.05 * theStatistics->getMaximum())); CHECK_THROW_EXPLICIT(theStatistics->getVx().getValue(0.03 * theStatistics->getMaximum()), core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getDx().getValue(0.9 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx().getValue(0.95 * theStatistics->getVolume())); CHECK_NO_THROW(theStatistics->getDx().getValue(0.99 * theStatistics->getVolume())); CHECK_THROW_EXPLICIT(theStatistics->getDx().getValue(0.04 * theStatistics->getVolume()), core::DataNotAvailableException); - CHECK_THROW_EXPLICIT(myDoseStatsCalculator.calculateAdditionalComplexDoseStatisticMeasures(faultyValues), + CHECK_THROW_EXPLICIT(myDoseStatsCalculator.addPrecomputeValues(faultyValues), core::InvalidParameterException); - CHECK_NO_THROW(myDoseStatsCalculator.calculateAdditionalComplexDoseStatisticMeasures(additionalValues)); + CHECK_NO_THROW(myDoseStatsCalculator.addPrecomputeValues(additionalValues)); + CHECK_NO_THROW(myDoseStatsCalculator.recalculateDoseStatistics()); CHECK_NO_THROW(theStatistics->getVx().getValue(0.03 * theStatistics->getMaximum())); CHECK_NO_THROW(theStatistics->getDx().getValue(0.04 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getVx().getValue(0.02 * theStatistics->getMaximum()), theStatisticsDefault->getVx().getValue(0.02 * theStatistics->getMaximum())); CHECK_EQUAL(theStatistics->getVx().getValue(0.05 * theStatistics->getMaximum()), theStatisticsDefault->getVx().getValue(0.05 * theStatistics->getMaximum())); CHECK_EQUAL(theStatistics->getDx().getValue(0.9 * theStatistics->getVolume()), theStatisticsDefault->getDx().getValue(0.9 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getDx().getValue(0.95 * theStatistics->getVolume()), theStatisticsDefault->getDx().getValue(0.95 * theStatistics->getVolume())); //check manually set reference dose and x values CHECK_NO_THROW(theStatistics = myDoseStatsCalculator.calculateDoseStatistics(precomputeDoseValues, precomputeVolumeValues, 100.0)); CHECK_THROW_EXPLICIT(theStatistics->getVx().getValue(0.01 * theStatistics->getMaximum()), core::DataNotAvailableException); CHECK_NO_THROW(theStatistics->getVx().getValue(0.01 * 100.0)); CHECK_NO_THROW(theStatistics->getDx().getValue(0.9 * theStatistics->getVolume())); CHECK_EQUAL(theStatistics->getReferenceDose(), 100.0); //MOHx, MOCx, MaxOHx and MinOCx are computed analogous to Dx, they will not be checked. //4) get statistical values CHECK_EQUAL(theStatistics->getNumberOfVoxels(), doseVals->size()); //compute simple statistical values (min, mean, max, stddev) for comparison DoseStatisticType maximum = 0; DoseStatisticType minimum = 1000000; DoseStatisticType mean = 0; DoseStatisticType variance = 0; std::vector::const_iterator doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (maximum < *doseIt) { maximum = *doseIt; } if (minimum > *doseIt) { minimum = *doseIt; } mean += *doseIt; ++doseIt; } mean /= doseVals->size(); DoseTypeGy compMean = (int(mean * 100)) / 100; doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { variance += pow(*doseIt - mean, 2); ++doseIt; } variance /= doseVals->size(); DoseStatisticType stdDev = pow(variance, 0.5); //we have some precision problems here... double errorConstantLarger = 1e-2; CHECK_EQUAL(theStatistics->getMaximum(), maximum); CHECK_EQUAL(theStatistics->getMinimum(), minimum); CHECK_CLOSE(theStatistics->getMean(), mean, errorConstantLarger); CHECK_CLOSE(theStatistics->getStdDeviation(), stdDev, errorConstantLarger); CHECK_CLOSE(theStatistics->getVariance(), variance, errorConstantLarger); //check for complex doseStatistics (maximumPositions, minimumPositions, Vx, Dx, MOHx, MOCx, MAXOHx, MinOCx) unsigned int nMax = 0, nMin = 0; doseIt = doseVals->begin(); while (doseIt != doseVals->end()) { if (*doseIt == theStatistics->getMaximum()) { nMax++; } if (*doseIt == theStatistics->getMinimum()) { nMin++; } ++doseIt; } //only 100 positions are stored if (nMax > 100) { nMax = 100; } if (nMin > 100) { nMin = 100; } auto maximaPositions = theStatistics->getMaximumVoxelPositions(); auto minimaPositions = theStatistics->getMinimumVoxelPositions(); CHECK_EQUAL(maximaPositions->size(), nMax); CHECK_EQUAL(minimaPositions->size(), nMin); for (auto maximaPositionsIterator = std::begin(*maximaPositions); maximaPositionsIterator != std::end(*maximaPositions); ++maximaPositionsIterator) { CHECK_EQUAL(maximaPositionsIterator->first, theStatistics->getMaximum()); } for (auto minimaPositionsIterator = std::begin(*minimaPositions); minimaPositionsIterator != std::end(*minimaPositions); ++minimaPositionsIterator) { CHECK_EQUAL(minimaPositionsIterator->first, theStatistics->getMinimum()); } //generate specific example dose maximum = 9.5; minimum = 2.5; mean = 6; int sizeTemplate = 500; std::vector aDoseVector; for (int i = 0; i < sizeTemplate; i++) { aDoseVector.push_back(maximum); aDoseVector.push_back(minimum); } core::GeometricInfo geoInfo = spTestDoseAccessor->getGeometricInfo(); geoInfo.setNumRows(20); geoInfo.setNumColumns(10); geoInfo.setNumSlices(5); boost::shared_ptr spTestDoseAccessor2 = boost::make_shared(aDoseVector, geoInfo); DoseAccessorPointer spDoseAccessor2(spTestDoseAccessor2); boost::shared_ptr spTestDoseIterator2 = boost::make_shared(spDoseAccessor2); DoseIteratorPointer spDoseIterator2(spTestDoseIterator2); rttb::algorithms::DoseStatisticsCalculator myDoseStatsCalculator2(spDoseIterator2); DoseStatisticsPointer theStatistics3 = myDoseStatsCalculator2.calculateDoseStatistics(); CHECK_EQUAL(theStatistics3->getMaximum(), maximum); CHECK_EQUAL(theStatistics3->getMinimum(), minimum); CHECK_EQUAL(theStatistics3->getMean(), mean); maximaPositions = theStatistics3->getMaximumVoxelPositions(); minimaPositions = theStatistics3->getMinimumVoxelPositions(); CHECK_EQUAL(maximaPositions->empty(), false); CHECK_EQUAL(minimaPositions->empty(), false); for (auto maximaPositionsIterator = std::begin(*maximaPositions); maximaPositionsIterator != std::end(*maximaPositions); ++maximaPositionsIterator) { CHECK_EQUAL(maximaPositionsIterator->first, theStatistics3->getMaximum()); } for (auto minimaPositionsIterator = std::begin(*minimaPositions); minimaPositionsIterator != std::end(*minimaPositions); ++minimaPositionsIterator) { CHECK_EQUAL(minimaPositionsIterator->first, theStatistics3->getMinimum()); } // compare with actual XML io::dicom::DicomFileDoseAccessorGenerator doseAccessorGenerator(doseFilename.c_str()); core::DoseAccessorInterface::DoseAccessorPointer doseAccessorPointer(doseAccessorGenerator.generateDoseAccessor()); rttb::io::dicom::DicomFileStructureSetGenerator structAccessorGenerator(structFilename.c_str()); core::StructureSetGeneratorInterface::StructureSetPointer structerSetGeneratorPointer = structAccessorGenerator.generateStructureSet(); std::vector foundIndices = rttb::masks::VOIindexIdentifier::getIndicesByVoiRegex( structerSetGeneratorPointer, "Heart"); CHECK_EQUAL(foundIndices.size(), 1); core::MaskAccessorInterface::MaskAccessorPointer maskAccessorPointer = boost::make_shared (structerSetGeneratorPointer->getStructure(foundIndices.at(0)), doseAccessorPointer->getGeometricInfo(), true); maskAccessorPointer->updateMask(); boost::shared_ptr maskedDoseIterator = boost::make_shared(maskAccessorPointer, doseAccessorPointer); rttb::core::DoseIteratorInterface::DoseIteratorPointer doseIteratorPointer(maskedDoseIterator); rttb::algorithms::DoseStatisticsCalculator doseStatisticsCalculator(doseIteratorPointer); DoseStatisticsPointer doseStatisticsActual = doseStatisticsCalculator.calculateDoseStatistics(14.0); io::other::DoseStatisticsXMLReader readerDefaultExpected(referenceXMLFilename); auto doseStatisticsExpected = readerDefaultExpected.generateDoseStatistic(); CHECK(checkEqualDoseStatistic(doseStatisticsExpected, doseStatisticsActual)); RETURN_AND_REPORT_TEST_SUCCESS; } }//end namespace testing }//end namespace rttb