diff --git a/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp b/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp index cdd9651d13..8596eafdae 100644 --- a/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp +++ b/Modules/Classification/CLUtilities/src/mitkCLResultXMLWriter.cpp @@ -1,296 +1,315 @@ /*============================================================================ 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 +#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) +void AddPropertyAsNode(const mitk::Image* image, const std::string& key, const std::string& tag, TiXmlElement* rootNode) { - auto prop = image->GetProperty(mitk::DICOMTagPathToPropertyName(mitk::DICOMTagPath(0x0020, 0x000e)).c_str()); + auto prop = image->GetProperty(key.c_str()); if (prop.IsNotNull()) { - auto seriesUIDNode = new TiXmlElement("mp:seriesInstanceUID"); + auto propNode = new TiXmlElement(tag); TiXmlText* valueText = new TiXmlText(prop->GetValueAsString()); - seriesUIDNode->LinkEndChild(valueText); - xmlNode->LinkEndChild(seriesUIDNode); + propNode->LinkEndChild(valueText); + + rootNode->LinkEndChild(propNode); } } -void AddFilePath(const mitk::Image* image, TiXmlElement* xmlNode) +void AddSeriesInstanceUID(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); + AddPropertyAsNode(image, mitk::DICOMTagPathToPropertyName(mitk::DICOMTagPath(0x0020, 0x000e)), "mp:seriesInstanceUID", xmlNode); +} - xmlNode->LinkEndChild(filePathNode); - } +void AddFilePath(const mitk::Image* image, TiXmlElement* xmlNode) +{ + AddPropertyAsNode(image, mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()), "mp:filePath", xmlNode); } 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*/) + if (dicomProp->IsUniform()) { 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 AddReaderInfo(const mitk::Image* image, TiXmlElement* imageNode) +{ + auto ioNode = new TiXmlElement("mp:IOReader"); + imageNode->LinkEndChild(ioNode); + AddPropertyAsNode(image, mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()), "mp:description", ioNode); + AddPropertyAsNode(image, mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_VERSION()), "mp:version", ioNode); + AddPropertyAsNode(image, mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION()), "mp:configuration", ioNode); + AddPropertyAsNode(image, mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM()), "mp:gdcmVersion", ioNode); + AddPropertyAsNode(image, mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK()), "mp:dcmtkVersion", ioNode); +} + 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); + AddReaderInfo(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); + AddReaderInfo(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(); + if (resultFile.is_open()) + { + this->write(resultFile); + resultFile.close(); + } + else + { + MITK_ERROR << "Cannot write xml results. Unable to open file: \""< class ImagePixelReadAccessor : public ImagePixelAccessor { friend class Image; public: typedef ImagePixelAccessor ImagePixelAccessorType; typedef itk::SmartPointer ImagePointer; typedef itk::SmartPointer ImageConstPointer; /** \brief Instantiates a mitk::ImageReadAccessor (see its doxygen page for more details) * \param Image::Pointer specifies the associated Image * \param ImageDataItem* specifies the allocated image part * \param OptionFlags properties from mitk::ImageAccessorBase::Options can be chosen and assembled with bitwise * unification. * \throws mitk::Exception if the Constructor was created inappropriately * \throws mitk::MemoryIsLockedException if requested image area is exclusively locked and * mitk::ImageAccessorBase::ExceptionIfLocked is set in OptionFlags * * Includes a check if typeid of PixelType coincides with templated TPixel * and a check if VDimension equals to the Dimension of the Image. */ ImagePixelReadAccessor(ImageConstPointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP, iDI), m_ReadAccessor(iP, iDI, OptionFlags) { } ImagePixelReadAccessor(ImagePointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP.GetPointer(), iDI), m_ReadAccessor(iP, iDI, OptionFlags) { } ImagePixelReadAccessor(Image *iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP, iDI), m_ReadAccessor(mitk::Image::ConstPointer(iP), iDI, OptionFlags) { } ImagePixelReadAccessor(const Image *iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP, iDI), m_ReadAccessor(iP, iDI, OptionFlags) { } /** Destructor informs Image to unlock memory. */ ~ImagePixelReadAccessor() override {} /** Returns a const reference to the pixel at given index. */ const TPixel &GetPixelByIndex(const itk::Index &idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); return *(((TPixel *)m_ReadAccessor.m_AddressBegin) + offset); } itk::VariableLengthVector GetConsecutivePixelsAsVector(const itk::Index &idx, int nrComponents) const { return itk::VariableLengthVector( (TPixel *)m_ReadAccessor.m_AddressBegin + ImagePixelAccessorType::GetOffset(idx) * m_ReadAccessor.GetImage()->GetPixelType().GetNumberOfComponents(), nrComponents); } /** Extends GetPixel by integrating index validation to prevent overflow. * \throws mitk::Exception in case of overflow */ const TPixel &GetPixelByIndexSafe(const itk::Index &idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel *targetAddress = ((TPixel *)m_ReadAccessor.m_AddressBegin) + offset; if (!(targetAddress >= m_ReadAccessor.m_AddressBegin && targetAddress < m_ReadAccessor.m_AddressEnd)) { mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } return *targetAddress; } /** Returns a const reference to the pixel at given world coordinate - works only with three-dimensional * ImageAccessor */ const TPixel &GetPixelByWorldCoordinates(mitk::Point3D position) { itk::Index<3> itkIndex; m_ReadAccessor.GetImage()->GetGeometry()->WorldToIndex(position, itkIndex); return GetPixelByIndex(itkIndex); } /** Returns a const reference to the pixel at given world coordinate - works only with four-dimensional * ImageAccessor */ const TPixel &GetPixelByWorldCoordinates(mitk::Point3D position, unsigned int timestep) { itk::Index<3> itkIndex; m_ReadAccessor.GetImage()->GetGeometry()->WorldToIndex(position, itkIndex); if (m_ReadAccessor.GetImage()->GetTimeSteps() < timestep) { timestep = m_ReadAccessor.GetImage()->GetTimeSteps(); } itk::Index<4> itk4Index; for (int i = 0; i < 3; ++i) itk4Index[i] = itkIndex[i]; itk4Index[3] = timestep; return GetPixelByIndex(itk4Index); } /** \brief Gives const access to the data. */ inline const TPixel *GetData() const { return static_cast(m_ReadAccessor.m_AddressBegin); } protected: // protected members private: ImageReadAccessor m_ReadAccessor; ImagePixelReadAccessor &operator=(const ImagePixelReadAccessor &); // Not implemented on purpose. ImagePixelReadAccessor(const ImagePixelReadAccessor &); }; /** Static method that gets direct access to a single pixel value. * The value is not guaranteed to be in a well-defined state and is automatically casted to mitk::ScalarType * The method can be called by the macros in mitkPixelTypeMultiplex.h */ template mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::Pointer im, ImageDataItem *item, itk::Index<3> idx, mitk::ScalarType &val, int component = 0) { ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); val = imAccess.GetConsecutivePixelsAsVector(idx, component + 1).GetElement(component); return val; } /** Const overload of FastSinglePixelAccess*/ template mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::ConstPointer im, const ImageDataItem* item, itk::Index<3> idx, mitk::ScalarType& val, int component = 0) { ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); val = imAccess.GetConsecutivePixelsAsVector(idx, component + 1).GetElement(component); return val; } - /** Const overload of FastSinglePixelAccess*/ - template - mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, - mitk::Image::ConstPointer im, - const ImageDataItem* item, - itk::Index<3> idx, - mitk::ScalarType& val, - int component = 0) - { - mitk::ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); - val = imAccess.GetConsecutivePixelsAsVector(idx, component + 1).GetElement(component); - return val; - } } #endif // MITKIMAGEPIXELREADACCESSOR_H