diff --git a/code/io/dicom/CMakeLists.txt b/code/io/dicom/CMakeLists.txt index 1f2883c..7ca61b2 100644 --- a/code/io/dicom/CMakeLists.txt +++ b/code/io/dicom/CMakeLists.txt @@ -1 +1 @@ -RTTB_CREATE_MODULE(RTTBDicomIO DEPENDS RTTBCore PACKAGE_DEPENDS BoostBinaries DCMTK) \ No newline at end of file +RTTB_CREATE_MODULE(RTTBDicomIO DEPENDS RTTBCore RTTBAlgorithms PACKAGE_DEPENDS BoostBinaries DCMTK) \ No newline at end of file diff --git a/code/io/dicom/files.cmake b/code/io/dicom/files.cmake index 76f1ec9..5cd768c 100644 --- a/code/io/dicom/files.cmake +++ b/code/io/dicom/files.cmake @@ -1,21 +1,25 @@ SET(CPP_FILES rttbDcmrtException.cpp rttbDicomDoseAccessor.cpp + rttbDicomFileDoseAccessorConverter.cpp rttbDicomFileDoseAccessorGenerator.cpp rttbDicomFileReaderHelper.cpp rttbDicomFileStructureSetGenerator.cpp + rttbDicomIODDoseAccessorConverter.cpp rttbDicomIODDoseAccessorGenerator.cpp rttbDicomIODStructureSetGenerator.cpp rttbDVHDicomFileReader.cpp ) SET(H_FILES rttbDcmrtException.h rttbDicomDoseAccessor.h + rttbDicomFileDoseAccessorConverter.h rttbDicomFileDoseAccessorGenerator.h rttbDicomFileReaderHelper.h rttbDicomFileStructureSetGenerator.h + rttbDicomIODDoseAccessorConverter.h rttbDicomIODDoseAccessorGenerator.h rttbDicomIODStructureSetGenerator.h rttbDVHDicomFileReader.h ) diff --git a/code/io/dicom/rttbDicomFileDoseAccessorConverter.cpp b/code/io/dicom/rttbDicomFileDoseAccessorConverter.cpp new file mode 100644 index 0000000..550fcd9 --- /dev/null +++ b/code/io/dicom/rttbDicomFileDoseAccessorConverter.cpp @@ -0,0 +1,280 @@ +// ----------------------------------------------------------------------- +// 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: 747 $ (last changed revision) +// @date $Date: 2014-09-17 12:01:00 +0200 (Mi, 17 Sep 2014) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + + +#include +#include +#include +#include + +#include "rttbDicomFileDoseAccessorConverter.h" +#include "rttbDicomIODDoseAccessorConverter.h" +#include "rttbInvalidDoseException.h" +#include "rttbGeometricInfo.h" +#include "rttbGeometricInfo.h" +#include "rttbGenericDoseIterator.h" +#include "rttbDoseStatistics.h" + +namespace rttb +{ + namespace io + { + namespace dicom + { + + DicomFileDoseAccessorConverter::DicomFileDoseAccessorConverter(DoseAccessorPointer aAccessor, DICOMRTFileNameString aFileName) + :_fileName(aFileName) + { + setDoseAccessor(aAccessor); + _doseIOD = boost::make_shared(); + _dataset = _fileformat.getDataset(); + } + + bool DicomFileDoseAccessorConverter::process() + { + std::string s; + OFCondition status; + + /* Prepare dcmtk */ + DcmItem *dcm_item = 0; + + //get geometric info + rttb::core::GeometricInfo geometricInfo = _doseAccessor->getGeometricInfo(); + + + /* ----------------------------------------------------------------- */ + /* Part 1 -- General header */ + /* ----------------------------------------------------------------- */ + OFString CreationUID(_doseAccessor->getDoseUID().c_str()); + _dataset->putAndInsertString (DCM_ImageType, + "DERIVED\\SECONDARY\\REFORMATTED"); + _dataset->putAndInsertOFStringArray(DCM_InstanceCreationDate, + "");//Creation Date + _dataset->putAndInsertOFStringArray(DCM_InstanceCreationTime, + "");//Creation Time + _dataset->putAndInsertOFStringArray(DCM_InstanceCreatorUID, + CreationUID); + _dataset->putAndInsertString (DCM_SOPClassUID, UID_RTDoseStorage); + _dataset->putAndInsertString (DCM_SOPInstanceUID, + _doseAccessor->getDoseUID().c_str()); + _dataset->putAndInsertOFStringArray (DCM_StudyDate, + ""); + _dataset->putAndInsertOFStringArray (DCM_StudyTime, + ""); + _dataset->putAndInsertOFStringArray (DCM_AccessionNumber, ""); + _dataset->putAndInsertOFStringArray (DCM_Modality, "RTDOSE"); + _dataset->putAndInsertString (DCM_Manufacturer, "RTToolbox"); + _dataset->putAndInsertString (DCM_InstitutionName, ""); + _dataset->putAndInsertString (DCM_ReferringPhysicianName, ""); + _dataset->putAndInsertString (DCM_StationName, ""); + _dataset->putAndInsertString (DCM_ManufacturerModelName, "RTToolbox"); + + + /* (0008,1140) DCM_ReferencedImageSequence -- MIM likes this */ + dcm_item = 0; + _dataset->findOrCreateSequenceItem ( + DCM_ReferencedImageSequence, dcm_item, -2); + dcm_item->putAndInsertString (DCM_ReferencedSOPClassUID, + UID_CTImageStorage); + dcm_item->putAndInsertString (DCM_ReferencedSOPInstanceUID, + "");//ct_series_uid? + + _dataset->putAndInsertString (DCM_PatientName, ""); + _dataset->putAndInsertString (DCM_PatientID, ""); + _dataset->putAndInsertString (DCM_PatientBirthDate, ""); + _dataset->putAndInsertString (DCM_PatientSex, "O"); + _dataset->putAndInsertString (DCM_SliceThickness, boost::lexical_cast(geometricInfo.getSliceThickness()).c_str()); + _dataset->putAndInsertString (DCM_SoftwareVersions, + ""); + _dataset->putAndInsertString (DCM_StudyInstanceUID, + ""); + _dataset->putAndInsertString (DCM_SeriesInstanceUID, + ""); + _dataset->putAndInsertString (DCM_StudyID, "10001"); + _dataset->putAndInsertString (DCM_SeriesNumber, ""); + _dataset->putAndInsertString (DCM_InstanceNumber, "1"); + + s = string_format ("%g\\%g\\%g", geometricInfo.getImagePositionPatient().x(), + geometricInfo.getImagePositionPatient().y(), geometricInfo.getImagePositionPatient().z()); + /* GCS FIX: PatientOrientation */ + _dataset->putAndInsertString (DCM_PatientOrientation, "L/P"); + _dataset->putAndInsertString (DCM_ImagePositionPatient, s.c_str()); + s = string_format ("%g\\%g\\%g\\%g\\%g\\%g", + geometricInfo.getImageOrientationRow().x(), + geometricInfo.getImageOrientationRow().y(), + geometricInfo.getImageOrientationRow().z(), + geometricInfo.getImageOrientationColumn().x(), + geometricInfo.getImageOrientationColumn().y(), + geometricInfo.getImageOrientationColumn().z()); + _dataset->putAndInsertString (DCM_ImageOrientationPatient, s.c_str()); + _dataset->putAndInsertString (DCM_FrameOfReferenceUID, + "");//FrameOfReferenceUID? + + _dataset->putAndInsertString (DCM_SamplesPerPixel, "1"); + _dataset->putAndInsertString (DCM_PhotometricInterpretation, "MONOCHROME2"); + s = string_format ("%d", geometricInfo.getNumSlices()); + _dataset->putAndInsertString (DCM_NumberOfFrames, s.c_str()); + + /* GCS FIX: Add FrameIncrementPointer */ + _dataset->putAndInsertString (DCM_FrameIncrementPointer, + "(3004,000c)"); + + _dataset->putAndInsertUint16 (DCM_Rows, geometricInfo.getNumRows()); + _dataset->putAndInsertUint16 (DCM_Columns, geometricInfo.getNumColumns()); + s = string_format ("%g\\%g", + geometricInfo.getSpacing()(1), geometricInfo.getSpacing()(0)); + _dataset->putAndInsertString (DCM_PixelSpacing, s.c_str()); + + _dataset->putAndInsertString (DCM_BitsAllocated, "32"); + _dataset->putAndInsertString (DCM_BitsStored, "32"); + _dataset->putAndInsertString (DCM_HighBit, "31"); + /*if (dose_metadata + && dose_metadata->get_metadata(0x3004, 0x0004) == "ERROR") + { + _dataset->putAndInsertString (DCM_PixelRepresentation, "1"); + } else { + _dataset->putAndInsertString (DCM_PixelRepresentation, "0"); + }*/ + + _dataset->putAndInsertString (DCM_DoseUnits, "GY"); + /*dcmtk_copy_from_metadata (_dataset, dose_metadata, + DCM_DoseType, "PHYSICAL");*/ + _dataset->putAndInsertString (DCM_DoseSummationType, "PLAN"); + + s = std::string ("0"); + for (int i = 1; i < geometricInfo.getNumSlices(); i++) { + s += string_format ("\\%g", i * geometricInfo.getSpacing()(2));//*dose_volume->direction_cosines[8]? What is dose_volume->direction_cosines[8] + } + _dataset->putAndInsertString (DCM_GridFrameOffsetVector, s.c_str()); + + /* GCS FIX: + Leave ReferencedRTPlanSequence empty (until I can cross reference) */ + + /* We need to convert image to uint16_t, but first we need to + scale it so that the maximum dose fits in a 16-bit unsigned + integer. Compute an appropriate scaling factor based on the + maximum dose. */ + + + /* Find the maximum value in the image */ + boost::shared_ptr spTestDoseIterator = boost::make_shared(_doseAccessor); + rttb::core::GenericDoseIterator::DoseIteratorPointer spDoseIterator (spTestDoseIterator); + rttb::algorithms::DoseStatistics doseStat (spDoseIterator); + boost::shared_ptr< std::vector > > myResultPairs = + boost::make_shared< std::vector > >(); + rttb::algorithms::DoseStatistics::ResultListPointer spMyResultPairs(myResultPairs); + double minDose = doseStat.getMinimum(myResultPairs); + double maxDose = doseStat.getMaximum(myResultPairs); + + /* Find scale factor */ + float dose_scale; + dose_scale = maxDose /PixelDataMaxValue; + + /* Scale the image and add scale factor to _dataset */ + s = string_format ("%g", dose_scale); + _dataset->putAndInsertString (DCM_DoseGridScaling, s.c_str()); + + /* (300c,0002) ReferencedRTPlanSequence -- for future expansion */ + dcm_item = 0; + _dataset->findOrCreateSequenceItem ( + DCM_ReferencedRTPlanSequence, dcm_item, -2); + dcm_item->putAndInsertString (DCM_ReferencedSOPClassUID, + UID_RTPlanStorage); + dcm_item->putAndInsertString (DCM_ReferencedSOPInstanceUID, + "");//ReferencedSOPInstanceUID?? + + /* (300c,0060) DCM_ReferencedStructureSetSequence -- MIM likes this */ + dcm_item = 0; + _dataset->findOrCreateSequenceItem ( + DCM_ReferencedStructureSetSequence, dcm_item, -2); + dcm_item->putAndInsertString (DCM_ReferencedSOPClassUID, + UID_RTStructureSetStorage); + dcm_item->putAndInsertString (DCM_ReferencedSOPInstanceUID, + "");//ReferencedSOPInstanceUID?? + + /* Convert image bytes to integer, then add to _dataset */ + Uint16* pixelData; + int pixelCount = geometricInfo.getNumRows() * geometricInfo.getNumColumns() * geometricInfo.getNumSlices(); + pixelData = new Uint16[pixelCount]; + for(unsigned int i=0; igetDoseAt(i); + double pixelValue = doseValue/dose_scale; + if(pixelValue > PixelDataMaxValue){ + pixelValue = PixelDataMaxValue; + } + + pixelData[i] =boost::numeric_cast(pixelValue); + } + + status = _dataset->putAndInsertUint16Array (DCM_PixelData, + pixelData, pixelCount); + + if(!status.good()){ + throw core::InvalidDoseException("Error: put and insert pixel data failed!"); + } + + return true; + } + + void DicomFileDoseAccessorConverter::writeDicomDoseFile(){ + OFCondition status; + + status = _fileformat.saveFile (_fileName.c_str(), EXS_LittleEndianExplicit); + if (status.bad()) { + std::cerr << "Error: cannot write DICOM RTDOSE!" << std::endl; + } + + } + + std::string DicomFileDoseAccessorConverter::string_format (const char *fmt, va_list ap) + { + int size=100; + std::string str; + while (1) { + str.resize(size); + va_list ap_copy = ap; //va_list ap_copy; va_copy (ap_copy, ap); va_copy() is supported starting in Visual Studio 2013. + int n = vsnprintf((char *)str.c_str(), size, fmt, ap_copy); + va_end (ap_copy); + if (n > -1 && n < size) { + str = std::string (str.c_str()); /* Strip excess padding */ + return str; + } + if (n > -1) + size=n+1; + else + size*=2; + } + } + + std::string DicomFileDoseAccessorConverter::string_format (const char *fmt, ...) + { + va_list ap; + va_start (ap, fmt); + std::string string = string_format (fmt, ap); + va_end (ap); + return string; + } + + + }//end namespace itk + }//end namespace io +}//end namespace rttb + diff --git a/code/io/dicom/rttbDicomFileDoseAccessorConverter.h b/code/io/dicom/rttbDicomFileDoseAccessorConverter.h new file mode 100644 index 0000000..9558143 --- /dev/null +++ b/code/io/dicom/rttbDicomFileDoseAccessorConverter.h @@ -0,0 +1,88 @@ +// ----------------------------------------------------------------------- +// RTToolbox - DKFZ radiotherapy quantitative evaluation library +// +// Copyright (c) German Cancer Research Center (DKFZ), +// Software development for Integrated Diagnostics and Therapy (SIDT). +// ALL RIGHTS RESERVED. +// See rttbCopyright.txt or +// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notices for more information. +// +//------------------------------------------------------------------------ +/*! +// @file +// @version $Revision: 793 $ (last changed revision) +// @date $Date: 2014-10-10 10:24:45 +0200 (Fr, 10 Okt 2014) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ +#ifndef __DICOM_FILE_DOSE_ACCESSOR_CONVERTER_H +#define __DICOM_FILE_DOSE_ACCESSOR_CONVERTER_H + + +#include "../itk/rttbDoseAccessorProcessorBase.h" +#include "../itk/rttbDoseAccessorConversionSettingInterface.h" +#include "rttbDicomDoseAccessor.h" + +namespace rttb +{ + namespace io + { + namespace dicom + { + /*! @class DicomFileDoseAccessorConverter + @brief Class converts/dumps the processed accessor into an dicom file + @remark DoseAccessorConversionInterface defines how the converter should react on non valid dose values. + */ + class DicomFileDoseAccessorConverter: public core::DoseAccessorProcessorBase, + public core::DoseAccessorConversionSettingInterface + { + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef DicomDoseAccessor::DRTDoseIODPtr DRTDoseIODPointer; + + /*! @brief Constructor. Initialisation with a dose accessor and a file name to write the dose + @param aFileName a file name to write the dose + */ + DicomFileDoseAccessorConverter(DoseAccessorPointer aAccessor, DICOMRTFileNameString aFileName); + + virtual ~DicomFileDoseAccessorConverter() {}; + + /*! @brief Convert the accessor into dicom dataset + @exception InvalidDoseException thrown if put and insert pixel data into dicom dataset failed + */ + bool process(); + + /*! @brief Write dicom dataset to a file + @pre process() should be true before writeDicomDoseFile() is called + */ + void writeDicomDoseFile(); + + + private: + DicomFileDoseAccessorConverter(const + DicomFileDoseAccessorConverter&); //not implemented on purpose -> non-copyable + DicomFileDoseAccessorConverter& operator=(const + DicomFileDoseAccessorConverter&);//not implemented on purpose -> non-copyable + + /* code from plastimatch + std::string formatting by Erik Aronesty + http://stackoverflow.com/questions/2342162/stdstring-formating-like-sprintf + Distributed under Attribution-ShareAlike 3.0 Unported license (CC BY-SA 3.0) + http://creativecommons.org/licenses/by-sa/3.0/ + */ + std::string string_format (const char *fmt, va_list ap); + std::string string_format (const char *fmt, ...); + + DRTDoseIODPointer _doseIOD; + DICOMRTFileNameString _fileName; + DcmFileFormat _fileformat; + DcmDataset *_dataset; + + }; + } + } +} +#endif diff --git a/code/io/dicom/rttbDicomIODDoseAccessorConverter.cpp b/code/io/dicom/rttbDicomIODDoseAccessorConverter.cpp new file mode 100644 index 0000000..a019a56 --- /dev/null +++ b/code/io/dicom/rttbDicomIODDoseAccessorConverter.cpp @@ -0,0 +1,56 @@ +// ----------------------------------------------------------------------- +// 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: 747 $ (last changed revision) +// @date $Date: 2014-09-17 12:01:00 +0200 (Mi, 17 Sep 2014) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ + +#include +#include +#include /* va_list, va_start, va_copy, va_arg, va_end */ + + +#include +#include +#include + +#include "rttbDicomIODDoseAccessorConverter.h" +#include "rttbException.h" +#include "rttbInvalidDoseException.h" +#include "rttbGeometricInfo.h" +#include "rttbGenericDoseIterator.h" +#include "rttbDoseStatistics.h" + + +namespace rttb +{ + namespace io + { + namespace dicom + { + + DicomIODDoseAccessorConverter::DicomIODDoseAccessorConverter(DoseAccessorPointer accessor) + { + setDoseAccessor(accessor); + _doseIOD = boost::make_shared(); + } + + + }//end namespace itk + }//end namespace io +}//end namespace rttb + diff --git a/code/io/dicom/rttbDicomIODDoseAccessorConverter.h b/code/io/dicom/rttbDicomIODDoseAccessorConverter.h new file mode 100644 index 0000000..0d7242a --- /dev/null +++ b/code/io/dicom/rttbDicomIODDoseAccessorConverter.h @@ -0,0 +1,77 @@ +// ----------------------------------------------------------------------- +// 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: 793 $ (last changed revision) +// @date $Date: 2014-10-10 10:24:45 +0200 (Fr, 10 Okt 2014) $ (last change date) +// @author $Author: hentsch $ (last changed by) +*/ +#ifndef __DICOM_IOD_DOSE_ACCESSOR_CONVERTER_H +#define __DICOM_IOD_DOSE_ACCESSOR_CONVERTER_H + + +#include "../itk/rttbDoseAccessorProcessorBase.h" +#include "../itk/rttbDoseAccessorConversionSettingInterface.h" +#include "rttbDicomDoseAccessor.h" + +#include + +//pixel data max value +#define PixelDataMaxValue UINT16_MAX + +namespace rttb +{ + namespace io + { + namespace dicom + { + + /*! @class DicomIODDoseAccessorConverter + @brief Class converts/dumps the processed accessor into an dicom dose iod + @remark DoseAccessorConversionInterface defines how the converter should react on non valid dose values. + @remark Not implemented because of no usage now, please use DicomFileDoseAccessorConverter + */ + class DicomIODDoseAccessorConverter: public core::DoseAccessorProcessorBase, + public core::DoseAccessorConversionSettingInterface + { + + public: + typedef core::DoseAccessorInterface::DoseAccessorPointer DoseAccessorPointer; + typedef DicomDoseAccessor::DRTDoseIODPtr DRTDoseIODPointer; + typedef boost::shared_ptr DcmItemPtr; + + DicomIODDoseAccessorConverter(DoseAccessorPointer accessor); + virtual ~DicomIODDoseAccessorConverter() {}; + + bool process();//not implemented because of no usage now, please use DicomFileDoseAccessorConverter + + DRTDoseIODPointer getDicomDoseIOD() + { + return _doseIOD; + } + + private: + DicomIODDoseAccessorConverter(const + DicomIODDoseAccessorConverter&); //not implemented on purpose -> non-copyable + DicomIODDoseAccessorConverter& operator=(const + DicomIODDoseAccessorConverter&);//not implemented on purpose -> non-copyable + + + DRTDoseIODPointer _doseIOD; + }; + } + } +} +#endif diff --git a/testing/io/dicom/CMakeLists.txt b/testing/io/dicom/CMakeLists.txt index ee8a29d..e0a0113 100644 --- a/testing/io/dicom/CMakeLists.txt +++ b/testing/io/dicom/CMakeLists.txt @@ -1,36 +1,40 @@ #----------------------------------------------------------------------------- # Setup the system information test. Write out some basic failsafe # information in case the test doesn't run. #----------------------------------------------------------------------------- SET(DICOMIO_TEST ${EXECUTABLE_OUTPUT_PATH}/rttbDicomIOTests) SET(TEST_DATA_ROOT ${RTTBTesting_SOURCE_DIR}/data) SET(TEMP ${RTTBTesting_BINARY_DIR}/temporary) #----------------------------------------------------------------------------- +ADD_TEST(DicomDoseAccessorConverterTest ${DICOMIO_TEST} DicomDoseAccessorConverterTest +"${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" +"${TEST_DATA_ROOT}/DICOM/TestDose/dose_w.dcm" ) + ADD_TEST(DicomDoseAccessorGeneratorTest ${DICOMIO_TEST} DicomDoseAccessorGeneratorTest "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantFifty.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" ) ADD_TEST(DicomFileReaderHelperTest ${DICOMIO_TEST} DicomFileReaderHelperTest "${TEST_DATA_ROOT}/DICOM/Helax/" "${TEST_DATA_ROOT}/DICOM/Helax/____mm_1_1.2.276.0.28.19.977891832855880720695789165493875543457754809556.dcm" "${TEST_DATA_ROOT}/DICOM/Helax/____mm__1.2.276.0.28.19.142087956198378746376227895256244905653791675016.dcm" ) ADD_TEST(DicomIOTest ${DICOMIO_TEST} DicomIOTest "${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantTwo.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/ConstantFifty.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/LinearIncrease3D.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/dicompylerTestDose.dcm" "${TEST_DATA_ROOT}/DICOM/TestDose/InhomogeneousGrid.dcm" ) ADD_TEST(DicomStructureSetGeneratorTest ${DICOMIO_TEST} DicomStructureSetGeneratorTest "${TEST_DATA_ROOT}/DICOM/StructureSet/RS1.3.6.1.4.1.2452.6.841242143.1311652612.1170940299.4217870819.dcm" ) RTTB_CREATE_TEST_MODULE(rttbDicomIO DEPENDS RTTBDicomIO PACKAGE_DEPENDS Boost Litmus DCMTK) diff --git a/testing/io/dicom/DicomDoseAccessorConverterTest.cpp b/testing/io/dicom/DicomDoseAccessorConverterTest.cpp new file mode 100644 index 0000000..d435c8b --- /dev/null +++ b/testing/io/dicom/DicomDoseAccessorConverterTest.cpp @@ -0,0 +1,131 @@ +// ----------------------------------------------------------------------- +// 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: 856 $ (last changed revision) +// @date $Date: 2014-11-27 13:39:53 +0100 (Do, 27 Nov 2014) $ (last change date) +// @author $Author: zhangl $ (last changed by) +*/ + +// this file defines the rttbCoreTests for the test driver +// and all it expects is that you have a function called RegisterTests + +#include +#include + +#include + +#include "litCheckMacros.h" + +#include "rttbBaseType.h" +#include "rttbDicomDoseAccessor.h" +#include "rttbDicomFileDoseAccessorGenerator.h" +#include "rttbDicomFileDoseAccessorConverter.h" +#include "rttbInvalidDoseException.h" +#include "rttbDcmrtException.h" +#include "rttbInvalidParameterException.h" + + +namespace rttb +{ + + namespace testing + { + + /*!@brief DicomDoseAccessorGeneratorTest - test the generators for dicom data + 1) test dicom file generator generateDoseAccessor() + 2) test dicom IOD generator generateDoseAccessor() + */ + + int DicomDoseAccessorConverterTest(int argc, char* argv[]) + { + typedef boost::shared_ptr DRTDoseIODPointer; + typedef rttb::io::dicom::DicomDoseAccessor::DoseAccessorPointer DoseAccessorPointer; + + PREPARE_DEFAULT_TEST_REPORTING; + + //ARGUMENTS: + // 1: the file name of the dose to read + // 2: the file name of the dose to write + + std::string RTDOSE_FILENAME_R; + std::string RTDOSE_FILENAME_W; + + if (argc > 2) + { + RTDOSE_FILENAME_R = argv[1]; + RTDOSE_FILENAME_W = argv[2]; + } + double errorConstant = 1e-3; + DoseAccessorPointer doseAccessor_r = io::dicom::DicomFileDoseAccessorGenerator(RTDOSE_FILENAME_R.c_str()).generateDoseAccessor(); + + CHECK_NO_THROW(io::dicom::DicomFileDoseAccessorConverter(doseAccessor_r, RTDOSE_FILENAME_W)); + io::dicom::DicomFileDoseAccessorConverter doseConverter(doseAccessor_r, RTDOSE_FILENAME_W); + CHECK_EQUAL(doseConverter.process(), true); + CHECK_NO_THROW(doseConverter.writeDicomDoseFile()); + + + DoseAccessorPointer doseAccessor_w = io::dicom::DicomFileDoseAccessorGenerator(RTDOSE_FILENAME_W).generateDoseAccessor(); + + //Check geometricinfo + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImagePositionPatient().x(), doseAccessor_w->getGeometricInfo().getImagePositionPatient().x(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImagePositionPatient().y(), doseAccessor_w->getGeometricInfo().getImagePositionPatient().y(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImagePositionPatient().z(), doseAccessor_w->getGeometricInfo().getImagePositionPatient().z(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationColumn().x(), doseAccessor_w->getGeometricInfo().getImageOrientationColumn().x(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationColumn().y(), doseAccessor_w->getGeometricInfo().getImageOrientationColumn().y(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationColumn().z(), doseAccessor_w->getGeometricInfo().getImageOrientationColumn().z(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationRow().x(), doseAccessor_w->getGeometricInfo().getImageOrientationRow().x(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationRow().y(), doseAccessor_w->getGeometricInfo().getImageOrientationRow().y(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getImageOrientationRow().z(), doseAccessor_w->getGeometricInfo().getImageOrientationRow().z(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getSpacing().x(), doseAccessor_w->getGeometricInfo().getSpacing().x(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getSpacing().y(), doseAccessor_w->getGeometricInfo().getSpacing().y(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getSpacing().z(), doseAccessor_w->getGeometricInfo().getSpacing().z(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getNumColumns(), doseAccessor_w->getGeometricInfo().getNumColumns(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getNumRows(), doseAccessor_w->getGeometricInfo().getNumRows(), + errorConstant); + CHECK_CLOSE(doseAccessor_r->getGeometricInfo().getNumSlices(), doseAccessor_w->getGeometricInfo().getNumSlices(), + errorConstant); + + //Check pixel data + int size = doseAccessor_r->getGeometricInfo().getNumColumns() * doseAccessor_r->getGeometricInfo().getNumRows() * + doseAccessor_r->getGeometricInfo().getNumSlices() ; + for(unsigned int index=0; index<30; index++){ + CHECK_CLOSE(doseAccessor_r->getDoseAt(index), doseAccessor_w->getDoseAt(index), errorConstant); + if(size/2 - index >=0 && size/2-index < size){ + CHECK_CLOSE(doseAccessor_r->getDoseAt(size/2 - index), doseAccessor_w->getDoseAt(size/2 - index), errorConstant); + } + CHECK_CLOSE(doseAccessor_r->getDoseAt(size-index-1), doseAccessor_w->getDoseAt(size-index-1), errorConstant); + } + + + RETURN_AND_REPORT_TEST_SUCCESS; + } + + }//testing +}//rttb + diff --git a/testing/io/dicom/files.cmake b/testing/io/dicom/files.cmake index f20cf2e..6ca2cfc 100644 --- a/testing/io/dicom/files.cmake +++ b/testing/io/dicom/files.cmake @@ -1,10 +1,11 @@ SET(CPP_FILES + DicomDoseAccessorConverterTest.cpp DicomDoseAccessorGeneratorTest.cpp DicomFileReaderHelperTest.cpp DicomIOTest.cpp DicomStructureSetGeneratorTest.cpp rttbIOTests.cpp ) SET(H_FILES ) diff --git a/testing/io/dicom/rttbIOTests.cpp b/testing/io/dicom/rttbIOTests.cpp index 746cde2..c38bc0c 100644 --- a/testing/io/dicom/rttbIOTests.cpp +++ b/testing/io/dicom/rttbIOTests.cpp @@ -1,62 +1,63 @@ // ----------------------------------------------------------------------- // RTToolbox - DKFZ radiotherapy quantitative evaluation library // // Copyright (c) German Cancer Research Center (DKFZ), // Software development for Integrated Diagnostics and Therapy (SIDT). // ALL RIGHTS RESERVED. // See rttbCopyright.txt or // http://www.dkfz.de/en/sidt/projects/rttb/copyright.html // // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notices for more information. // //------------------------------------------------------------------------ /*! // @file // @version $Revision$ (last changed revision) // @date $Date$ (last change date) // @author $Author$ (last changed by) */ // this file defines the rttbCoreTests for the test driver // and all it expects is that you have a function called RegisterTests #if defined(_MSC_VER) #pragma warning ( disable : 4786 ) #endif #include "litMultiTestsMain.h" namespace rttb{ namespace testing{ void registerTests() - { - LIT_REGISTER_TEST(DicomDoseAccessorGeneratorTest); - LIT_REGISTER_TEST(DicomFileReaderHelperTest); - LIT_REGISTER_TEST(DicomIOTest); + { + LIT_REGISTER_TEST(DicomDoseAccessorConverterTest); + LIT_REGISTER_TEST(DicomDoseAccessorGeneratorTest); + LIT_REGISTER_TEST(DicomFileReaderHelperTest); + LIT_REGISTER_TEST(DicomIOTest); LIT_REGISTER_TEST(DicomStructureSetGeneratorTest); - - } + } } +} int main(int argc, char* argv[]) - { +{ int result = 0; rttb::testing::registerTests(); try - { + { result = lit::multiTestsMain(argc,argv); - } + } catch(...) - { + { result = -1; - } + } return result; - } +}