diff --git a/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp b/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp index 461c6d482e..79859938a1 100644 --- a/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp +++ b/Modules/DICOMReader/src/mitkDICOMTagsOfInterestHelper.cpp @@ -1,177 +1,194 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY { } without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMTagsOfInterestHelper.h" #include #include "usModuleContext.h" #include "usGetModuleContext.h" mitk::DICOMTagPathMapType::value_type MakeLegacyEntry(const std::string& propName, const mitk::DICOMTag& tag) { return std::make_pair(tag, propName); } mitk::DICOMTagPathMapType::value_type MakeEntry(const mitk::DICOMTagPath& tagPath) { return std::make_pair(tagPath, ""); } mitk::DICOMTagPathMapType mitk::GetCurrentDICOMTagsOfInterest() { mitk::DICOMTagPathMapType result; std::vector > toiRegisters = us::GetModuleContext()->GetServiceReferences(); if (toiRegisters.empty()) { // bad, no service found, cannot get tags of interest MITK_ERROR << "DICOM tag error: no service for DICOM tags of interest"; return result; } else if (toiRegisters.size() > 1) { MITK_WARN << "DICOM tag error: multiple service for DICOM tags of interest found. Using just one."; } IDICOMTagsOfInterest* toiRegister = us::GetModuleContext()->GetService(toiRegisters.front()); if (!toiRegister) { MITK_ERROR << "Service lookup error, cannot get DICOM tag of interest service "; } return toiRegister->GetTagsOfInterest(); } mitk::DICOMTagPathMapType mitk::GetDefaultDICOMTagsOfInterest() { DICOMTagPathMapType result; //These tags are copied from DICOMSeriesReader. The old naming style (deprecated) - //is keept for backwards compatibility until it is removed. + //is kept for backwards compatibility until it is removed. //Below we have also already added the properties with the new naming style // Patient module /*dicom.patient.PatientsName*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0010))); /*dicom.patient.PatientID*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0020))); /*dicom.patient.PatientsBirthDate*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0030))); /*dicom.patient.PatientsSex*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0040))); /*dicom.patient.PatientsBirthTime*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0032))); /*dicom.patient.OtherPatientIDs*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x1000))); /*dicom.patient.OtherPatientNames*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x1001))); /*dicom.patient.EthnicGroup*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x2160))); /*dicom.patient.PatientComments*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x4000))); /*dicom.patient.PatientIdentityRemoved*/ result.insert(MakeEntry(DICOMTag(0x0012, 0x0062))); /*dicom.patient.DeIdentificationMethod*/ result.insert(MakeEntry(DICOMTag(0x0012, 0x0063))); // General Study module /*dicom.study.StudyInstanceUID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x000d))); /*dicom.study.StudyDate*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0020))); /*dicom.study.StudyTime*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0030))); /*dicom.study.ReferringPhysiciansName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0090))); /*dicom.study.StudyID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0010))); /*dicom.study.AccessionNumber*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0050))); /*dicom.study.StudyDescription*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1030))); /*dicom.study.PhysiciansOfRecord*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1048))); /*dicom.study.NameOfPhysicianReadingStudy*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1060))); // General Series module /*dicom.series.Modality*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0060))); /*dicom.series.SeriesInstanceUID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x000e))); /*dicom.series.SeriesNumber*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0011))); /*dicom.series.Laterality*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0060))); /*dicom.series.SeriesDate*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0021))); /*dicom.series.SeriesTime*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0031))); /*dicom.series.PerformingPhysiciansName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1050))); /*dicom.series.ProtocolName*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x1030))); /*dicom.series.SeriesDescription*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x103e))); /*dicom.series.OperatorsName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1070))); /*dicom.series.BodyPartExamined*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x0015))); /*dicom.series.PatientPosition*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x5100))); /*dicom.series.SmallestPixelValueInSeries*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0108))); /*dicom.series.LargestPixelValueInSeries*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0109))); // VOI LUT module /*dicom.voilut.WindowCenter*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1050))); /*dicom.voilut.WindowWidth*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1051))); /*dicom.voilut.WindowCenterAndWidthExplanation*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1055))); // Image Pixel module /*dicom.pixel.PhotometricInterpretation*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0004))); /*dicom.pixel.Rows*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0010))); /*dicom.pixel.Columns*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0011))); // Image Plane module /*dicom.PixelSpacing*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0030))); /*dicom.ImagerPixelSpacing*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x1164))); //additional for RT /*dicom.RescaleIntercept*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1052))); /*dicom.RescaleSlope*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1053))); /*dicom.ManufacturerModelName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1090))); /*dicom.ManufacturerName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0070))); /*dicom.InstitutionName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0080))); /*dicom.StationName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1010))); /*dicom.DoseGridScaling*/ result.insert(MakeEntry(DICOMTag(0x3004, 0x000e))); + //Additions for RTPLAN + DICOMTagPath doseReferenceSequence; + doseReferenceSequence.AddAnySelection(0x300A, 0x0010); + DICOMTagPath fractionGroupSequence; + fractionGroupSequence.AddAnySelection(0x300A, 0x0070); + DICOMTagPath beamSequence; + beamSequence.AddAnySelection(0x300A, 0x00B0); + DICOMTagPath referencedStructureSetSequence; + referencedStructureSetSequence.AddAnySelection(0x300C, 0x0060); + result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0013))); //dicom.DoseReferenceSequence.DoseReferenceUID + result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0016))); //dicom.DoseReferenceSequence.DoseReferenceDescription + result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0026))); //dicom.DoseReferenceSequence.TargetPrescriptionDose + result.insert(MakeEntry(DICOMTagPath(fractionGroupSequence).AddElement(0x300A, 0x0078))); //dicom.FractionGroupSequence.NumberOfFractionsPlanned + result.insert(MakeEntry(DICOMTagPath(fractionGroupSequence).AddElement(0x300A, 0x0080))); //dicom.FractionGroupSequence.NumberOfBeams + result.insert(MakeEntry(DICOMTagPath(beamSequence).AddElement(0x300A, 0x00C6))); //dicom.BeamSequence.RadiationType + result.insert(MakeEntry(DICOMTagPath(referencedStructureSetSequence).AddElement(0x0008, 0x1155))); //dicom.ReferencedStructureSetSequence.ReferencedSOPInstanceUID + //Additions for PET DICOMTagPath radioPharmaRootTag; radioPharmaRootTag.AddAnySelection(0x0054, 0x0016); DICOMTagPath radioNuclideRootTag(radioPharmaRootTag); radioNuclideRootTag.AddAnySelection(0x0054, 0x0300); result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x0031))); //dicom.pet.Radiopharmaceutical result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1072))); //dicom.pet.RadiopharmaceuticalStartTime result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1074))); //dicom.pet.RadionuclideTotalDose result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1075))); //dicom.pet.RadionuclideHalfLife result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1076))); //dicom.pet.RadionuclidePositronFraction result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0100))); //dicom.pet.Radionuclide.CodeValue result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0102))); //dicom.pet.Radionuclide.CodingSchemeDesignator result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0104))); //dicom.pet.Radionuclide.CodemManing result.insert(MakeEntry(DICOMTag(0x0054, 0x1001))); //dicom.pet.RadioactivityUnits result.insert(MakeEntry(DICOMTag(0x0054, 0x1102))); //dicom.pet.DecayCorrection result.insert(MakeEntry(DICOMTag(0x0054, 0x1321))); //dicom.pet.DecayFactor result.insert(MakeEntry(DICOMTag(0x0054, 0x1300))); //dicom.pet.FrameReferenceTime result.insert(MakeEntry(DICOMTag(0x0010, 0x1030))); //dicom.patient.PatientWeight result.insert(MakeEntry(DICOMTag(0x0010, 0x1020))); //dicom.patient.PatientSize //Other interesting acquisition correlated information result.insert(MakeEntry(DICOMTag(0x0008, 0x0022))); //dicom.acquisition date result.insert(MakeEntry(DICOMTag(0x0008, 0x0032))); //dicom.acquisition time result.insert(MakeEntry(DICOMTag(0x0008, 0x002a))); //dicom.acquisition datetime result.insert(MakeEntry(DICOMTag(0x0008, 0x0080))); //dicom.Modality result.insert(MakeEntry(DICOMTag(0x0018, 0x002a))); //dicom.Sequence Name result.insert(MakeEntry(DICOMTag(0x0018, 0x0020))); //dicom.Scanning Sequence result.insert(MakeEntry(DICOMTag(0x0018, 0x0021))); //dicom.Sequence Variant result.insert(MakeEntry(DICOMTag(0x0018, 0x0080))); //dicom.TR result.insert(MakeEntry(DICOMTag(0x0018, 0x0081))); //dicom.TE result.insert(MakeEntry(DICOMTag(0x0018, 0x1310))); //dicom.Acquisition Matrix result.insert(MakeEntry(DICOMTag(0x0018, 0x0087))); //dicom.Magnetic Field Strength //SOP result.insert(MakeEntry(DICOMTag(0x0008, 0x0018))); //SOP Instance UID result.insert(MakeEntry(DICOMTag(0x0020, 0x0013))); //Instance number result.insert(MakeEntry(DICOMTag(0x0020, 0x1041))); //Slice location return result; }; diff --git a/Modules/DicomRT/CMakeLists.txt b/Modules/DicomRT/CMakeLists.txt index eb5f4b43ba..592e612cf2 100644 --- a/Modules/DicomRT/CMakeLists.txt +++ b/Modules/DicomRT/CMakeLists.txt @@ -1,9 +1,9 @@ if(NOT DEFINED DCMTK_dcmrt_LIBRARY OR DCMTK_dcmrt_LIBRARY) mitk_create_module( - DEPENDS MitkSegmentation MitkSceneSerializationBase MitkLegacyIO MitkDICOMReader + DEPENDS MitkSegmentation MitkSceneSerializationBase MitkLegacyIO MitkDICOMReader MitkDICOMReaderServices PACKAGE_DEPENDS PUBLIC Qt5|Core DCMTK ) add_subdirectory(test) else() message("MITK DicomRT Support disabled because the DCMTK dcmrt library not found") endif() diff --git a/Modules/DicomRT/files.cmake b/Modules/DicomRT/files.cmake index dedcb7a47d..bbb35f627c 100644 --- a/Modules/DicomRT/files.cmake +++ b/Modules/DicomRT/files.cmake @@ -1,20 +1,21 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") SET(CPP_FILES mitkRTDoseReader.cpp + mitkRTPlanReader.cpp mitkRTConstants.cpp mitkIsoDoseLevel.cpp mitkIsoDoseLevelCollections.cpp mitkIsoDoseLevelSetProperty.cpp mitkIsoDoseLevelVectorProperty.cpp mitkRTStructureSetReader.cpp mitkDoseImageVtkMapper2D.cpp mitkIsoLevelsGenerator.cpp mitkDoseNodeHelper.cpp ) set(TPP_FILES ) set(MOC_H_FILES ) diff --git a/Modules/DicomRT/include/mitkRTPlanReader.h b/Modules/DicomRT/include/mitkRTPlanReader.h new file mode 100644 index 0000000000..cbfd29c079 --- /dev/null +++ b/Modules/DicomRT/include/mitkRTPlanReader.h @@ -0,0 +1,66 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef mitkDicomRTPlanReader_h +#define mitkDicomRTPlanReader_h + +#include "mitkAbstractFileReader.h" + +#include +#include + +#include "mitkImage.h" +#include "mitkIDICOMTagsOfInterest.h" +#include "mitkDICOMDatasetAccessingImageFrameInfo.h" + +#include + +#include "MitkDicomRTExports.h" + +namespace mitk +{ + /** + * \brief RTPlanReader reads DICOM files of modality RTPLAN. + \details The tags are defined in mitk::GetDefaultDICOMTagsOfInterest(). They are stored as TemporoSpatialStringProperty. + with the key as their respective DICOM tags. + \note No image information is in RTPLAN. + */ + class MITKDICOMRT_EXPORT RTPlanReader : public mitk::AbstractFileReader + { + + public: + typedef std::vector FindingsListVectorType; + RTPlanReader(); + RTPlanReader(const RTPlanReader& other); + + using AbstractFileReader::Read; + virtual std::vector > Read() override; + + virtual ~RTPlanReader(); + + private: + RTPlanReader* Clone() const override; + FindingsListVectorType ExtractPathsOfInterest(const DICOMTagPathList& pathsOfInterest, const DICOMDatasetAccessingImageFrameList& frames) const; + void SetProperties(Image::Pointer image, const FindingsListVectorType& findings) const; + mitk::IDICOMTagsOfInterest* GetDicomTagsOfInterestService() const; + + us::ServiceRegistration m_FileReaderServiceReg; + IDICOMTagsOfInterest* m_DICOMTagsOfInterestService; + }; +} + +#endif diff --git a/Modules/DicomRT/src/mitkRTPlanReader.cpp b/Modules/DicomRT/src/mitkRTPlanReader.cpp new file mode 100644 index 0000000000..dd4d7da911 --- /dev/null +++ b/Modules/DicomRT/src/mitkRTPlanReader.cpp @@ -0,0 +1,116 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#include "mitkRTPlanReader.h" + +#include "mitkDICOMDCMTKTagScanner.h" +#include "mitkIOMimeTypes.h" +#include "mitkDICOMTagPath.h" +#include "mitkDICOMTagsOfInterestService.h" +#include "mitkTemporoSpatialStringProperty.h" + +namespace mitk +{ + + RTPlanReader::RTPlanReader() : AbstractFileReader(IOMimeTypes::DICOM_MIMETYPE_NAME(), "DICOM RTPlan File Reader") { + m_FileReaderServiceReg = RegisterService(); + m_DICOMTagsOfInterestService = GetDicomTagsOfInterestService(); + } + + RTPlanReader::RTPlanReader(const RTPlanReader& other) : mitk::AbstractFileReader(other) + {} + + RTPlanReader::~RTPlanReader(){} + + std::vector > RTPlanReader::Read() + { + std::vector > result; + + auto tagsOfInterest = m_DICOMTagsOfInterestService->GetTagsOfInterest(); + DICOMTagPathList tagsOfInterestList; + for (const auto& tag : tagsOfInterest){ + tagsOfInterestList.push_back(tag.first); + } + + std::string location = GetInputLocation(); + mitk::StringList files = { location }; + mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); + scanner->SetInputFiles(files); + scanner->AddTagPaths(tagsOfInterestList); + scanner->Scan(); + + mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); + if (frames.empty()){ + MITK_ERROR << "Error reading the RTPLAN file" << std::endl; + return result; + } + + auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); + + //just create empty image. No image information available in RTPLAN. But properties will be attached. + Image::Pointer dummyImage = Image::New(); + SetProperties(dummyImage, findings); + + result.push_back(dummyImage.GetPointer()); + + return result; + } + + RTPlanReader* RTPlanReader::Clone() const + { + return new RTPlanReader(*this); + } + + RTPlanReader::FindingsListVectorType RTPlanReader::ExtractPathsOfInterest(const DICOMTagPathList& pathsOfInterest, const mitk::DICOMDatasetAccessingImageFrameList& frames) const + { + std::vector findings; + for (const auto& entry : pathsOfInterest){ + findings.push_back(frames.front()->GetTagValueAsString(entry)); + } + return findings; + } + + void RTPlanReader::SetProperties(Image::Pointer image, const FindingsListVectorType& findings) const + { + for (const auto& finding : findings){ + for (const auto& entry : finding){ + const std::string propertyName = mitk::DICOMTagPathToPropertyName(entry.path); + auto property = mitk::TemporoSpatialStringProperty::New(); + property->SetValue(entry.value); + image->SetProperty(propertyName.c_str(), property); + } + } + } + + mitk::IDICOMTagsOfInterest* RTPlanReader::GetDicomTagsOfInterestService() const + { + mitk::IDICOMTagsOfInterest* result = nullptr; + + std::vector > toiRegisters = us::GetModuleContext()->GetServiceReferences(); + if (!toiRegisters.empty()) + { + if (toiRegisters.size() > 1) + { + MITK_WARN << "Multiple DICOM tags of interest services found. Using just one."; + } + result = us::GetModuleContext()->GetService(toiRegisters.front()); + } + + return result; + } + +} diff --git a/Modules/DicomRT/test/files.cmake b/Modules/DicomRT/test/files.cmake index f9c7fa5d99..e589e7598e 100644 --- a/Modules/DicomRT/test/files.cmake +++ b/Modules/DicomRT/test/files.cmake @@ -1,4 +1,5 @@ SET(MODULE_TESTS mitkRTStructureSetReaderTest.cpp mitkRTDoseReaderTest.cpp + mitkRTPlanReaderTest.cpp ) diff --git a/Modules/DicomRT/test/mitkRTPlanReaderTest.cpp b/Modules/DicomRT/test/mitkRTPlanReaderTest.cpp new file mode 100644 index 0000000000..d75601ed0f --- /dev/null +++ b/Modules/DicomRT/test/mitkRTPlanReaderTest.cpp @@ -0,0 +1,80 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +#include "mitkRTPlanReader.h" +#include "mitkImage.h" +#include "mitkTemporoSpatialStringProperty.h" +#include "mitkBaseProperty.h" + +class mitkRTPlanReaderTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkRTPlanReaderTestSuite); + MITK_TEST(TestProperties); + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::Image::ConstPointer m_image; + +public: + + void setUp() override + { + auto rtPlanReader = mitk::RTPlanReader(); + rtPlanReader.SetInput(GetTestDataFilePath("RT/Plan/rtplan.dcm")); + auto readerOutput = rtPlanReader.Read(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("reader output should have one entry.", static_cast(1), static_cast(readerOutput.size())); + + m_image = dynamic_cast(readerOutput.at(0).GetPointer()); + CPPUNIT_ASSERT_EQUAL(m_image.IsNotNull(), true); + } + + void TestProperties() + { + CheckStringProperty("DICOM.300A.0010.[0].300A.0013", "1.2.246.352.72.11.320687012.17740.20090508173031"); + CheckStringProperty("DICOM.300A.0010.[1].300A.0013", "1.2.246.352.72.11.320687012.17741.20090508173031"); + CheckStringProperty("DICOM.300A.0010.[0].300A.0016", "Breast"); + CheckStringProperty("DICOM.300A.0010.[1].300A.0016", "CALC POINT"); + CheckStringProperty("DICOM.300A.0010.[0].300A.0026", "14"); + CheckStringProperty("DICOM.300A.0010.[1].300A.0026", "11.3113869239676"); + + CheckStringProperty("DICOM.300A.0070.[0].300A.0078", "7"); + CheckStringProperty("DICOM.300A.0070.[0].300A.0080", "4"); + + CheckStringProperty("DICOM.300A.00B0.[0].300A.00C6", "PHOTON"); + CheckStringProperty("DICOM.300A.00B0.[1].300A.00C6", "PHOTON"); + CheckStringProperty("DICOM.300A.00B0.[2].300A.00C6", "PHOTON"); + CheckStringProperty("DICOM.300A.00B0.[3].300A.00C6", "PHOTON"); + + CheckStringProperty("DICOM.300C.0060.[0].0008.1155", "1.2.246.352.71.4.320687012.3190.20090511122144"); + } + + void CheckStringProperty(std::string propertyName, std::string expectedPropertyValue) + { + auto actualProperty = m_image->GetProperty(propertyName.c_str()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Property not found: " + propertyName, actualProperty.IsNotNull(), true); + auto actualTemporoSpatialStringProperty = dynamic_cast(actualProperty.GetPointer()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Property has not type string: " + propertyName, actualTemporoSpatialStringProperty != nullptr, true); + std::string actualStringProperty = actualTemporoSpatialStringProperty->GetValue(); + CPPUNIT_ASSERT_EQUAL_MESSAGE(propertyName + " is not as expected", actualStringProperty, expectedPropertyValue); + } + + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkRTPlanReader) diff --git a/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp b/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp index a4f2577b2d..3b465dd276 100644 --- a/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp +++ b/Plugins/org.mitk.gui.qt.dicom/src/internal/DicomEventHandler.cpp @@ -1,395 +1,418 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPluginActivator.h" #include "DicomEventHandler.h" #include #include #include #include #include #include #include #include #include #include "mitkImage.h" #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DicomEventHandler::DicomEventHandler() { } DicomEventHandler::~DicomEventHandler() { } void DicomEventHandler::OnSignalAddSeriesToDataManager(const ctkEvent& ctkEvent) { QStringList listOfFilesForSeries; listOfFilesForSeries = ctkEvent.getProperty("FilesForSeries").toStringList(); if (!listOfFilesForSeries.isEmpty()) { //for rt data, if the modality tag isn't defined or is "CT" the image is handled like before if(ctkEvent.containsProperty("Modality") && (ctkEvent.getProperty("Modality").toString().compare("RTDOSE",Qt::CaseInsensitive) == 0 || - ctkEvent.getProperty("Modality").toString().compare("RTSTRUCT",Qt::CaseInsensitive) == 0)) + ctkEvent.getProperty("Modality").toString().compare("RTSTRUCT",Qt::CaseInsensitive) == 0 || + ctkEvent.getProperty("Modality").toString().compare("RTPLAN", Qt::CaseInsensitive) == 0)) { QString modality = ctkEvent.getProperty("Modality").toString(); if(modality.compare("RTDOSE",Qt::CaseInsensitive) == 0) { auto doseReader = mitk::RTDoseReader(); - doseReader.SetInput(listOfFilesForSeries.at(0).toStdString()); + doseReader.SetInput(listOfFilesForSeries.front().toStdString()); std::vector > readerOutput = doseReader.Read(); if (!readerOutput.empty()){ mitk::Image::Pointer doseImage = dynamic_cast(readerOutput.at(0).GetPointer()); mitk::DataNode::Pointer doseImageNode = mitk::DataNode::New(); doseImageNode->SetData(doseImage); doseImageNode->SetName("RTDose"); mitk::DataNode::Pointer doseOutlineNode = mitk::DataNode::New(); doseOutlineNode->SetData(doseImage); if (doseImage != nullptr) { auto sopUIDProperty = doseImage->GetProperty("dicomseriesreader.SOPClassUID"); if (sopUIDProperty.IsNotNull()){ auto sopUIDStringProperty = dynamic_cast(sopUIDProperty.GetPointer()); if (sopUIDStringProperty != nullptr){ std::string sopUID = sopUIDStringProperty->GetValue(); doseImageNode->SetName(sopUID); } } berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer prefNode = prefService->GetSystemPreferences()->Node(mitk::RTUIConstants::ROOT_ISO_PRESETS_PREFERENCE_NODE_ID.c_str()); typedef QStringList NamesType; NamesType names = prefNode->ChildrenNames(); std::map presetMap; for (NamesType::const_iterator pos = names.begin(); pos != names.end(); ++pos) { berry::IPreferences::Pointer aPresetNode = prefNode->Node(*pos); if (aPresetNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find preset node under given name. Name: " << (*pos).toStdString(); } mitk::IsoDoseLevelSet::Pointer levelSet = mitk::IsoDoseLevelSet::New(); NamesType levelNames = aPresetNode->ChildrenNames(); for (NamesType::const_iterator levelName = levelNames.begin(); levelName != levelNames.end(); ++levelName) { berry::IPreferences::Pointer levelNode = aPresetNode->Node(*levelName); if (aPresetNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find level node under given preset name. Name: " << (*pos).toStdString() << "; Level id: " << (*levelName).toStdString(); } mitk::IsoDoseLevel::Pointer isoLevel = mitk::IsoDoseLevel::New(); isoLevel->SetDoseValue(levelNode->GetDouble(mitk::RTUIConstants::ISO_LEVEL_DOSE_VALUE_ID.c_str(), 0.0)); mitk::IsoDoseLevel::ColorType color; color.SetRed(levelNode->GetFloat(mitk::RTUIConstants::ISO_LEVEL_COLOR_RED_ID.c_str(), 1.0)); color.SetGreen(levelNode->GetFloat(mitk::RTUIConstants::ISO_LEVEL_COLOR_GREEN_ID.c_str(), 1.0)); color.SetBlue(levelNode->GetFloat(mitk::RTUIConstants::ISO_LEVEL_COLOR_BLUE_ID.c_str(), 1.0)); isoLevel->SetColor(color); isoLevel->SetVisibleIsoLine(levelNode->GetBool(mitk::RTUIConstants::ISO_LEVEL_VISIBILITY_ISOLINES_ID.c_str(), true)); isoLevel->SetVisibleColorWash(levelNode->GetBool(mitk::RTUIConstants::ISO_LEVEL_VISIBILITY_COLORWASH_ID.c_str(), true)); levelSet->SetIsoDoseLevel(isoLevel); } presetMap.insert(std::make_pair((*pos).toStdString(), levelSet)); } if (presetMap.size() == 0) { presetMap.insert(std::make_pair(std::string("Virtuos"), mitk::GeneratIsoLevels_Virtuos())); } prefNode = prefService->GetSystemPreferences()->Node(mitk::RTUIConstants::ROOT_DOSE_VIS_PREFERENCE_NODE_ID.c_str()); if (prefNode.IsNull()) { mitkThrow() << "Error in preference interface. Cannot find preset node under given name. Name: " << prefNode->ToString().toStdString(); } //set some specific colorwash and isoline properties bool showColorWashGlobal = prefNode->GetBool(mitk::RTUIConstants::GLOBAL_VISIBILITY_COLORWASH_ID.c_str(), true); doseImageNode->SetBoolProperty(mitk::RTConstants::DOSE_SHOW_COLORWASH_PROPERTY_NAME.c_str(), showColorWashGlobal); bool showIsolinesGlobal = prefNode->GetBool(mitk::RTUIConstants::GLOBAL_VISIBILITY_ISOLINES_ID.c_str(), true); doseOutlineNode->SetBoolProperty(mitk::RTConstants::DOSE_SHOW_ISOLINES_PROPERTY_NAME.c_str(), showIsolinesGlobal); //Set reference dose property double referenceDose = prefNode->GetDouble(mitk::RTUIConstants::REFERENCE_DOSE_ID.c_str(), mitk::RTUIConstants::DEFAULT_REFERENCE_DOSE_VALUE); doseImageNode->SetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), referenceDose); doseOutlineNode->SetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), referenceDose); QString presetName = prefNode->Get(mitk::RTUIConstants::SELECTED_ISO_PRESET_ID.c_str(), "Virtuos"); mitk::IsoDoseLevelSet::Pointer isoDoseLevelPreset = presetMap[presetName.toStdString()]; mitk::IsoDoseLevelSetProperty::Pointer levelSetProp = mitk::IsoDoseLevelSetProperty::New(isoDoseLevelPreset); doseImageNode->SetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(), levelSetProp); doseOutlineNode->SetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str(), levelSetProp); mitk::IsoDoseLevelVector::Pointer levelVector = mitk::IsoDoseLevelVector::New(); mitk::IsoDoseLevelVectorProperty::Pointer levelVecProp = mitk::IsoDoseLevelVectorProperty::New(levelVector); doseImageNode->SetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(), levelVecProp); doseOutlineNode->SetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str(), levelVecProp); mitk::RenderingModeProperty::Pointer renderingModeProp = mitk::RenderingModeProperty::New(); if (showColorWashGlobal) { //Generating the Colorwash vtkSmartPointer transferFunction = vtkSmartPointer::New(); for (mitk::IsoDoseLevelSet::ConstIterator itIsoDoseLevel = isoDoseLevelPreset->Begin(); itIsoDoseLevel != isoDoseLevelPreset->End(); ++itIsoDoseLevel) { float *hsv = new float[3]; //used for transfer rgb to hsv vtkSmartPointer cCalc = vtkSmartPointer::New(); if (itIsoDoseLevel->GetVisibleColorWash()){ cCalc->RGBToHSV(itIsoDoseLevel->GetColor()[0], itIsoDoseLevel->GetColor()[1], itIsoDoseLevel->GetColor()[2], &hsv[0], &hsv[1], &hsv[2]); transferFunction->AddHSVPoint(itIsoDoseLevel->GetDoseValue()*referenceDose, hsv[0], hsv[1], hsv[2], 1.0, 1.0); } } mitk::TransferFunction::Pointer mitkTransFunc = mitk::TransferFunction::New(); mitk::TransferFunctionProperty::Pointer mitkTransFuncProp = mitk::TransferFunctionProperty::New(); mitkTransFunc->SetColorTransferFunction(transferFunction); mitkTransFuncProp->SetValue(mitkTransFunc); doseImageNode->SetProperty("Image Rendering.Transfer Function", mitkTransFuncProp); renderingModeProp->SetValue(mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR); } else { //Set rendering mode to levelwindow color mode renderingModeProp->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); } doseImageNode->SetProperty("Image Rendering.Mode", renderingModeProp); doseImageNode->SetProperty("opacity", mitk::FloatProperty::New(0.5)); //set the outline properties doseOutlineNode->SetBoolProperty("outline binary", true); doseOutlineNode->SetProperty("helper object", mitk::BoolProperty::New(true)); doseOutlineNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); dataStorage->Add(doseImageNode); dataStorage->Add(doseOutlineNode, doseImageNode); //set the dose mapper for outline drawing; the colorwash is realized by the imagevtkmapper2D mitk::DoseImageVtkMapper2D::Pointer contourMapper = mitk::DoseImageVtkMapper2D::New(); doseOutlineNode->SetMapper(1, contourMapper); mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); } }//END DOSE } else if(modality.compare("RTSTRUCT",Qt::CaseInsensitive) == 0) { mitk::RTStructureSetReader::Pointer structreader = mitk::RTStructureSetReader::New(); std::deque modelVector = structreader->ReadStructureSet(listOfFilesForSeries.at(0).toStdString().c_str()); if(modelVector.empty()) { MITK_ERROR << "No structuresets were created" << endl; } else { ctkServiceReference serviceReference =mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); for(int i=0; iAdd(modelVector.at(i)); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); } } + else if (modality.compare("RTPLAN", Qt::CaseInsensitive) == 0) + { + std::cout << "RTPLAN" << std::endl; + auto planReader = mitk::RTPlanReader(); + planReader.SetInput(listOfFilesForSeries.front().toStdString()); + std::vector > readerOutput = planReader.Read(); + if (!readerOutput.empty()){ + //there is no image, only the properties are interesting + mitk::Image::Pointer planDummyImage = dynamic_cast(readerOutput.at(0).GetPointer()); + + mitk::DataNode::Pointer planImageNode = mitk::DataNode::New(); + planImageNode->SetData(planDummyImage); + planImageNode->SetName("RTPlan"); + + ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); + mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); + mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); + + dataStorage->Add(planImageNode); + } + } } else { mitk::StringList seriesToLoad; QStringListIterator it(listOfFilesForSeries); while (it.hasNext()) { seriesToLoad.push_back(it.next().toStdString()); } //Get Reference for default data storage. ctkServiceReference serviceReference = mitk::PluginActivator::getContext()->getServiceReference(); mitk::IDataStorageService* storageService = mitk::PluginActivator::getContext()->getService(serviceReference); mitk::DataStorage* dataStorage = storageService->GetDefaultDataStorage().GetPointer()->GetDataStorage(); //special handling of Philips 3D US DICOM. //Copied from DICOMSeriesReaderService if (!seriesToLoad.empty() && mitk::DicomSeriesReader::IsPhilips3DDicom(seriesToLoad.front())) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); mitk::DataNode::Pointer node = mitk::DataNode::New(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(seriesToLoad.front()); if (mitk::DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { mitk::BaseData::Pointer data = node->GetData(); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenameName(seriesToLoad.front())); data->GetPropertyList()->SetProperty("name", nameProp); node->SetProperty("name", nameProp); dataStorage->Add(node); } std::cin.imbue(previousCppLocale); return; } //Normal DICOM handling (It wasn't a Philips 3D US) mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New(); selector->LoadBuiltIn3DConfigs(); selector->LoadBuiltIn3DnTConfigs(); selector->SetInputFiles(seriesToLoad); mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages(); reader->SetAdditionalTagsOfInterest(mitk::GetCurrentDICOMTagsOfInterest()); reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor); reader->SetInputFiles(seriesToLoad); mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->AddTagPaths(reader->GetTagsOfInterest()); scanner->SetInputFiles(seriesToLoad); scanner->Scan(); reader->SetTagCache(scanner->GetScanCache()); reader->AnalyzeInputFiles(); reader->LoadImages(); for (unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i) { const mitk::DICOMImageBlockDescriptor& desc = reader->GetOutput(i); mitk::BaseData::Pointer data = desc.GetMitkImage().GetPointer(); std::string nodeName = "Unnamed_DICOM"; std::string studyDescription = desc.GetPropertyAsString("studyDescription"); std::string seriesDescription = desc.GetPropertyAsString("seriesDescription"); if (!studyDescription.empty()) { nodeName = studyDescription; } if (!seriesDescription.empty()) { if (!studyDescription.empty()) { nodeName += "/"; } nodeName += seriesDescription; } mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New(nodeName); data->SetProperty("name", nameProp); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(data); nameProp = mitk::StringProperty::New(nodeName); node->SetProperty("name", nameProp); dataStorage->Add(node); } if (reader->GetNumberOfOutputs() < 1) { MITK_ERROR << "Error loading series: " << ctkEvent.getProperty("SeriesName").toString().toStdString() << " id: " << ctkEvent.getProperty("SeriesUID").toString().toStdString(); } } } else { MITK_INFO << "There are no files for the current series"; } } void DicomEventHandler::OnSignalRemoveSeriesFromStorage(const ctkEvent& /*ctkEvent*/) { } void DicomEventHandler::SubscribeSlots() { ctkServiceReference ref = mitk::PluginActivator::getContext()->getServiceReference(); if (ref) { ctkEventAdmin* eventAdmin = mitk::PluginActivator::getContext()->getService(ref); ctkDictionary properties; properties[ctkEventConstants::EVENT_TOPIC] = "org/mitk/gui/qt/dicom/ADD"; eventAdmin->subscribeSlot(this, SLOT(OnSignalAddSeriesToDataManager(ctkEvent)), properties); properties[ctkEventConstants::EVENT_TOPIC] = "org/mitk/gui/qt/dicom/DELETED"; eventAdmin->subscribeSlot(this, SLOT(OnSignalRemoveSeriesFromStorage(ctkEvent)), properties); } }