diff --git a/code/core/rttbAccessorWithGeoInfoBase.h b/code/core/rttbAccessorWithGeoInfoBase.h index 606e410..6c50bab 100644 --- a/code/core/rttbAccessorWithGeoInfoBase.h +++ b/code/core/rttbAccessorWithGeoInfoBase.h @@ -1,52 +1,53 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed 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 __ACCESSOR_WITH_GEO_INFO_BASE_H #define __ACCESSOR_WITH_GEO_INFO_BASE_H #include "rttbAccessorInterface.h" #include "RTTBCoreExports.h" namespace rttb { namespace core { /*! @class AccessorWithGeoInfoBase @brief Base class for all accessor implementations that have there own geometric info. */ class RTTBCore_EXPORT AccessorWithGeoInfoBase : public core::AccessorInterface { protected: + virtual void assembleGeometricInfo()=0; core::GeometricInfo _geoInfo; public: ~AccessorWithGeoInfoBase() override; AccessorWithGeoInfoBase(); const core::GeometricInfo& getGeometricInfo() const override; }; } } #endif diff --git a/code/io/dicom/rttbDicomDoseAccessor.cpp b/code/io/dicom/rttbDicomDoseAccessor.cpp index b25e4ad..8616ded 100644 --- a/code/io/dicom/rttbDicomDoseAccessor.cpp +++ b/code/io/dicom/rttbDicomDoseAccessor.cpp @@ -1,294 +1,293 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed 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 "drtdose.h" #include #include #include "rttbDicomDoseAccessor.h" #include "rttbNullPointerException.h" #include "rttbInvalidDoseException.h" #include "rttbDcmrtException.h" #include "rttbIndexOutOfBoundsException.h" namespace rttb { namespace io { namespace dicom { DicomDoseAccessor::~DicomDoseAccessor() = default; DicomDoseAccessor::DicomDoseAccessor(DRTDoseIODPtr aDRTDoseIODP, DcmItemPtr aDcmDataset) { _dose = aDRTDoseIODP; _dataSet = aDcmDataset; OFString uid; _dose->getSeriesInstanceUID(uid); _doseUID = uid.c_str(); this->begin(); } bool DicomDoseAccessor::begin() { assembleGeometricInfo(); doseData.clear(); OFString doseGridScalingStr; this->_dose->getDoseGridScaling(doseGridScalingStr); try { _doseGridScaling = boost::lexical_cast(doseGridScalingStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Dose grid scaling not readable or = 0!") ; } OFCondition status; unsigned long count; const Uint16* pixelData; status = _dataSet->findAndGetUint16Array(DcmTagKey(0x7fe0, 0x0010), pixelData, &count); if (status.good()) { for (unsigned int i = 0; i < static_cast(this->_geoInfo.getNumberOfVoxels()); i++) { this->doseData.push_back(pixelData[i]); } return true; } else { throw io::dicom::DcmrtException("Read Pixel Data (7FE0,0010) failed!"); } } - bool DicomDoseAccessor::assembleGeometricInfo() + void DicomDoseAccessor::assembleGeometricInfo() { Uint16 temp = 0; this->_dose->getColumns(temp); _geoInfo.setNumColumns(temp); temp = 0; this->_dose->getRows(temp); _geoInfo.setNumRows(temp); if (_geoInfo.getNumColumns() == 0 || _geoInfo.getNumRows() == 0) { throw core::InvalidDoseException("Empty dicom dose!") ; } OFString numberOfFramesStr; OFString imageOrientationRowX, imageOrientationRowY, imageOrientationRowZ; OFString imageOrientationColumnX, imageOrientationColumnY, imageOrientationColumnZ; WorldCoordinate3D imageOrientationRow; WorldCoordinate3D imageOrientationColumn; try { this->_dose->getNumberOfFrames(numberOfFramesStr); _geoInfo.setNumSlices(boost::lexical_cast(numberOfFramesStr.c_str())); _dose->getImageOrientationPatient(imageOrientationRowX, 0); _dose->getImageOrientationPatient(imageOrientationRowY, 1); _dose->getImageOrientationPatient(imageOrientationRowZ, 2); _dose->getImageOrientationPatient(imageOrientationColumnX, 3); _dose->getImageOrientationPatient(imageOrientationColumnY, 4); _dose->getImageOrientationPatient(imageOrientationColumnZ, 5); imageOrientationRow(0) = boost::lexical_cast(imageOrientationRowX.c_str()); imageOrientationRow(1) = boost::lexical_cast(imageOrientationRowY.c_str()); imageOrientationRow(2) = boost::lexical_cast(imageOrientationRowZ.c_str()); imageOrientationColumn(0) = boost::lexical_cast(imageOrientationColumnX.c_str()); imageOrientationColumn(1) = boost::lexical_cast(imageOrientationColumnY.c_str()); imageOrientationColumn(2) = boost::lexical_cast(imageOrientationColumnZ.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("boost::lexical_cast failed! Empty dicom dose!") ; } /*Get orientation*/ OrientationMatrix orientation; orientation(0, 0) = imageOrientationRow.x(); orientation(1, 0) = imageOrientationRow.y(); orientation(2, 0) = imageOrientationRow.z(); orientation(0, 1) = imageOrientationColumn.x(); orientation(1, 1) = imageOrientationColumn.y(); orientation(2, 1) = imageOrientationColumn.z(); WorldCoordinate3D perpendicular = imageOrientationRow.cross(imageOrientationColumn); orientation(0, 2) = perpendicular.x(); orientation(1, 2) = perpendicular.y(); orientation(2, 2) = perpendicular.z(); _geoInfo.setOrientationMatrix(orientation); OFString imagePositionX, imagePositionY, imagePositionZ; _dose->getImagePositionPatient(imagePositionX, 0); _dose->getImagePositionPatient(imagePositionY, 1); _dose->getImagePositionPatient(imagePositionZ, 2); WorldCoordinate3D imagePositionPatient; try { imagePositionPatient(0) = boost::lexical_cast(imagePositionX.c_str()); imagePositionPatient(1) = boost::lexical_cast(imagePositionY.c_str()); imagePositionPatient(2) = boost::lexical_cast(imagePositionZ.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Can not read image position X/Y/Z!") ; } _geoInfo.setImagePositionPatient(imagePositionPatient); /*Get spacing*/ SpacingVectorType3D spacingVector; OFString pixelSpacingRowStr, pixelSpacingColumnStr, sliceThicknessStr; _dose->getPixelSpacing(pixelSpacingRowStr, 0); _dose->getPixelSpacing(pixelSpacingColumnStr, 1); try { spacingVector(1) = boost::lexical_cast(pixelSpacingRowStr.c_str()); spacingVector(0) = boost::lexical_cast(pixelSpacingColumnStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Can not read Pixel Spacing Row/Column!") ; } _geoInfo.setSpacing(spacingVector); if (_geoInfo.getSpacing()(0) == 0 || _geoInfo.getSpacing()(1) == 0) { throw core::InvalidDoseException("Pixel spacing is 0!"); } _dose->getSliceThickness(sliceThicknessStr); try { spacingVector(2) = boost::lexical_cast(sliceThicknessStr.c_str()); } catch (boost::bad_lexical_cast&) { spacingVector(2) = 0 ; } if (spacingVector(2) == 0) { OFVector gridFrameOffsetVector; _dose->getGridFrameOffsetVector(gridFrameOffsetVector); if (gridFrameOffsetVector.size() >= 2) { spacingVector(2) = gridFrameOffsetVector.at(1) - gridFrameOffsetVector.at( 0); //read slice thickness from GridFrameOffsetVector (3004,000c) } if (spacingVector(2) == 0) { OFCondition status; DcmItem doseitem; OFString pixelSpacingBetweenSlices; status = _dose->write(doseitem); if (status.good()) { status = doseitem.findAndGetOFString(DcmTagKey(0x0018, 0x0088), pixelSpacingBetweenSlices); try { spacingVector(2) = boost::lexical_cast (pixelSpacingBetweenSlices.c_str());//read slice thickness from PixelSpacingBetweenSlices (0018,0088) } catch (boost::bad_lexical_cast&) { spacingVector(2) = 0 ; } } //if no useful tags to compute slicing -> set slice thickness to spacingVector(0) if (spacingVector(2) == 0) { std::cerr << "sliceThickness == 0! It wird be replaced with pixelSpacingRow=" << _geoInfo.getSpacing()(0) << "!" << std::endl; spacingVector(2) = spacingVector(0); } } } _geoInfo.setSpacing(spacingVector); - return true; } GenericValueType DicomDoseAccessor::getValueAt(const VoxelGridID aID) const { return doseData.at(aID) * _doseGridScaling; } GenericValueType DicomDoseAccessor::getValueAt(const VoxelGridIndex3D& aIndex) const { VoxelGridID aVoxelGridID; if (_geoInfo.convert(aIndex, aVoxelGridID)) { return getValueAt(aVoxelGridID); } else { return -1; } } } } } diff --git a/code/io/dicom/rttbDicomDoseAccessor.h b/code/io/dicom/rttbDicomDoseAccessor.h index fa8194c..2358d28 100644 --- a/code/io/dicom/rttbDicomDoseAccessor.h +++ b/code/io/dicom/rttbDicomDoseAccessor.h @@ -1,99 +1,99 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DICOM_DOSE_ACCESSOR_H #define __DICOM_DOSE_ACCESSOR_H #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "drtdose.h" #include #include #include #include "rttbAccessorWithGeoInfoBase.h" #include "rttbBaseType.h" namespace rttb { namespace io { namespace dicom { /*! @class DicomDoseAccessor @brief This class gives access to dose information from DRTDoseIOD and DcmItem */ class DicomDoseAccessor: public core::AccessorWithGeoInfoBase { public: using DRTDoseIODPtr = boost::shared_ptr; using DcmItemPtr = boost::shared_ptr; private: DRTDoseIODPtr _dose; DcmItemPtr _dataSet; /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ std::vector doseData; double _doseGridScaling; IDType _doseUID; DicomDoseAccessor() = delete; protected: /*! @brief Initialize dose data @exception InvalidDoseException Thrown if _dose is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 @exception DcmrtException Throw if dcmrt error @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. */ bool begin(); /*! @brief get all required data from dicom information contained in _dose @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. */ - bool assembleGeometricInfo(); + void assembleGeometricInfo() override; public: ~DicomDoseAccessor() override; /*! @brief Constructor. Initialisation with a boost::shared_ptr of DRTDoseIOD and of DcmItem to get the pixel data @exception DcmrtException Throw if dcmrt error */ DicomDoseAccessor(DRTDoseIODPtr aDRTDoseIODP, DcmItemPtr aDcmDataset); GenericValueType getValueAt(const VoxelGridID aID) const override; GenericValueType getValueAt(const VoxelGridIndex3D& aIndex) const override; const IDType getUID() const override { return _doseUID; }; }; } } } #endif diff --git a/code/io/helax/rttbDicomHelaxDoseAccessor.cpp b/code/io/helax/rttbDicomHelaxDoseAccessor.cpp index b70d5c2..0df602e 100644 --- a/code/io/helax/rttbDicomHelaxDoseAccessor.cpp +++ b/code/io/helax/rttbDicomHelaxDoseAccessor.cpp @@ -1,327 +1,326 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed 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 "rttbDicomHelaxDoseAccessor.h" #include "boost/lexical_cast.hpp" #include "boost/filesystem/operations.hpp" #include "boost/filesystem/path.hpp" #include "boost/numeric/ublas/matrix.hpp" #include #include "rttbInvalidDoseException.h" #include "rttbDcmrtException.h" #include "rttbInvalidParameterException.h" namespace rttb { namespace io { namespace helax { DicomHelaxDoseAccessor::~DicomHelaxDoseAccessor() = default; DicomHelaxDoseAccessor::DicomHelaxDoseAccessor(std::vector aDICOMRTDoseVector) { for (const auto & i : aDICOMRTDoseVector) { _doseVector.push_back(i); } this->begin(); } bool DicomHelaxDoseAccessor::begin() { if (_doseVector.size() == 0) { throw core::InvalidParameterException(" The size of aDICOMRTDoseVector is 0!"); } assembleGeometricInfo(); _doseData.clear(); OFString doseGridScalingStr; _doseVector.at(0)->getDoseGridScaling( doseGridScalingStr);//get the first dose grid scaling as _doseGridScaling try { _doseGridScaling = boost::lexical_cast(doseGridScalingStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Dose grid scaling not readable or = 0!") ; } for (auto dose : _doseVector) { OFString currentDoseGridScalingStr; dose->getDoseGridScaling(currentDoseGridScalingStr); double currentDoseGridScaling; try { currentDoseGridScaling = boost::lexical_cast(currentDoseGridScalingStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Dose grid scaling not readable or = 0!") ; } OFCondition status; DcmFileFormat fileformat; DcmItem doseitem; status = dose->write(doseitem); if (status.good()) { unsigned long count; const Uint16* pixelData; status = doseitem.findAndGetUint16Array(DcmTagKey(0x7fe0, 0x0010), pixelData, &count); if (status.good()) { for (unsigned int j = 0; j < static_cast(_geoInfo.getNumColumns()*_geoInfo.getNumRows()); j++) { auto data = static_cast(pixelData[j] * currentDoseGridScaling / _doseGridScaling); this->_doseData.push_back(data); //recalculate dose data } } else { throw dicom::DcmrtException("Read Pixel Data (7FE0,0010) failed!"); } } else { throw dicom::DcmrtException("Read DICOM-RT Dose file failed!"); } } return true; } - bool DicomHelaxDoseAccessor::assembleGeometricInfo() + void DicomHelaxDoseAccessor::assembleGeometricInfo() { DRTDoseIODPtr dose = _doseVector.at(0); Uint16 temp = 0; dose->getColumns(temp); _geoInfo.setNumColumns(temp); temp = 0; dose->getRows(temp); _geoInfo.setNumRows(temp); OFString numberOfFramesStr; dose->getNumberOfFrames(numberOfFramesStr); if (!numberOfFramesStr.empty()) { _geoInfo.setNumSlices(boost::lexical_cast(numberOfFramesStr.c_str())); } else { _geoInfo.setNumSlices((VoxelGridDimensionType)_doseVector.size()); } if (_geoInfo.getNumColumns() == 0 || _geoInfo.getNumRows() == 0 || _geoInfo.getNumSlices() == 0) { throw core::InvalidDoseException("Empty dicom dose!") ; } OFString imageOrientationRowX; dose->getImageOrientationPatient(imageOrientationRowX, 0); OFString imageOrientationRowY; dose->getImageOrientationPatient(imageOrientationRowY, 1); OFString imageOrientationRowZ; dose->getImageOrientationPatient(imageOrientationRowZ, 2); OFString imageOrientationColumnX; dose->getImageOrientationPatient(imageOrientationColumnX, 3); OFString imageOrientationColumnY; dose->getImageOrientationPatient(imageOrientationColumnY, 4); OFString imageOrientationColumnZ; dose->getImageOrientationPatient(imageOrientationColumnZ, 5); WorldCoordinate3D imageOrientationRow; WorldCoordinate3D imageOrientationColumn; try { imageOrientationRow(0) = boost::lexical_cast(imageOrientationRowX.c_str()); imageOrientationRow(1) = boost::lexical_cast(imageOrientationRowY.c_str()); imageOrientationRow(2) = boost::lexical_cast(imageOrientationRowZ.c_str()); imageOrientationColumn(0) = boost::lexical_cast(imageOrientationColumnX.c_str()); imageOrientationColumn(1) = boost::lexical_cast(imageOrientationColumnY.c_str()); imageOrientationColumn(2) = boost::lexical_cast(imageOrientationColumnZ.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("boost::lexical_cast WorldCoordinate failed! Can not read image orientation X/Y/Z!") ; } OrientationMatrix orientation; orientation(0, 0) = imageOrientationRow.x(); orientation(1, 0) = imageOrientationRow.y(); orientation(2, 0) = imageOrientationRow.z(); orientation(0, 1) = imageOrientationColumn.x(); orientation(1, 1) = imageOrientationColumn.y(); orientation(2, 1) = imageOrientationColumn.z(); WorldCoordinate3D perpendicular = imageOrientationRow.cross(imageOrientationColumn); orientation(0, 2) = perpendicular.x(); orientation(1, 2) = perpendicular.y(); orientation(2, 2) = perpendicular.z(); _geoInfo.setOrientationMatrix(orientation); OFString imagePositionX; dose->getImagePositionPatient(imagePositionX, 0); OFString imagePositionY; dose->getImagePositionPatient(imagePositionY, 1); OFString imagePositionZ; dose->getImagePositionPatient(imagePositionZ, 2); WorldCoordinate3D imagePositionPatient; try { imagePositionPatient(0) = boost::lexical_cast(imagePositionX.c_str()); imagePositionPatient(1) = boost::lexical_cast(imagePositionY.c_str()); imagePositionPatient(2) = boost::lexical_cast(imagePositionZ.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("boost::lexical_cast ImagePosition failed! Can not read image position X/Y/Z!") ; } _geoInfo.setImagePositionPatient(imagePositionPatient); SpacingVectorType3D spacingVector; OFString pixelSpacingRowStr; dose->getPixelSpacing(pixelSpacingRowStr, 0); OFString pixelSpacingColumnStr; dose->getPixelSpacing(pixelSpacingColumnStr, 1); try { spacingVector(1) = boost::lexical_cast(pixelSpacingRowStr.c_str()); spacingVector(0) = boost::lexical_cast(pixelSpacingColumnStr.c_str()); } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Can not read Pixel Spacing Row/Column!") ; } _geoInfo.setSpacing(spacingVector); if (_geoInfo.getSpacing()(0) == 0 || _geoInfo.getSpacing()(1) == 0) { throw core::InvalidDoseException("Pixel spacing not readable or = 0!"); } OFString sliceThicknessStr; dose->getSliceThickness(sliceThicknessStr); try { spacingVector(2) = boost::lexical_cast(sliceThicknessStr.c_str()); } catch (boost::bad_lexical_cast&) { spacingVector(2) = 0 ;//if no information about slice thickness, set to 0 and calculate it using z coordinate difference between 1. and 2. dose } if (spacingVector(2) == 0) { if (_doseVector.size() > 1) { DRTDoseIODPtr dose2 = _doseVector.at(1);//get the 2. dose OFString imagePositionZ2; dose2->getImagePositionPatient(imagePositionZ2, 2); try { spacingVector(2) = boost::lexical_cast(imagePositionZ2.c_str()) - imagePositionPatient( 2); //caculate slicethickness } catch (boost::bad_lexical_cast&) { throw core::InvalidDoseException("Can not read image position Z of the 2. dose!"); } } else { std::cerr << "sliceThickness == 0! It will be replaced with pixelSpacingRow=" << _geoInfo.getSpacing()(0) << "!" << std::endl; spacingVector(2) = spacingVector(0); } } _geoInfo.setSpacing(spacingVector); - return true; } GenericValueType DicomHelaxDoseAccessor::getValueAt(const VoxelGridID aID) const { return _doseData.at(aID) * _doseGridScaling; } GenericValueType DicomHelaxDoseAccessor::getValueAt(const VoxelGridIndex3D& aIndex) const { VoxelGridID aVoxelGridID; if (_geoInfo.convert(aIndex, aVoxelGridID)) { return getValueAt(aVoxelGridID); } else { return -1; } } } } } diff --git a/code/io/helax/rttbDicomHelaxDoseAccessor.h b/code/io/helax/rttbDicomHelaxDoseAccessor.h index 7cf697f..ba0d8a0 100644 --- a/code/io/helax/rttbDicomHelaxDoseAccessor.h +++ b/code/io/helax/rttbDicomHelaxDoseAccessor.h @@ -1,101 +1,101 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DICOM_HELAX_DOSE_ACCESSOR_H #define __DICOM_HELAX_DOSE_ACCESSOR_H #include #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "drtdose.h" #include "rttbBaseType.h" #include "rttbAccessorWithGeoInfoBase.h" namespace rttb { namespace io { namespace helax { /*! @class DicomHelaxDoseAccessor @brief Load dose data from a directory containing dicom dose files, each file describes the helax dose in one slice. */ class DicomHelaxDoseAccessor: public core::AccessorWithGeoInfoBase { public: using DRTDoseIODPtr = boost::shared_ptr; private: /*! vector of DRTDoseIOD shared pointers, each DRTDoseIOD pointer presents the dose in one slice*/ std::vector _doseVector; /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ std::vector _doseData; double _doseGridScaling; IDType _doseUID; DicomHelaxDoseAccessor() = delete; protected: /*! @brief Initialize dose data @exception InvalidDoseException Thrown if any DRTDoseIOD pointer of _doseVector is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 @exception DcmrtException Throw if dcmrt error @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. */ bool begin(); /*! @brief get all required data from dicom information contained in _dose @exception boost/bad_lexical_cast Thrown if the imported header tags are not numerical. */ - bool assembleGeometricInfo(); + void assembleGeometricInfo() override; public: ~DicomHelaxDoseAccessor() override; /*! @brief Constructor. Initialisation with a vector of DRTDoseIOD pointers @exception InvalidDoseException Thrown if any DRTDoseIOD pointer of _doseVector is invalid: one of column/row/numberOfFrames/doseGridScaling/pixelSpacing=0 @exception DcmrtException Throw if dcmrt error */ DicomHelaxDoseAccessor(std::vector aDICOMRTDoseVector); GenericValueType getValueAt(const VoxelGridID aID) const override; GenericValueType getValueAt(const VoxelGridIndex3D& aIndex) const override; const IDType getUID() const override { return _doseUID; }; }; } } } #endif diff --git a/code/io/itk/rttbITKImageAccessor.cpp b/code/io/itk/rttbITKImageAccessor.cpp index bcb5553..73f74cc 100644 --- a/code/io/itk/rttbITKImageAccessor.cpp +++ b/code/io/itk/rttbITKImageAccessor.cpp @@ -1,116 +1,114 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include "rttbITKImageAccessor.h" #include "rttbException.h" #include "rttbInvalidDoseException.h" namespace rttb { namespace io { namespace itk { ITKImageAccessor::~ITKImageAccessor() = default; ITKImageAccessor::ITKImageAccessor(ITKImageType::ConstPointer image) { _data = image; if (_data.IsNull()) { throw core::InvalidDoseException("Dose image = 0!") ; } assembleGeometricInfo(); } GenericValueType ITKImageAccessor::getValueAt(const VoxelGridID aID) const { VoxelGridIndex3D aVoxelGridIndex; if (_geoInfo.convert(aID, aVoxelGridIndex)) { return getValueAt(aVoxelGridIndex); } else { return -1; } } GenericValueType ITKImageAccessor::getValueAt(const VoxelGridIndex3D& aIndex) const { if (_geoInfo.validIndex(aIndex)) { const ITKImageType::IndexType pixelIndex = {{aIndex[0], aIndex[1], aIndex[2]}}; return _data->GetPixel(pixelIndex); } else { return -1; } } - bool ITKImageAccessor::assembleGeometricInfo() + void ITKImageAccessor::assembleGeometricInfo() { _geoInfo.setSpacing(SpacingVectorType3D(_data->GetSpacing()[0], _data->GetSpacing()[1], _data->GetSpacing()[2])); if (_geoInfo.getSpacing()[0] == 0 || _geoInfo.getSpacing()[1] == 0 || _geoInfo.getSpacing()[2] == 0) { throw core::InvalidDoseException("Pixel spacing = 0!"); } _geoInfo.setImagePositionPatient(WorldCoordinate3D(_data->GetOrigin()[0], _data->GetOrigin()[1], _data->GetOrigin()[2])); OrientationMatrix OM(0); for (unsigned int col = 0; col < 3; ++col) { for (unsigned int row = 0; row < 3; ++row) { OM(col, row) = _data->GetDirection()(col, row); } } _geoInfo.setOrientationMatrix(OM); _geoInfo.setNumColumns(_data->GetLargestPossibleRegion().GetSize()[0]); _geoInfo.setNumRows(_data->GetLargestPossibleRegion().GetSize()[1]); _geoInfo.setNumSlices(_data->GetLargestPossibleRegion().GetSize()[2]); if (_geoInfo.getNumColumns() == 0 || _geoInfo.getNumRows() == 0 || _geoInfo.getNumSlices() == 0) { throw core::InvalidDoseException("Empty dicom dose!") ; } - return true; - } } } } diff --git a/code/io/itk/rttbITKImageAccessor.h b/code/io/itk/rttbITKImageAccessor.h index 84a9f37..a02a26d 100644 --- a/code/io/itk/rttbITKImageAccessor.h +++ b/code/io/itk/rttbITKImageAccessor.h @@ -1,89 +1,89 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __ITK_IMAGE_ACCESSOR_H #define __ITK_IMAGE_ACCESSOR_H #include "rttbAccessorWithGeoInfoBase.h" #include "rttbBaseType.h" #include "itkImage.h" #include "RTTBITKIOExports.h" namespace rttb { namespace io { namespace itk { /*! @class ITKImageAccessor @brief This class gives access to information stored in an itk image @details There is no value scaling, i.e., it is assumed that the values in the itkImage are absolute. */ class RTTBITKIO_EXPORT ITKImageAccessor : public core::AccessorWithGeoInfoBase { public: typedef ::itk::Image ITKImageType; private: /** @brief The dose as itkImage */ ITKImageType::ConstPointer _data; IDType _UID; /*! @brief constructor @exception InvalidDoseException if _dose is nullptr */ ITKImageAccessor() = delete; /*! @brief get all required data from the itk image contained in _dose @exception InvalidDoseException if PixelSpacing is 0 or size in any dimension is 0. */ - bool assembleGeometricInfo(); + void assembleGeometricInfo() override; public: ~ITKImageAccessor() override; /*! @brief Constructor. Initialization with a itk image @pre image must be a valid instance (and !nullptr) @note the doseImage pixels are assumed absolute */ ITKImageAccessor(ITKImageType::ConstPointer image); /*! @brief returns the dose for an id */ GenericValueType getValueAt(const VoxelGridID aID) const override; /*! @brief returns the dose for an index */ GenericValueType getValueAt(const VoxelGridIndex3D& aIndex) const override; const IDType getUID() const override { return _UID; }; }; } } } #endif diff --git a/code/models/rttbLQModelAccessor.cpp b/code/models/rttbLQModelAccessor.cpp index 2c8e5f0..edc564d 100644 --- a/code/models/rttbLQModelAccessor.cpp +++ b/code/models/rttbLQModelAccessor.cpp @@ -1,103 +1,100 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include "rttbLQModelAccessor.h" #include "rttbDoseBasedModels.h" #include "rttbInvalidDoseException.h" #include "rttbInvalidParameterException.h" #include "rttbMappingOutsideOfImageException.h" namespace rttb { namespace models { LQModelAccessor::~LQModelAccessor() = default; LQModelAccessor::LQModelAccessor(DoseAccessorPointer dose, BioModelParamType alpha, BioModelParamType beta, unsigned int nFractions, double doseScaling) : _dose(dose), _alpha(alpha), _beta(beta), _nFractions(nFractions), _alphaMap(nullptr), _betaMap(nullptr), _doseScaling(doseScaling), _withAlphaBetaMaps(false) { if (_dose == nullptr) { throw core::InvalidDoseException("Dose is nullptr"); } if (_doseScaling < 0) { throw core::InvalidParameterException("Dose Scaling must be >0"); } assembleGeometricInfo(); } LQModelAccessor::LQModelAccessor(DoseAccessorPointer dose, DoseAccessorPointer alphaMap, DoseAccessorPointer betaMap, unsigned int nFractions, double doseScaling) :_dose(dose), _alpha(-1.), _beta(-1.), _nFractions(nFractions), _alphaMap(betaMap), _betaMap(alphaMap), _doseScaling(doseScaling), _withAlphaBetaMaps(true) { if (_dose == nullptr || _alphaMap == nullptr || _betaMap == nullptr) { throw core::InvalidDoseException("Dose or alphaMap or betaMap is nullptr"); } if (_doseScaling < 0) { throw core::InvalidParameterException("Dose Scaling must be >0"); } assembleGeometricInfo(); } GenericValueType LQModelAccessor::getValueAt(const VoxelGridID aID) const { VoxelGridIndex3D aVoxelGridIndex3D; if (_geoInfo.convert(aID, aVoxelGridIndex3D)) { return getValueAt(aVoxelGridIndex3D); } else { throw core::MappingOutsideOfImageException("Error in conversion from index to world coordinates"); } } GenericValueType LQModelAccessor::getValueAt(const VoxelGridIndex3D& aIndex) const { if (_withAlphaBetaMaps){ return calcLQ(_dose->getValueAt(aIndex) * _doseScaling, _alphaMap->getValueAt(aIndex), _betaMap->getValueAt(aIndex), _nFractions); } else { return calcLQ(_dose->getValueAt(aIndex) * _doseScaling, _alpha, _beta, _nFractions); } } - bool LQModelAccessor::assembleGeometricInfo() + void LQModelAccessor::assembleGeometricInfo() { _geoInfo = _dose->getGeometricInfo(); - - return true; - } } } diff --git a/code/models/rttbLQModelAccessor.h b/code/models/rttbLQModelAccessor.h index 745375f..535ca66 100644 --- a/code/models/rttbLQModelAccessor.h +++ b/code/models/rttbLQModelAccessor.h @@ -1,92 +1,92 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __LQ_MODEL_ACCESSOR_H #define __LQ_MODEL_ACCESSOR_H #include "rttbAccessorWithGeoInfoBase.h" #include "rttbDoseAccessorInterface.h" #include "rttbBaseTypeModels.h" namespace rttb { namespace models { /*! @class LQModelAccessor @brief This class gives access to the LQ Model information in an image */ class LQModelAccessor: public core::AccessorWithGeoInfoBase { public: using DoseAccessorPointer = core::DoseAccessorInterface::DoseAccessorPointer; private: DoseAccessorPointer _dose; BioModelParamType _alpha; BioModelParamType _beta; unsigned int _nFractions; DoseAccessorPointer _alphaMap; DoseAccessorPointer _betaMap; double _doseScaling; bool _withAlphaBetaMaps; IDType _bioModelUID; LQModelAccessor() = delete; /*! @brief get all required data from the dose geometric info */ - bool assembleGeometricInfo(); + void assembleGeometricInfo() override; public: ~LQModelAccessor() override; /*! @brief Constructor. @pre dose must be a valid instance (and != nullptr) @exception InvalidDoseException if _dose is nullptr */ LQModelAccessor(DoseAccessorPointer dose, BioModelParamType alpha, BioModelParamType beta, unsigned int nFractions=1, double doseScaling = 1.0); /*! @brief Constructor. @pre dose must be a valid instance (and != nullptr) @exception InvalidDoseException if dose is nullptr, if alphaMap is nullptr or if betaMap is nullptr */ LQModelAccessor(DoseAccessorPointer dose, DoseAccessorPointer alphaMap, DoseAccessorPointer betaMap, unsigned int nFractions = 1, double doseScaling = 1.0); /*! @brief returns the LQ Model value for an id */ GenericValueType getValueAt(const VoxelGridID aID) const override; /*! @brief returns the LQ Model value for an index */ GenericValueType getValueAt(const VoxelGridIndex3D& aIndex) const override; const IDType getUID() const override { return _bioModelUID; }; }; } } #endif diff --git a/testing/core/DummyDoseAccessor.cpp b/testing/core/DummyDoseAccessor.cpp index a35da52..5d42d3b 100644 --- a/testing/core/DummyDoseAccessor.cpp +++ b/testing/core/DummyDoseAccessor.cpp @@ -1,96 +1,100 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #include #include #include #include "DummyDoseAccessor.h" #include "rttbNullPointerException.h" #include "rttbInvalidDoseException.h" #include "rttbIndexOutOfBoundsException.h" namespace rttb { namespace testing { DummyDoseAccessor::~DummyDoseAccessor() {} DummyDoseAccessor::DummyDoseAccessor() { boost::uuids::uuid id; boost::uuids::random_generator generator; id = generator(); std::stringstream ss; ss << id; _doseUID = "DummyDoseAccessor_" + ss.str(); - SpacingVectorType3D aVector(2.5); - _geoInfo.setSpacing(aVector); - WorldCoordinate3D anOtherVector(-25, -2, 35); - _geoInfo.setImagePositionPatient(anOtherVector); - _geoInfo.setNumRows(11); - _geoInfo.setNumColumns(10); - _geoInfo.setNumSlices(10); - - OrientationMatrix unit = OrientationMatrix(); - _geoInfo.setOrientationMatrix(unit); + assembleGeometricInfo(); for (int i = 0; i < _geoInfo.getNumberOfVoxels(); i++) { doseData.push_back((double(rand()) / RAND_MAX) * 1000); } } DummyDoseAccessor::DummyDoseAccessor(const std::vector& aDoseVector, const core::GeometricInfo& geoInfo) { boost::uuids::uuid id; boost::uuids::random_generator generator; id = generator(); std::stringstream ss; ss << id; _doseUID = "DummyDoseAccessor_" + ss.str(); doseData = aDoseVector; _geoInfo = geoInfo; } + void DummyDoseAccessor::assembleGeometricInfo() { + SpacingVectorType3D aVector(2.5); + _geoInfo.setSpacing(aVector); + WorldCoordinate3D anOtherVector(-25, -2, 35); + _geoInfo.setImagePositionPatient(anOtherVector); + _geoInfo.setNumRows(11); + _geoInfo.setNumColumns(10); + _geoInfo.setNumSlices(10); + + OrientationMatrix unit = OrientationMatrix(); + _geoInfo.setOrientationMatrix(unit); + } + GenericValueType DummyDoseAccessor::getValueAt(const VoxelGridID aID) const { if (!_geoInfo.validID(aID)) { throw core::IndexOutOfBoundsException("Not a valid Position!"); } return doseData.at(aID); } GenericValueType DummyDoseAccessor::getValueAt(const VoxelGridIndex3D& aIndex) const { VoxelGridID gridID = 0; _geoInfo.convert(aIndex, gridID); return getValueAt(gridID); } }//end namespace testing }//end namespace rttb \ No newline at end of file diff --git a/testing/core/DummyDoseAccessor.h b/testing/core/DummyDoseAccessor.h index 6c47856..067e76f 100644 --- a/testing/core/DummyDoseAccessor.h +++ b/testing/core/DummyDoseAccessor.h @@ -1,82 +1,84 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ #ifndef __DUMMY_DOSE_ACCESSOR_H #define __DUMMY_DOSE_ACCESSOR_H #include #include #include "rttbAccessorWithGeoInfoBase.h" #include "rttbGeometricInfo.h" #include "rttbBaseType.h" namespace rttb { namespace testing { /*! @class DummyDoseAccessor @brief A dummy DoseAccessor for testing filled with random dose values between 0 and 100. The default grid size is [11,10,5] */ class DummyDoseAccessor: public core::AccessorWithGeoInfoBase { private: /*! vector of dose data(absolute Gy dose/doseGridScaling)*/ std::vector doseData; IDType _doseUID; public: ~DummyDoseAccessor(); /*! @brief A dummy DoseAccessor for testing filled with random dose values between 0 and 100. The default grid size is [11,10,5] */ DummyDoseAccessor(); /*! @brief Constructor. Initialisation of dose with a given vector. */ DummyDoseAccessor(const std::vector& aDoseVector, const core::GeometricInfo& geoInfo); + void assembleGeometricInfo() override; + const std::vector* getDoseVector() const { return &doseData; }; GenericValueType getValueAt(const VoxelGridID aID) const; GenericValueType getValueAt(const VoxelGridIndex3D& aIndex) const; const IDType getUID() const { return _doseUID; }; }; } } #endif