diff --git a/Modules/Classification/CLUtilities/CMakeLists.txt b/Modules/Classification/CLUtilities/CMakeLists.txt index 1a57c99513..0061dac5ba 100644 --- a/Modules/Classification/CLUtilities/CMakeLists.txt +++ b/Modules/Classification/CLUtilities/CMakeLists.txt @@ -1,14 +1,14 @@ mitk_create_module( - DEPENDS MitkCore MitkCLCore MitkCommandLine - PACKAGE_DEPENDS PUBLIC Eigen + DEPENDS MitkCore MitkCLCore MitkCommandLine MitkDICOMReader + PACKAGE_DEPENDS PUBLIC Eigen PRIVATE tinyxml ) if(TARGET ${MODULE_TARGET}) if(MITK_USE_OpenMP) target_link_libraries(${MODULE_TARGET} PUBLIC OpenMP::OpenMP_CXX) endif() if(BUILD_TESTING) add_subdirectory(test) endif() endif() diff --git a/Modules/Classification/CLUtilities/files.cmake b/Modules/Classification/CLUtilities/files.cmake index 64e037b845..6295ba3e1e 100644 --- a/Modules/Classification/CLUtilities/files.cmake +++ b/Modules/Classification/CLUtilities/files.cmake @@ -1,39 +1,40 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES - mitkCLResultWritter.cpp + mitkCLResultWriter.cpp + mitkCLResultXMLWriter.cpp Algorithms/itkLabelSampler.cpp Algorithms/itkSmoothedClassProbabilites.cpp Algorithms/mitkRandomImageSampler.cpp Features/itkNeighborhoodFunctorImageFilter.cpp Features/itkLineHistogramBasedMassImageFilter.cpp GlobalImageFeatures/mitkGIFCooccurenceMatrix.cpp GlobalImageFeatures/mitkGIFCooccurenceMatrix2.cpp GlobalImageFeatures/mitkGIFGreyLevelRunLength.cpp GlobalImageFeatures/mitkGIFImageDescriptionFeatures.cpp GlobalImageFeatures/mitkGIFFirstOrderStatistics.cpp GlobalImageFeatures/mitkGIFFirstOrderHistogramStatistics.cpp GlobalImageFeatures/mitkGIFFirstOrderNumericStatistics.cpp GlobalImageFeatures/mitkGIFVolumetricStatistics.cpp GlobalImageFeatures/mitkGIFNeighbouringGreyLevelDependenceFeatures.cpp GlobalImageFeatures/mitkGIFNeighbourhoodGreyLevelDifference.cpp GlobalImageFeatures/mitkGIFGreyLevelSizeZone.cpp GlobalImageFeatures/mitkGIFGreyLevelDistanceZone.cpp GlobalImageFeatures/mitkGIFLocalIntensity.cpp GlobalImageFeatures/mitkGIFVolumetricDensityStatistics.cpp GlobalImageFeatures/mitkGIFIntensityVolumeHistogramFeatures.cpp GlobalImageFeatures/mitkGIFNeighbourhoodGreyToneDifferenceFeatures.cpp GlobalImageFeatures/mitkGIFCurvatureStatistic.cpp MiniAppUtils/mitkGlobalImageFeaturesParameter.cpp MiniAppUtils/mitkSplitParameterToVector.cpp mitkCLUtil.cpp ) set( TOOL_FILES ) diff --git a/Modules/Classification/CLUtilities/include/mitkCLResultXMLWriter.h b/Modules/Classification/CLUtilities/include/mitkCLResultXMLWriter.h new file mode 100644 index 0000000000..73f805ae06 --- /dev/null +++ b/Modules/Classification/CLUtilities/include/mitkCLResultXMLWriter.h @@ -0,0 +1,60 @@ +/*============================================================================ + +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 mitkCLResultXMLWriter_h +#define mitkCLResultXMLWriter_h + +#include "MitkCLUtilitiesExports.h" + +#include "mitkImage.h" +#include "mitkAbstractGlobalImageFeature.h" + +namespace mitk +{ + namespace cl + { + class MITKCLUTILITIES_EXPORT CLResultXMLWriter + { + public: + CLResultXMLWriter() = default; + ~CLResultXMLWriter() = default; + + void SetImage(const Image* image); + void SetMask(const Image* mask); + void SetFeatures(const mitk::AbstractGlobalImageFeature::FeatureListType& features); + + void SetMethodName(const std::string& name); + void SetMethodVersion(const std::string& version); + void SetOrganisation(const std::string& orga); + void SetPipelineUID(const std::string& pipelineUID); + void SetCLIArgs(const std::map& args); + + void write(const std::string& filePath) const; + void write(std::ostream& stream) const; + + private: + CLResultXMLWriter(const CLResultXMLWriter&) = delete; + CLResultXMLWriter& operator = (const CLResultXMLWriter&) = delete; + + Image::ConstPointer m_Image; + Image::ConstPointer m_Mask; + mitk::AbstractGlobalImageFeature::FeatureListType m_Features; + std::string m_MethodName = "unknown"; + std::string m_MethodVersion = "unknown"; + std::string m_Organisation = "unknown"; + std::string m_PipelineUID = "unknown"; + std::map m_CLIArgs; + }; + } +} + +#endif //mitkCLResultXMLWriter_h diff --git a/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp b/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp new file mode 100644 index 0000000000..cdd9651d13 --- /dev/null +++ b/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp @@ -0,0 +1,296 @@ +/*============================================================================ + +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 + +#include +#include +#include + +#include + +template +class punct_facet : public std::numpunct { +public: + punct_facet(charT sep) : + m_Sep(sep) + { + + } +protected: + charT do_decimal_point() const override { return m_Sep; } +private: + charT m_Sep; +}; + +void AddSeriesInstanceUID(const mitk::Image* image, TiXmlElement* xmlNode) +{ + auto prop = image->GetProperty(mitk::DICOMTagPathToPropertyName(mitk::DICOMTagPath(0x0020, 0x000e)).c_str()); + if (prop.IsNotNull()) + { + auto seriesUIDNode = new TiXmlElement("mp:seriesInstanceUID"); + TiXmlText* valueText = new TiXmlText(prop->GetValueAsString()); + seriesUIDNode->LinkEndChild(valueText); + xmlNode->LinkEndChild(seriesUIDNode); + } +} + +void AddFilePath(const mitk::Image* image, TiXmlElement* xmlNode) +{ + auto prop = image->GetProperty("TODO_fileName"); + if (prop.IsNotNull()) + { + auto filePathNode = new TiXmlElement("mp:filePath"); + TiXmlText* valueText = new TiXmlText(prop->GetValueAsString()); + filePathNode->LinkEndChild(valueText); + + xmlNode->LinkEndChild(filePathNode); + } +} + +void AddSOPInstanceUIDs(const mitk::Image* image, TiXmlElement* xmlNode) +{ + auto prop = image->GetProperty(mitk::DICOMTagPathToPropertyName(mitk::DICOMTagPath(0x0008, 0x0018)).c_str()); + auto dicomProp = dynamic_cast(prop.GetPointer()); + if (dicomProp != nullptr) + { + auto instanceUIDsNode = new TiXmlElement("mp:sopInstanceUIDs"); + xmlNode->LinkEndChild(instanceUIDsNode); + + if (true /*TODO support compact writting*/) + { + auto instanceUIDNode = new TiXmlElement("mp:sopInstanceUID"); + TiXmlText* valueText = new TiXmlText(dicomProp->GetValueAsString()); + instanceUIDNode->LinkEndChild(valueText); + instanceUIDsNode->LinkEndChild(instanceUIDNode); + } + else + { + const auto timeSteps = dicomProp->GetAvailableTimeSteps(); + for (auto timeStep : timeSteps) + { + const auto slices = dicomProp->GetAvailableSlices(timeStep); + for (auto slice : slices) + { + auto instanceUIDNode = new TiXmlElement("mp:sopInstanceUID"); + instanceUIDNode->SetAttribute("z", slice); + instanceUIDNode->SetAttribute("t", timeStep); + TiXmlText* valueText = new TiXmlText(dicomProp->GetValue(timeStep, slice)); + instanceUIDNode->LinkEndChild(valueText); + instanceUIDsNode->LinkEndChild(instanceUIDNode); + } + } + } + } +} + +void AddDateAndTime(TiXmlElement* rootNode) +{ + auto dateNode = new TiXmlElement("mp:generationDate"); + auto time = std::time(nullptr); + std::ostringstream sstream; + sstream << std::put_time(std::localtime(&time), "%Y%m%d"); + TiXmlText* valueText = new TiXmlText(sstream.str()); + dateNode->LinkEndChild(valueText); + + rootNode->LinkEndChild(dateNode); + + auto timeNode = new TiXmlElement("mp:generationTime"); + std::ostringstream timestream; + timestream << std::put_time(std::localtime(&time), "%H%M%S"); + valueText = new TiXmlText(timestream.str()); + timeNode->LinkEndChild(valueText); + + rootNode->LinkEndChild(timeNode); +} + +void AddParameters(const std::map& parameters, TiXmlElement* paramsNode) +{ + for (const auto& param : parameters) + { + mitk::LocaleSwitch lswitch("C"); + auto paramNode = new TiXmlElement("mp:parameter"); + paramNode->SetAttribute("name", param.first); + TiXmlText* valueText = new TiXmlText(param.second.ToString()); + paramNode->LinkEndChild(valueText); + paramsNode->LinkEndChild(paramNode); + } +} + +void AddFeatures(const mitk::AbstractGlobalImageFeature::FeatureListType& features, TiXmlElement* featsNode) +{ + for (const auto& feat : features) + { + auto featNode = new TiXmlElement("mp:feature"); + featNode->SetAttribute("name", feat.first.name); + featNode->SetAttribute("version", feat.first.version); + featNode->SetAttribute("class", feat.first.featureClass); + featNode->SetAttribute("setting", feat.first.settingID); + std::ostringstream sstream; + sstream.imbue(std::locale("C")); + sstream << feat.second; + TiXmlText* valueText = new TiXmlText(sstream.str()); + featNode->LinkEndChild(valueText); + featsNode->LinkEndChild(featNode); + } +} + +void AddFeatureSettings(const mitk::AbstractGlobalImageFeature::FeatureListType& features, TiXmlElement* featSettingsNode) +{ + std::list coveredSettings; + for (const auto& feat : features) + { + auto finding = std::find(coveredSettings.begin(), coveredSettings.end(), feat.first.settingID); + if (finding == coveredSettings.end()) + { + auto featSettingNode = new TiXmlElement("mp:featureSetting"); + featSettingsNode->LinkEndChild(featSettingNode); + featSettingNode->SetAttribute("name", feat.first.settingID); + AddParameters(feat.first.parameters, featSettingNode); + coveredSettings.push_back(feat.first.settingID); + } + } +} + +void WriteDocument(std::ostream& stream, const mitk::Image* image, const mitk::Image* mask, + const mitk::AbstractGlobalImageFeature::FeatureListType& features, const std::string& methodName, + const std::string& organisation, const std::string& version, const std::string& pipelineUID, + const std::map& cliArgs) +{ + TiXmlDocument doc; + + auto decl = new TiXmlDeclaration( + "1.0", "UTF-8", ""); + doc.LinkEndChild(decl); + + auto rootNode = new TiXmlElement("mp:measurement"); + rootNode->SetAttribute("xmlns:mp", "http://www.mitk.org/Phenotyping"); + doc.LinkEndChild(rootNode); + + auto methodNode = new TiXmlElement("mp:measurementMethod"); + rootNode->LinkEndChild(methodNode); + + auto methodNameNode = new TiXmlElement("mp:name"); + TiXmlText* valueText = new TiXmlText(methodName); + methodNameNode->LinkEndChild(valueText); + methodNode->LinkEndChild(methodNameNode); + + auto organisationNode = new TiXmlElement("mp:organisation"); + valueText = new TiXmlText(organisation); + organisationNode->LinkEndChild(valueText); + methodNode->LinkEndChild(organisationNode); + + auto versionNode = new TiXmlElement("mp:version"); + valueText = new TiXmlText(version); + versionNode->LinkEndChild(valueText); + methodNode->LinkEndChild(versionNode); + + auto imageNode = new TiXmlElement("mp:image"); + rootNode->LinkEndChild(imageNode); + + AddSeriesInstanceUID(image, imageNode); + AddFilePath(image, imageNode); + AddSOPInstanceUIDs(image, imageNode); + + //todo image reader meta info + + auto maskNode = new TiXmlElement("mp:mask"); + rootNode->LinkEndChild(maskNode); + + AddSeriesInstanceUID(mask, maskNode); + AddFilePath(mask, maskNode); + AddSOPInstanceUIDs(mask, maskNode); + + //todo mask reader meta info + + AddDateAndTime(rootNode); + + auto pipelineNode = new TiXmlElement("mp:pipelineUID"); + valueText = new TiXmlText(pipelineUID); + pipelineNode->LinkEndChild(valueText); + rootNode->LinkEndChild(pipelineNode); + + auto paramsNode = new TiXmlElement("mp:parameters"); + rootNode->LinkEndChild(paramsNode); + AddParameters(cliArgs, paramsNode); + + auto featsNode = new TiXmlElement("mp:features"); + rootNode->LinkEndChild(featsNode); + + AddFeatures(features, featsNode); + + auto featSettingsNode = new TiXmlElement("mp:featureSettings"); + rootNode->LinkEndChild(featSettingsNode); + AddFeatureSettings(features, featSettingsNode); + + doc.SetCondenseWhiteSpace(false); + stream << doc; +} + +void mitk::cl::CLResultXMLWriter::SetImage(const Image* image) +{ + m_Image = image; +} + +void mitk::cl::CLResultXMLWriter::SetMask(const Image* mask) +{ + m_Mask = mask; +} + +void mitk::cl::CLResultXMLWriter::SetFeatures(const mitk::AbstractGlobalImageFeature::FeatureListType& features) +{ + m_Features = features; +} + +void mitk::cl::CLResultXMLWriter::SetMethodName(const std::string& name) +{ + m_MethodName = name; +} + +void mitk::cl::CLResultXMLWriter::SetMethodVersion(const std::string& version) +{ + m_MethodVersion = version; +} + +void mitk::cl::CLResultXMLWriter::SetOrganisation(const std::string& orga) +{ + m_Organisation = orga; +} + +void mitk::cl::CLResultXMLWriter::SetPipelineUID(const std::string& pipelineUID) +{ + m_PipelineUID = pipelineUID; +} + +void mitk::cl::CLResultXMLWriter::SetCLIArgs(const std::map& args) +{ + m_CLIArgs = args; +} + +void mitk::cl::CLResultXMLWriter::write(const std::string& filePath) const +{ + std::ofstream resultFile; + resultFile.open(filePath.c_str()); + this->write(resultFile); + resultFile.close(); +} + +void mitk::cl::CLResultXMLWriter::write(std::ostream& stream) const +{ + WriteDocument(stream, m_Image, m_Mask, m_Features, m_MethodName, m_Organisation, m_MethodVersion, m_PipelineUID, m_CLIArgs); +}