diff --git a/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp b/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp index c9be3c3167..6ea1cfeb86 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp +++ b/Modules/CEST/autoload/IO/mitkCESTIOActivator.cpp @@ -1,102 +1,66 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCESTIOActivator.h" #include "mitkCESTDICOMReaderService.h" #include "mitkCESTGenericDICOMReaderService.h" #include #include #include "mitkCESTIOMimeTypes.h" #include namespace mitk { void CESTIOActivator::Load(us::ModuleContext *context) { us::ServiceProperties props; props[us::ServiceConstants::SERVICE_RANKING()] = 10; m_MimeTypes = mitk::MitkCESTIOMimeTypes::Get(); for (const auto& mimeType : m_MimeTypes) { if (mimeType->GetName() == mitk::MitkCESTIOMimeTypes::CEST_DICOM_WITHOUT_META_FILE_NAME()) { // "w/o meta" mimetype should only registered with low priority. context->RegisterService(mimeType); } else { context->RegisterService(mimeType, props); } } m_CESTDICOMReader = std::make_unique(); m_CESTDICOMManualWithMetaFileReader = std::make_unique(MitkCESTIOMimeTypes::CEST_DICOM_WITH_META_FILE_MIMETYPE(), "CEST DICOM Manual Reader"); m_CESTDICOMManualWithOutMetaFileReader = std::make_unique(MitkCESTIOMimeTypes::CEST_DICOM_WITHOUT_META_FILE_MIMETYPE(), "CEST DICOM Manual Reader"); m_Context = context; - { - std::lock_guard lock(m_Mutex); - // Listen for events pertaining to dictionary services. - m_Context->AddServiceListener(this, &CESTIOActivator::DICOMTagsOfInterestServiceChanged, - std::string("(&(") + us::ServiceConstants::OBJECTCLASS() + "=" + - us_service_interface_iid() + "))"); - // Query for any service references matching any language. - std::vector > refs = - context->GetServiceReferences(); - if (!refs.empty()) - { - for (const auto& ref : refs) - { - this->RegisterTagsOfInterest(m_Context->GetService(ref)); - m_Context->UngetService(ref); - } - } - } + + DICOMTagsOfInterestAddHelper::TagsOfInterestVector tags = { mitk::DICOM_IMAGING_FREQUENCY_PATH() }; + m_TagHelper.Activate(m_Context, tags); } void CESTIOActivator::Unload(us::ModuleContext *) { for (auto& elem : m_MimeTypes) { delete elem; elem = nullptr; } - } - - void CESTIOActivator::RegisterTagsOfInterest(IDICOMTagsOfInterest* toiService) const - { - if (toiService != nullptr) - { - toiService->AddTagOfInterest(mitk::DICOM_IMAGING_FREQUENCY_PATH()); - } - } - - void CESTIOActivator::DICOMTagsOfInterestServiceChanged(const us::ServiceEvent event) - { - std::lock_guard lock(m_Mutex); - // If a dictionary service was registered, see if we - // need one. If so, get a reference to it. - if (event.GetType() == us::ServiceEvent::REGISTERED) - { - // Get a reference to the service object. - us::ServiceReference ref = event.GetServiceReference(); - this->RegisterTagsOfInterest(m_Context->GetService(ref)); - m_Context->UngetService(ref); - } + m_TagHelper.Deactivate(); } } US_EXPORT_MODULE_ACTIVATOR(mitk::CESTIOActivator) diff --git a/Modules/CEST/autoload/IO/mitkCESTIOActivator.h b/Modules/CEST/autoload/IO/mitkCESTIOActivator.h index 5c19a699b0..428cc1480a 100644 --- a/Modules/CEST/autoload/IO/mitkCESTIOActivator.h +++ b/Modules/CEST/autoload/IO/mitkCESTIOActivator.h @@ -1,53 +1,47 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCESTIOActivator_H #define MITKCESTIOActivator_H #include -#include +#include #include -#include #include -#include namespace mitk { struct IFileReader; - class IDICOMTagsOfInterest; class CESTIOActivator : public us::ModuleActivator { public: void Load(us::ModuleContext *context) override; void Unload(us::ModuleContext *context) override; - private: - void RegisterTagsOfInterest(IDICOMTagsOfInterest* toiService) const; - void DICOMTagsOfInterestServiceChanged(const us::ServiceEvent event); - std::unique_ptr m_CESTDICOMReader; std::unique_ptr m_CESTDICOMManualWithMetaFileReader; std::unique_ptr m_CESTDICOMManualWithOutMetaFileReader; std::vector m_MimeTypes; // Module context us::ModuleContext* m_Context; - /**mutex to guard the service listening */ - std::mutex m_Mutex; + + DICOMTagsOfInterestAddHelper m_TagHelper; }; + } #endif // MITKCESTIOActivator_H diff --git a/Modules/DICOMReader/files.cmake b/Modules/DICOMReader/files.cmake index 7218fa0662..05d6cd2f66 100644 --- a/Modules/DICOMReader/files.cmake +++ b/Modules/DICOMReader/files.cmake @@ -1,63 +1,64 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkBaseDICOMReaderService.cpp mitkDICOMFileReader.cpp mitkDICOMTagScanner.cpp mitkDICOMGDCMTagScanner.cpp mitkDICOMDCMTKTagScanner.cpp mitkDICOMImageBlockDescriptor.cpp mitkDICOMITKSeriesGDCMReader.cpp mitkDICOMDatasetSorter.cpp mitkDICOMTagBasedSorter.cpp mitkDICOMGDCMImageFrameInfo.cpp mitkDICOMImageFrameInfo.cpp mitkDICOMIOHelper.cpp mitkDICOMGenericImageFrameInfo.cpp mitkDICOMDatasetAccessingImageFrameInfo.cpp mitkDICOMSortCriterion.cpp mitkDICOMSortByTag.cpp mitkITKDICOMSeriesReaderHelper.cpp mitkEquiDistantBlocksSorter.cpp mitkNormalDirectionConsistencySorter.cpp mitkSortByImagePositionPatient.cpp mitkGantryTiltInformation.cpp mitkClassicDICOMSeriesReader.cpp mitkThreeDnTDICOMSeriesReader.cpp mitkDICOMTag.cpp mitkDICOMTagsOfInterestHelper.cpp mitkDICOMTagCache.cpp mitkDICOMGDCMTagCache.cpp mitkDICOMGenericTagCache.cpp mitkDICOMEnums.cpp mitkDICOMReaderConfigurator.cpp mitkDICOMFileReaderSelector.cpp mitkIDICOMTagsOfInterest.cpp + mitkDICOMTagsOfInterestAddHelper.cpp mitkDICOMTagPath.cpp mitkDICOMProperty.cpp mitkDICOMFilesHelper.cpp mitkDICOMIOMetaInformationPropertyConstants.cpp legacy/mitkDicomSeriesReader.cpp legacy/mitkDicomSR_GantryTiltInformation.cpp legacy/mitkDicomSR_ImageBlockDescriptor.cpp legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp legacy/mitkDicomSR_LoadDICOMScalar.cpp legacy/mitkDicomSR_LoadDICOMScalar4D.cpp legacy/mitkDicomSR_SliceGroupingResult.cpp ) set(RESOURCE_FILES configurations/3D/classicreader.xml configurations/3D/imageposition.xml configurations/3D/imageposition_byacquisition.xml configurations/3D/instancenumber.xml configurations/3D/instancenumber_soft.xml configurations/3D/slicelocation.xml configurations/3D/simpleinstancenumber_soft.xml configurations/3DnT/classicreader.xml configurations/3DnT/imageposition.xml configurations/3DnT/imageposition_byacquisition.xml configurations/3DnT/imageposition_bytriggertime.xml ) diff --git a/Modules/DICOMReader/include/mitkDICOMFileReaderSelector.h b/Modules/DICOMReader/include/mitkDICOMFileReaderSelector.h index 42ead769e7..1ec18d21e6 100644 --- a/Modules/DICOMReader/include/mitkDICOMFileReaderSelector.h +++ b/Modules/DICOMReader/include/mitkDICOMFileReaderSelector.h @@ -1,102 +1,116 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkDICOMFileReaderSelector_h #define mitkDICOMFileReaderSelector_h #include "mitkDICOMFileReader.h" #include namespace mitk { /** \ingroup DICOMReaderModule \brief Simple best-reader selection. This class implements a process of comparing different DICOMFileReader%s and selecting the reader with the minimal number of mitk::Image%s in its output. The code found in this class can - just be used to select a reader using this simple strategy - be taken as an example of how to use DICOMFileReader%s To create a selection of potential readers, the class makes use of mitk::DICOMReaderConfigurator, i.e. DICOMFileReaderSelector also expects the configuration files/strings to be in the format expected by mitk::DICOMReaderConfigurator. Two convenience methods load "default" configurations from compiled-in resources: LoadBuiltIn3DConfigs() and LoadBuiltIn3DnTConfigs(). + @remark If you use LoadBuiltIn3DConfigs() and LoadBuiltIn3DnTConfigs() you must + ensure that the MitkDICOMReader module (and therefore its resources) is properly + loaded. If the module is not available these methods will do nothing. + This can especially lead to problems if these methods are used in the scope + of another module's activator. */ class MITKDICOMREADER_EXPORT DICOMFileReaderSelector : public itk::LightObject { public: typedef std::list ReaderList; mitkClassMacroItkParent( DICOMFileReaderSelector, itk::LightObject ); itkNewMacro( DICOMFileReaderSelector ); /// \brief Add a configuration as expected by DICOMReaderConfigurator. /// Configs can only be reset by instantiating a new DICOMFileReaderSelector. void AddConfig(const std::string& xmlDescription); /// \brief Add a configuration as expected by DICOMReaderConfigurator. /// Configs can only be reset by instantiating a new DICOMFileReaderSelector. void AddConfigFile(const std::string& filename); /// \brief Add a configuration that is stored in the passed us::ModuleResourse. /// Configs can only be reset by instantiating a new DICOMFileReaderSelector. void AddConfigFromResource(us::ModuleResource& resource); /// \brief Add a whole pre-configured reader to the selection process. void AddFileReaderCanditate(DICOMFileReader::Pointer reader); /// \brief Load 3D image creating configurations from the MITK module system (see us::Module::FindResources()). /// For a default set of configurations, look into the directory Resources of the DICOMReader module. + /// @remark If you use this function you must ensure that the MitkDICOMReader module(and therefore its resources) + /// is properly loaded. If the module is not available this function will do nothing. + /// This can especially lead to problem if this function is used in the scope + /// of another module's activator. void LoadBuiltIn3DConfigs(); + /// \brief Load 3D+t image creating configurations from the MITK module system (see us::Module::FindResources()). /// For a default set of configurations, look into the directory Resources of the DICOMReader module. + /// @remark If you use this function you must ensure that the MitkDICOMReader module(and therefore its resources) + /// is properly loaded. If the module is not available this function will do nothing. + /// This can especially lead to problem if this function is used in the scope + /// of another module's activator. void LoadBuiltIn3DnTConfigs(); /// \brief Return all the DICOMFileReader%s that are currently used for selection by this class. /// The readers returned by this method depend on what config files have been added earlier /// (or which of the built-in readers have been loaded) ReaderList GetAllConfiguredReaders() const; /// Input files void SetInputFiles(StringList filenames); /// Input files const StringList& GetInputFiles() const; /// Execute the analysis and selection process. The first reader with a minimal number of outputs will be returned. DICOMFileReader::Pointer GetFirstReaderWithMinimumNumberOfOutputImages(); protected: DICOMFileReaderSelector(); ~DICOMFileReaderSelector() override; void AddConfigsFromResources(const std::string& path); void AddConfigFromResource(const std::string& resourcename); private: StringList m_PossibleConfigurations; StringList m_InputFilenames; ReaderList m_Readers; }; } // namespace #endif // mitkDICOMFileReaderSelector_h diff --git a/Modules/DICOMReader/include/mitkDICOMTagsOfInterestAddHelper.h b/Modules/DICOMReader/include/mitkDICOMTagsOfInterestAddHelper.h new file mode 100644 index 0000000000..d5cb9cceb4 --- /dev/null +++ b/Modules/DICOMReader/include/mitkDICOMTagsOfInterestAddHelper.h @@ -0,0 +1,72 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkDICOMTagsOfInterestAddHelper_h +#define mitkDICOMTagsOfInterestAddHelper_h + +#include + +#include + +#include + +#include +#include + +#include "MitkDICOMReaderExports.h" + +namespace us +{ + class ModuleContext; +} + +namespace mitk +{ + class IDICOMTagsOfInterest; + + /** Helper class that can be used to ensure that a given list + of DICOM tags will be registered at existing DICOMTagsOfInterest + services. If the services are available the tags will be directly + added. As long as the helper is active, it will add the given tags to + all new DICOMTagsOfInterest services as soon as they are registered. + This helper class is e.g. used in module activators where the + DICOMTagsOfInterest service might not be registered when a module + is loaded but the module wants to ensure that the tags are added + as soon as possible. + The helper must be deactivated before the module context used on activation + gets invalid (e.g. in the Unload function of the module activator that + uses the helper to ensure that the tags will be added). + */ + class MITKDICOMREADER_EXPORT DICOMTagsOfInterestAddHelper + { + public: + using TagsOfInterestVector = std::vector; + ~DICOMTagsOfInterestAddHelper(); + + void Activate(us::ModuleContext* context, TagsOfInterestVector tags); + void Deactivate(); + + private: + void RegisterTagsOfInterest(IDICOMTagsOfInterest* toiService) const; + void DICOMTagsOfInterestServiceChanged(const us::ServiceEvent event); + + TagsOfInterestVector m_TagsOfInterest; + + bool m_Active = false; + us::ModuleContext* m_Context = nullptr; + + /**mutex to guard the service listening */ + std::mutex m_Mutex; + }; +} + +#endif diff --git a/Modules/DICOMReader/src/mitkDICOMFileReaderSelector.cpp b/Modules/DICOMReader/src/mitkDICOMFileReaderSelector.cpp index bca9f9b9e6..e538eb3f43 100644 --- a/Modules/DICOMReader/src/mitkDICOMFileReaderSelector.cpp +++ b/Modules/DICOMReader/src/mitkDICOMFileReaderSelector.cpp @@ -1,248 +1,252 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDICOMFileReaderSelector.h" #include "mitkDICOMReaderConfigurator.h" #include "mitkDICOMGDCMTagScanner.h" #include #include #include #include #include mitk::DICOMFileReaderSelector ::DICOMFileReaderSelector() { } mitk::DICOMFileReaderSelector ::~DICOMFileReaderSelector() { } std::list mitk::DICOMFileReaderSelector ::GetAllConfiguredReaders() const { return m_Readers; } void mitk::DICOMFileReaderSelector ::AddConfigsFromResources(const std::string& path) { + if (nullptr == us::GetModuleContext()) return; + const std::vector configs = us::GetModuleContext()->GetModule()->FindResources( path, "*.xml", false ); for ( auto iter = configs.cbegin(); iter != configs.cend(); ++iter ) { us::ModuleResource resource = *iter; if (resource.IsValid()) { us::ModuleResourceStream stream(resource); // read all into string s std::string s; stream.seekg(0, std::ios::end); s.reserve(stream.tellg()); stream.seekg(0, std::ios::beg); s.assign((std::istreambuf_iterator(stream)), std::istreambuf_iterator()); this->AddConfig(s); } } } void mitk::DICOMFileReaderSelector ::AddConfigFromResource(us::ModuleResource& resource) { if (resource.IsValid()) { us::ModuleResourceStream stream(resource); // read all into string s std::string s; stream.seekg(0, std::ios::end); s.reserve(stream.tellg()); stream.seekg(0, std::ios::beg); s.assign((std::istreambuf_iterator(stream)), std::istreambuf_iterator()); this->AddConfig(s); } } void mitk::DICOMFileReaderSelector ::AddConfigFromResource(const std::string& resourcename) { + if (nullptr == us::GetModuleContext()) return; + us::ModuleResource r = us::GetModuleContext()->GetModule()->GetResource(resourcename); this->AddConfigFromResource(r); } void mitk::DICOMFileReaderSelector ::AddFileReaderCanditate(DICOMFileReader::Pointer reader) { if (reader.IsNotNull()) { m_Readers.push_back( reader ); } } void mitk::DICOMFileReaderSelector ::LoadBuiltIn3DConfigs() { //this->AddConfigsFromResources("configurations/3D"); // in this order of preference... this->AddConfigFromResource("configurations/3D/instancenumber.xml"); this->AddConfigFromResource("configurations/3D/instancenumber_soft.xml"); this->AddConfigFromResource("configurations/3D/slicelocation.xml"); this->AddConfigFromResource("configurations/3D/imageposition.xml"); this->AddConfigFromResource("configurations/3D/imageposition_byacquisition.xml"); //this->AddConfigFromResource("configurations/3D/classicreader.xml"); // not the best choice in ANY of the images I came across } void mitk::DICOMFileReaderSelector ::LoadBuiltIn3DnTConfigs() { this->AddConfigsFromResources("configurations/3DnT"); } void mitk::DICOMFileReaderSelector ::AddConfig(const std::string& xmlDescription) { DICOMReaderConfigurator::Pointer configurator = DICOMReaderConfigurator::New(); DICOMFileReader::Pointer reader = configurator->CreateFromUTF8ConfigString(xmlDescription); if (reader.IsNotNull()) { m_Readers.push_back( reader ); m_PossibleConfigurations.push_back(xmlDescription); } else { std::stringstream ss; ss << "Could not parse reader configuration. Ignoring it."; throw std::invalid_argument( ss.str() ); } } void mitk::DICOMFileReaderSelector ::AddConfigFile(const std::string& filename) { std::ifstream file(filename.c_str()); std::string s; file.seekg(0, std::ios::end); s.reserve(file.tellg()); file.seekg(0, std::ios::beg); s.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); this->AddConfig(s); } void mitk::DICOMFileReaderSelector ::SetInputFiles(StringList filenames) { m_InputFilenames = filenames; } const mitk::StringList& mitk::DICOMFileReaderSelector ::GetInputFiles() const { return m_InputFilenames; } mitk::DICOMFileReader::Pointer mitk::DICOMFileReaderSelector ::GetFirstReaderWithMinimumNumberOfOutputImages() { ReaderList workingCandidates; // do the tag scanning externally and just ONCE DICOMGDCMTagScanner::Pointer gdcmScanner = DICOMGDCMTagScanner::New(); gdcmScanner->SetInputFiles( m_InputFilenames ); // let all readers analyze the file set for ( auto rIter = m_Readers.cbegin(); rIter != m_Readers.cend(); ++rIter ) { gdcmScanner->AddTagPaths((*rIter)->GetTagsOfInterest()); } gdcmScanner->Scan(); // let all readers analyze the file set unsigned int readerIndex(0); for ( auto rIter = m_Readers.cbegin(); rIter != m_Readers.cend(); ++readerIndex, ++rIter ) { (*rIter)->SetInputFiles( m_InputFilenames ); (*rIter)->SetTagCache( gdcmScanner->GetScanCache() ); try { (*rIter)->AnalyzeInputFiles(); workingCandidates.push_back( *rIter ); MITK_INFO << "Reader " << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << ") suggests " << (*rIter)->GetNumberOfOutputs() << " 3D blocks"; if ((*rIter)->GetNumberOfOutputs() == 1) { MITK_DEBUG << "Early out with reader #" << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << "), less than 1 block is not possible"; return *rIter; } } catch ( const std::exception& e ) { MITK_ERROR << "Reader " << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << ") threw exception during file analysis, ignoring this reader. Exception: " << e.what(); } catch (...) { MITK_ERROR << "Reader " << readerIndex << " (" << (*rIter)->GetConfigurationLabel() << ") threw unknown exception during file analysis, ignoring this reader."; } } DICOMFileReader::Pointer bestReader; unsigned int minimumNumberOfOutputs = std::numeric_limits::max(); readerIndex = 0; unsigned int bestReaderIndex(0); // select the reader with the minimum number of mitk::Images as output for ( auto rIter = workingCandidates.cbegin(); rIter != workingCandidates.cend(); ++readerIndex, ++rIter ) { const unsigned int thisReadersNumberOfOutputs = (*rIter)->GetNumberOfOutputs(); if ( thisReadersNumberOfOutputs > 0 // we don't count readers that don't actually produce output && thisReadersNumberOfOutputs < minimumNumberOfOutputs ) { minimumNumberOfOutputs = (*rIter)->GetNumberOfOutputs(); bestReader = *rIter; bestReaderIndex = readerIndex; } } MITK_DEBUG << "Decided for reader #" << bestReaderIndex << " (" << bestReader->GetConfigurationLabel() << ")"; MITK_DEBUG << m_PossibleConfigurations[bestReaderIndex]; return bestReader; } diff --git a/Modules/DICOMReader/src/mitkDICOMIOHelper.cpp b/Modules/DICOMReader/src/mitkDICOMIOHelper.cpp index 94c328a90d..be7a272a0f 100644 --- a/Modules/DICOMReader/src/mitkDICOMIOHelper.cpp +++ b/Modules/DICOMReader/src/mitkDICOMIOHelper.cpp @@ -1,71 +1,71 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDICOMIOHelper.h" #include #include #include #include namespace mitk { mitk::IDICOMTagsOfInterest *GetDicomTagsOfInterestService() { mitk::IDICOMTagsOfInterest *result = nullptr; us::ModuleContext *context = us::GetModuleContext(); if (context == nullptr) { - MITK_WARN << "No dicom context found."; + MITK_WARN << "No MitkDICOMReader module context found."; return result; } std::vector> toiRegisters = context->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; } FindingsListVectorType ExtractPathsOfInterest(const DICOMTagPathList &pathsOfInterest, const DICOMDatasetAccessingImageFrameList &frames) { std::vector findings; for (const auto &entry : pathsOfInterest) { findings.push_back(frames.front()->GetTagValueAsString(entry)); } return findings; } void SetProperties(BaseDataPointer image, const FindingsListVectorType &findings) { 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); } } } } diff --git a/Modules/DICOMReader/src/mitkDICOMTagsOfInterestAddHelper.cpp b/Modules/DICOMReader/src/mitkDICOMTagsOfInterestAddHelper.cpp new file mode 100644 index 0000000000..2f6f84d733 --- /dev/null +++ b/Modules/DICOMReader/src/mitkDICOMTagsOfInterestAddHelper.cpp @@ -0,0 +1,118 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkDICOMTagsOfInterestAddHelper.h" + +#include + +#include "usModuleContext.h" +#include "usGetModuleContext.h" + +void mitk::DICOMTagsOfInterestAddHelper::Activate(us::ModuleContext* context, TagsOfInterestVector tags) +{ + if (!m_Active && nullptr != context) + { + std::lock_guard lock(m_Mutex); + m_Active = true; + m_Context = context; + m_TagsOfInterest = tags; + + // Listen for events pertaining to dictionary services. + m_Context->AddServiceListener(this, &DICOMTagsOfInterestAddHelper::DICOMTagsOfInterestServiceChanged, + std::string("(&(") + us::ServiceConstants::OBJECTCLASS() + "=" + + us_service_interface_iid() + "))"); + // Query for any service references matching any language. + std::vector > refs = + m_Context->GetServiceReferences(); + if (!refs.empty()) + { + for (const auto& ref : refs) + { + this->RegisterTagsOfInterest(context->GetService(ref)); + context->UngetService(ref); + } + } + } +} + +void mitk::DICOMTagsOfInterestAddHelper::Deactivate() +{ + if (m_Active) + { + std::lock_guard lock(m_Mutex); + m_Active = false; + if (nullptr != m_Context) + { + try + { + m_Context->RemoveServiceListener(this, &DICOMTagsOfInterestAddHelper::DICOMTagsOfInterestServiceChanged); + } + catch (...) + { + MITK_WARN << "Was not able to remove service listener from module context."; + } + } + } +} + +mitk::DICOMTagsOfInterestAddHelper::~DICOMTagsOfInterestAddHelper() +{ + if (m_Active) + { + MITK_ERROR << "DICOMTagsOfInterestAddHelper was not deactivated correctly befor its destructor was called."; + auto context = us::GetModuleContext(); + //we cannot trust m_Context at this point anymore and have no means to validate it. So try to get the own module context + //and to remove the listener via this context. + if (nullptr != context) + { + try + { + context->RemoveServiceListener(this, &DICOMTagsOfInterestAddHelper::DICOMTagsOfInterestServiceChanged); + } + catch (...) + { + MITK_WARN << "Was not able to remove service listener from module context."; + } + } + } +} + +void mitk::DICOMTagsOfInterestAddHelper::RegisterTagsOfInterest(IDICOMTagsOfInterest* toiService) const +{ + if (nullptr != toiService) + { + for (const auto& tag : m_TagsOfInterest) + { + toiService->AddTagOfInterest(tag); + } + } +} + +void mitk::DICOMTagsOfInterestAddHelper::DICOMTagsOfInterestServiceChanged(const us::ServiceEvent event) +{ + // If a DICOMTagsOfInterestService was registered, register all tags of interest. + if (event.GetType() == us::ServiceEvent::REGISTERED) + { + if (nullptr != m_Context) + { + std::lock_guard lock(m_Mutex); + // Get a reference to the service object. + us::ServiceReference ref = event.GetServiceReference(); + this->RegisterTagsOfInterest(m_Context->GetService(ref)); + m_Context->UngetService(ref); + } + else + { + MITK_ERROR << "New DICOMTagsOfInterestService was registered, but no module context exists. Thus, no DICOM tags of interest where added."; + } + } +} diff --git a/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp b/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp index 61cbded071..907f5ab402 100644 --- a/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp +++ b/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp @@ -1,94 +1,123 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDICOMReaderServicesActivator.h" #include "mitkAutoSelectingDICOMReaderService.h" #include "mitkManualSelectingDICOMReaderService.h" #include "mitkDICOMTagsOfInterestService.h" #include "mitkSimpleVolumeDICOMSeriesReaderService.h" #include "mitkCoreServices.h" #include "mitkPropertyPersistenceInfo.h" #include "mitkDICOMIOMetaInformationPropertyConstants.h" #include "mitkIPropertyPersistence.h" #include "mitkTemporoSpatialStringProperty.h" #include +#include void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath, bool temporoSpatial = false) { mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); mitk::PropertyPersistenceInfo::Pointer info = mitk::PropertyPersistenceInfo::New(); if (propPath.IsExplicit()) { std::string name = mitk::PropertyKeyPathToPropertyName(propPath); std::string key = name; std::replace(key.begin(), key.end(), '.', '_'); info->SetNameAndKey(name, key); } else { std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); } if (temporoSpatial) { info->SetDeserializationFunction(mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty); info->SetSerializationFunction(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON); } persistenceService->AddInfo(info); } namespace mitk { void DICOMReaderServicesActivator::Load(us::ModuleContext* context) { + m_Context = context; + m_AutoSelectingDICOMReader = std::make_unique(); m_SimpleVolumeDICOMSeriesReader = std::make_unique(); - m_ManualSelectingDICOMSeriesReader = std::make_unique(); m_DICOMTagsOfInterestService = std::make_unique(); context->RegisterService(m_DICOMTagsOfInterestService.get()); DICOMTagPathMapType tagmap = GetDefaultDICOMTagsOfInterest(); for (auto tag : tagmap) { m_DICOMTagsOfInterestService->AddTagOfInterest(tag.first); } //add properties that should be persistent (if possible/supported by the writer) AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES(), true); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION()); AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING()); + //We have to handle ManualSelectingDICOMSeriesReader different then the other + //readers. Reason: The reader uses DICOMFileReaderSelector in its constructor. + //this class needs to access resources of MitkDICOMReader module, which might + //not be initialized yet (that would lead to a crash, see i.a. T27553). Thus check if the module + //is alreade loaded. If not, register a listener and create the reader as soon + //as the module is available. + auto dicomModule = us::ModuleRegistry::GetModule("MitkDICOMReader"); + if (nullptr == dicomModule) + { + std::lock_guard lock(m_Mutex); + // Listen for events of module life cycle. + m_Context->AddModuleListener(this, &DICOMReaderServicesActivator::EnsureManualSelectingDICOMSeriesReader); + } + else + { + m_ManualSelectingDICOMSeriesReader = std::make_unique(); + } } void DICOMReaderServicesActivator::Unload(us::ModuleContext*) { } + void DICOMReaderServicesActivator::EnsureManualSelectingDICOMSeriesReader(const us::ModuleEvent event) + { + //We have to handle ManualSelectingDICOMSeriesReader different then the other + //readers. For more details see the explanations in the constructor. + std::lock_guard lock(m_Mutex); + if (nullptr == m_ManualSelectingDICOMSeriesReader && event.GetModule()->GetName()=="MitkDICOMReader" && event.GetType() == us::ModuleEvent::LOADED) + { + m_ManualSelectingDICOMSeriesReader = std::make_unique(); + } + } } US_EXPORT_MODULE_ACTIVATOR(mitk::DICOMReaderServicesActivator) diff --git a/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.h b/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.h index 4793cd2145..ae6c9d4de1 100644 --- a/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.h +++ b/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.h @@ -1,48 +1,50 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKDICOMREADERSERVICESACTIVATOR_H #define MITKDICOMREADERSERVICESACTIVATOR_H #include -#include +#include #include +#include namespace mitk { struct IFileReader; class IDICOMTagsOfInterest; class DICOMReaderServicesActivator : public us::ModuleActivator { public: void Load(us::ModuleContext* context) override; void Unload(us::ModuleContext* context) override; - void AliasServiceChanged(const us::ServiceEvent event); - private: + void EnsureManualSelectingDICOMSeriesReader(const us::ModuleEvent event); std::unique_ptr m_AutoSelectingDICOMReader; std::unique_ptr m_ManualSelectingDICOMSeriesReader; std::unique_ptr m_SimpleVolumeDICOMSeriesReader; std::unique_ptr m_DICOMTagsOfInterestService; - us::ModuleContext* mitkContext; + us::ModuleContext* m_Context; + /**mutex to guard the module listening */ + std::mutex m_Mutex; }; } #endif // MITKDICOMREADERSERVICESACTIVATOR_H diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMQIIOActivator.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMQIIOActivator.cpp index fb9c72631d..f79062ae3c 100644 --- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMQIIOActivator.cpp +++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMQIIOActivator.cpp @@ -1,59 +1,66 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include "mitkDICOMSegmentationIO.h" +#include #include namespace mitk { /** \brief Registers services for multilabel dicom module. */ class DICOMQIIOActivator : public us::ModuleActivator { std::vector m_FileIOs; + DICOMTagsOfInterestAddHelper m_TagHelper; public: void Load(us::ModuleContext * context) override { us::ServiceProperties props; props[us::ServiceConstants::SERVICE_RANKING()] = 10; std::vector mimeTypes = mitk::MitkDICOMSEGIOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { context->RegisterService(*mimeTypeIter, props); } m_FileIOs.push_back(new DICOMSegmentationIO()); + + DICOMTagsOfInterestAddHelper::TagsOfInterestVector tags = DICOMSegmentationIO::GetDICOMTagsOfInterest(); + m_TagHelper.Activate(context, tags); } + void Unload(us::ModuleContext *) override { for (auto &elem : m_FileIOs) { delete elem; } + m_TagHelper.Deactivate(); } }; } US_EXPORT_MODULE_ACTIVATOR(mitk::DICOMQIIOActivator) diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp index 0731005329..a8caa81a5f 100644 --- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp +++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp @@ -1,707 +1,704 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkDICOMSegmentationIO__cpp #define __mitkDICOMSegmentationIO__cpp #include "mitkDICOMSegmentationIO.h" #include "mitkDICOMSegIOMimeTypes.h" #include "mitkDICOMSegmentationConstants.h" #include #include #include #include #include #include #include #include // itk #include // dcmqi #include // us #include #include namespace mitk { DICOMSegmentationIO::DICOMSegmentationIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), mitk::MitkDICOMSEGIOMimeTypes::DICOMSEG_MIMETYPE_NAME(), "DICOM Segmentation") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); - - this->AddDICOMTagsToService(); } - void DICOMSegmentationIO::AddDICOMTagsToService() + std::vector DICOMSegmentationIO::GetDICOMTagsOfInterest() { - IDICOMTagsOfInterest *toiService = GetDicomTagsOfInterestService(); - if (toiService != nullptr) - { - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()); - - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()); - - toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()); - - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()); - - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()); - - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()); - toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()); - } + std::vector result; + result.emplace_back(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()); + + result.emplace_back(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()); + + result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()); + result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()); + result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()); + result.emplace_back(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()); + + result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()); + + result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()); + + result.emplace_back(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()); + result.emplace_back(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()); + + return result; } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; // Check if the input file is a segmentation const LabelSetImage *input = static_cast(this->GetInput()); if (input) { if ((input->GetDimension() != 3)) { MITK_INFO << "DICOM segmentation writer is tested only with 3D images, sorry."; return Unsupported; } // Check if input file has dicom information for the referenced image (original DICOM image, e.g. CT) Still necessary, see write() mitk::StringLookupTableProperty::Pointer dicomFilesProp = dynamic_cast(input->GetProperty("referenceFiles").GetPointer()); if (dicomFilesProp.IsNotNull()) return Supported; } return Unsupported; } void DICOMSegmentationIO::Write() { ValidateOutputLocation(); mitk::LocaleSwitch localeSwitch("C"); LocalFile localFile(this); const std::string path = localFile.GetFileName(); auto input = dynamic_cast(this->GetInput()); if (input == nullptr) mitkThrow() << "Cannot write non-image data"; // Get DICOM information from referenced image vector> dcmDatasetsSourceImage; std::unique_ptr readFileFormat(new DcmFileFormat()); try { // TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the // property list mitk::StringLookupTableProperty::Pointer filesProp = dynamic_cast(input->GetProperty("referenceFiles").GetPointer()); if (filesProp.IsNull()) { mitkThrow() << "No property with dicom file path."; return; } StringLookupTable filesLut = filesProp->GetValue(); const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable(); for (auto it : lookUpTableMap) { const char *fileName = (it.second).c_str(); if (readFileFormat->loadFile(fileName, EXS_Unknown).good()) { std::unique_ptr readDCMDataset(readFileFormat->getAndRemoveDataset()); dcmDatasetsSourceImage.push_back(std::move(readDCMDataset)); } } } catch (const std::exception &e) { MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl; return; } // Iterate over all layers. For each a dcm file will be generated for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer) { vector segmentations; try { // Hack: Remove the const attribute to switch between the layer images. Normally you could get the different // layer images by input->GetLayerImage(layer) mitk::LabelSetImage *mitkLayerImage = const_cast(input); mitkLayerImage->SetActiveLayer(layer); // Cast mitk layer image to itk ImageToItk::Pointer imageToItkFilter = ImageToItk::New(); imageToItkFilter->SetInput(mitkLayerImage); // Cast from original itk type to dcmqi input itk image type typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(imageToItkFilter->GetOutput()); castFilter->Update(); itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput(); itkLabelImage->DisconnectPipeline(); // Iterate over all labels. For each label a segmentation image will be created const LabelSet *labelSet = input->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { // Thresold over the image with the given label value itk::ThresholdImageFilter::Pointer thresholdFilter = itk::ThresholdImageFilter::New(); thresholdFilter->SetInput(itkLabelImage); thresholdFilter->ThresholdOutside(labelIter->first, labelIter->first); thresholdFilter->SetOutsideValue(0); thresholdFilter->Update(); itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput(); segmentImage->DisconnectPipeline(); segmentations.push_back(segmentImage); } } catch (const itk::ExceptionObject &e) { MITK_ERROR << e.GetDescription() << endl; return; } // Create segmentation meta information const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer); MITK_INFO << "Writing image: " << path << std::endl; try { //TODO is there a better way? Interface expects a vector of raw pointer. vector rawVecDataset; for (const auto& dcmDataSet : dcmDatasetsSourceImage) rawVecDataset.push_back(dcmDataSet.get()); // Convert itk segmentation images to dicom image std::unique_ptr converter = std::make_unique(); std::unique_ptr result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile, false)); // Write dicom file DcmFileFormat dcmFileFormat(result.get()); std::string filePath = path.substr(0, path.find_last_of(".")); // If there is more than one layer, we have to write more than 1 dicom file if (input->GetNumberOfLayers() != 1) filePath = filePath + std::to_string(layer) + ".dcm"; else filePath = filePath + ".dcm"; dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit); } catch (const std::exception &e) { MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl; return; } } // Write a dcm file for the next layer } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(fileName.c_str()); if (status.bad()) return Unsupported; OFString modality; if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good()) { if (modality.compare("SEG") == 0) return Supported; else return Unsupported; } return Unsupported; } std::vector DICOMSegmentationIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); LabelSetImage::Pointer labelSetImage; std::vector result; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << std::endl; if (path.empty()) mitkThrow() << "Empty filename in mitk::ItkImageIO "; try { // Get the dcm data set from file path DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(path.c_str()); if (status.bad()) mitkThrow() << "Can't read the input file!"; DcmDataset *dataSet = dcmFileFormat.getDataset(); if (dataSet == nullptr) mitkThrow() << "Can't read data from input file!"; //=============================== dcmqi part ==================================== // Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo) std::unique_ptr converter = std::make_unique(); pair, string> dcmqiOutput = converter->dcmSegmentation2itkimage(dataSet); map segItkImages = dcmqiOutput.first; dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str()); metaInfo.read(); MITK_INFO << "Input " << metaInfo.getJSONOutputAsString(); //=============================================================================== // Get the label information from segment attributes for each itk image vector>::const_iterator segmentIter = metaInfo.segmentsAttributesMappingList.begin(); // For each itk image add a layer to the LabelSetImage output for (auto &element : segItkImages) { // Get the labeled image and cast it to mitkImage typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(element.second); castFilter->Update(); Image::Pointer layerImage; CastToMitkImage(castFilter->GetOutput(), layerImage); // Get pixel value of the label itkInternalImageType::ValueType segValue = 1; typedef itk::ImageRegionIterator IteratorType; // Iterate over the image to find the pixel value of the label IteratorType iter(element.second, element.second->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { itkInputImageType::PixelType value = iter.Get(); if (value != 0) { segValue = value; break; } ++iter; } // Get Segment information map map segmentMap = (*segmentIter); map::const_iterator segmentMapIter = (*segmentIter).begin(); dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second; OFString labelName; if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr) { segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName); if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr) { OFString modifier; segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier); labelName.append(" (").append(modifier).append(")"); } } else { labelName = std::to_string(segmentAttribute->getLabelID()).c_str(); if (labelName.empty()) labelName = "Unnamed"; } float tmp[3] = { 0.0, 0.0, 0.0 }; if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr) { tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0; tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0; tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0; } Label *newLabel = nullptr; // If labelSetImage do not exists (first image) if (labelSetImage.IsNull()) { // Initialize the labelSetImage with the read image labelSetImage = LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(layerImage); // Already a label was generated, so set the information to this newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer()); newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); } else { // Add a new layer to the labelSetImage. Background label is set automatically labelSetImage->AddLayer(layerImage); // Add new label newLabel = new Label; newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); labelSetImage->GetLabelSet(labelSetImage->GetActiveLayer())->AddLabel(newLabel); } // Add some more label properties this->SetLabelProperties(newLabel, segmentAttribute); ++segmentIter; } labelSetImage->GetLabelSet()->SetAllLabelsVisible(true); // Add some general DICOM Segmentation properties mitk::IDICOMTagsOfInterest *toiSrv = GetDicomTagsOfInterestService(); auto tagsOfInterest = toiSrv->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto &tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles({ GetInputLocation() }); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the DICOM Seg file" << std::endl; return result; } auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); SetProperties(labelSetImage, findings); // Set active layer to the first layer of the labelset image if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0) labelSetImage->SetActiveLayer(0); } catch (const std::exception &e) { MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what(); return result; } catch (...) { MITK_ERROR << "An error occurred in dcmqi while reading the DICOM Seg file"; return result; } result.push_back(labelSetImage.GetPointer()); return result; } const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer) { const mitk::LabelSetImage *image = dynamic_cast(this->GetInput()); const std::string output; dcmqi::JSONSegmentationMetaInformationHandler handler; // 1. Metadata attributes that will be listed in the resulting DICOM SEG object std::string contentCreatorName; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(), contentCreatorName)) contentCreatorName = "MITK"; handler.setContentCreatorName(contentCreatorName); std::string clinicalTrailSeriesId; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(), clinicalTrailSeriesId)) clinicalTrailSeriesId = "Session 1"; handler.setClinicalTrialSeriesID(clinicalTrailSeriesId); std::string clinicalTrialTimePointID; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(), clinicalTrialTimePointID)) clinicalTrialTimePointID = "0"; handler.setClinicalTrialTimePointID(clinicalTrialTimePointID); std::string clinicalTrialCoordinatingCenterName = ""; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(), clinicalTrialCoordinatingCenterName)) clinicalTrialCoordinatingCenterName = "Unknown"; handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName); std::string seriesDescription; if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription)) seriesDescription = "MITK Segmentation"; handler.setSeriesDescription(seriesDescription); handler.setSeriesNumber("0" + std::to_string(layer)); handler.setInstanceNumber("1"); handler.setBodyPartExamined(""); const LabelSet *labelSet = image->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { const Label *label = labelIter->second; if (label != nullptr) { TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str())); TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str())); TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str())); dcmqi::SegmentAttributes *segmentAttribute = nullptr; if (segmentNumberProp->GetValue() == "") { MITK_ERROR << "Something went wrong with the label ID."; } else { int labelId = std::stoi(segmentNumberProp->GetValue()); segmentAttribute = handler.createAndGetNewSegment(labelId); } if (segmentAttribute != nullptr) { segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmName("MITK Segmentation"); if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr && segmentCategoryCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyCategoryCodeSequence( segmentCategoryCodeValueProp->GetValueAsString(), segmentCategoryCodeSchemeProp->GetValueAsString(), segmentCategoryCodeMeaningProp->GetValueAsString()); else // some default values segmentAttribute->setSegmentedPropertyCategoryCodeSequence( "M-01000", "SRT", "Morphologically Altered Structure"); if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr && segmentTypeCodeMeaningProp != nullptr) { segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(), segmentTypeCodeSchemeProp->GetValueAsString(), segmentTypeCodeMeaningProp->GetValueAsString()); handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString()); } else { // some default values segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass"); handler.setBodyPartExamined("Mass"); } if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr && segmentModifierCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence( segmentModifierCodeValueProp->GetValueAsString(), segmentModifierCodeSchemeProp->GetValueAsString(), segmentModifierCodeMeaningProp->GetValueAsString()); Color color = label->GetColor(); segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255); } } } return handler.getJSONOutputAsString(); } void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute) { // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique // within the Segmentation instance in which it is created label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(), TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); // Segment Label: User-defined label identifying this segment. label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(), TemporoSpatialStringProperty::New(label->GetName())); // Segment Algorithm Type: Type of algorithm used to generate the segment. if (!segmentAttribute->getSegmentAlgorithmType().empty()) label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(), TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType())); // Add Segmented Property Category Code Sequence tags auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence(); if (categoryCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value categoryCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator categoryCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning categoryCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Code Sequence tags auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence(); if (typeCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value typeCodeSequence->getCodeValue(codeValue); label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator typeCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning typeCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Modifier Code Sequence tags auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence(); if (modifierCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value modifierCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator modifierCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning modifierCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Atomic RegionSequence tags auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence(); if (atomicRegionSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value atomicRegionSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator atomicRegionSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning atomicRegionSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } } DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); } } // namespace #endif //__mitkDICOMSegmentationIO__cpp diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h index 123bcb803c..f219be1024 100644 --- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h +++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h @@ -1,64 +1,70 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkDICOMSegmentationIO_h #define __mitkDICOMSegmentationIO_h #include #include +#include + #include +#include + namespace mitk { /** * Read and Writes a LabelSetImage to a dcm file * @ingroup Process */ class DICOMSegmentationIO : public mitk::AbstractFileIO { public: typedef mitk::LabelSetImage InputType; typedef itk::Image itkInputImageType; typedef itk::Image itkInternalImageType; DICOMSegmentationIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; + + static std::vector GetDICOMTagsOfInterest(); + protected: /** * @brief Reads a number of DICOM segmentation from the file system * @return a vector of mitk::LabelSetImages * @throws throws an mitk::Exception if an error ocurrs */ std::vector> DoRead() override; private: DICOMSegmentationIO *IOClone() const override; // -------------- DICOMSegmentationIO specific functions ------------- const std::string CreateMetaDataJsonFile(int layer); void SetLabelProperties(Label *label, dcmqi::SegmentAttributes *segmentAttribute); - void AddDICOMTagsToService(); }; } // end of namespace mitk #endif // __mitkDICOMSegmentationIO_h