diff --git a/Modules/Core/src/IO/mitkGeometry3DToXML.cpp b/Modules/Core/src/IO/mitkGeometry3DToXML.cpp index eb6f9e25e6..e9ee434cf7 100644 --- a/Modules/Core/src/IO/mitkGeometry3DToXML.cpp +++ b/Modules/Core/src/IO/mitkGeometry3DToXML.cpp @@ -1,242 +1,242 @@ /*============================================================================ 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 "mitkGeometry3DToXML.h" #include #include #include tinyxml2::XMLElement *mitk::Geometry3DToXML::ToXML(tinyxml2::XMLDocument& doc, const Geometry3D *geom3D) { assert(geom3D); // really serialize const AffineTransform3D *transform = geom3D->GetIndexToWorldTransform(); // get transform parameters that would need to be serialized AffineTransform3D::MatrixType matrix = transform->GetMatrix(); AffineTransform3D::OffsetType offset = transform->GetOffset(); bool isImageGeometry = geom3D->GetImageGeometry(); BaseGeometry::BoundsArrayType bounds = geom3D->GetBounds(); // create XML file // construct XML tree describing the geometry auto *geomElem = doc.NewElement("Geometry3D"); - geomElem->SetAttribute("ImageGeometry", isImageGeometry ? "true" : "false"); + geomElem->SetAttribute("ImageGeometry", isImageGeometry); geomElem->SetAttribute("FrameOfReferenceID", geom3D->GetFrameOfReferenceID()); // coefficients are matrix[row][column]! auto *matrixElem = doc.NewElement("IndexToWorld"); matrixElem->SetAttribute("type", "Matrix3x3"); matrixElem->SetAttribute("m_0_0", boost::lexical_cast(matrix[0][0]).c_str()); matrixElem->SetAttribute("m_0_1", boost::lexical_cast(matrix[0][1]).c_str()); matrixElem->SetAttribute("m_0_2", boost::lexical_cast(matrix[0][2]).c_str()); matrixElem->SetAttribute("m_1_0", boost::lexical_cast(matrix[1][0]).c_str()); matrixElem->SetAttribute("m_1_1", boost::lexical_cast(matrix[1][1]).c_str()); matrixElem->SetAttribute("m_1_2", boost::lexical_cast(matrix[1][2]).c_str()); matrixElem->SetAttribute("m_2_0", boost::lexical_cast(matrix[2][0]).c_str()); matrixElem->SetAttribute("m_2_1", boost::lexical_cast(matrix[2][1]).c_str()); matrixElem->SetAttribute("m_2_2", boost::lexical_cast(matrix[2][2]).c_str()); geomElem->InsertEndChild(matrixElem); auto *offsetElem = doc.NewElement("Offset"); offsetElem->SetAttribute("type", "Vector3D"); offsetElem->SetAttribute("x", boost::lexical_cast(offset[0]).c_str()); offsetElem->SetAttribute("y", boost::lexical_cast(offset[1]).c_str()); offsetElem->SetAttribute("z", boost::lexical_cast(offset[2]).c_str()); geomElem->InsertEndChild(offsetElem); auto *boundsElem = doc.NewElement("Bounds"); auto *boundsMinElem = doc.NewElement("Min"); boundsMinElem->SetAttribute("type", "Vector3D"); boundsMinElem->SetAttribute("x", boost::lexical_cast(bounds[0]).c_str()); boundsMinElem->SetAttribute("y", boost::lexical_cast(bounds[2]).c_str()); boundsMinElem->SetAttribute("z", boost::lexical_cast(bounds[4]).c_str()); boundsElem->InsertEndChild(boundsMinElem); auto *boundsMaxElem = doc.NewElement("Max"); boundsMaxElem->SetAttribute("type", "Vector3D"); boundsMaxElem->SetAttribute("x", boost::lexical_cast(bounds[1]).c_str()); boundsMaxElem->SetAttribute("y", boost::lexical_cast(bounds[3]).c_str()); boundsMaxElem->SetAttribute("z", boost::lexical_cast(bounds[5]).c_str()); boundsElem->InsertEndChild(boundsMaxElem); geomElem->InsertEndChild(boundsElem); return geomElem; } mitk::Geometry3D::Pointer mitk::Geometry3DToXML::FromXML(const tinyxml2::XMLElement *geometryElement) { if (!geometryElement) { MITK_ERROR << "Cannot deserialize Geometry3D from nullptr."; return nullptr; } AffineTransform3D::MatrixType matrix; AffineTransform3D::OffsetType offset; bool isImageGeometry(false); unsigned int frameOfReferenceID(0); BaseGeometry::BoundsArrayType bounds; if (tinyxml2::XML_SUCCESS != geometryElement->QueryUnsignedAttribute("FrameOfReferenceID", &frameOfReferenceID)) { MITK_WARN << "Missing FrameOfReference for Geometry3D."; } if (tinyxml2::XML_SUCCESS != geometryElement->QueryBoolAttribute("ImageGeometry", &isImageGeometry)) { MITK_WARN << "Missing bool ImageGeometry for Geometry3D."; } // matrix if (auto *matrixElem = geometryElement->FirstChildElement("IndexToWorld")) { bool matrixComplete = true; for (unsigned int r = 0; r < 3; ++r) { for (unsigned int c = 0; c < 3; ++c) { std::stringstream element_namer; element_namer << "m_" << r << "_" << c; const char* string_value = matrixElem->Attribute(element_namer.str().c_str()); if (nullptr != string_value) { try { matrix[r][c] = boost::lexical_cast(string_value); } catch ( const boost::bad_lexical_cast &e ) { MITK_ERROR << "Could not parse '" << string_value << "' as number: " << e.what(); return nullptr; } } else { matrixComplete = false; } } } if (!matrixComplete) { MITK_ERROR << "Could not parse all Geometry3D matrix coefficients!"; return nullptr; } } else { MITK_ERROR << "Parse error: expected Matrix3x3 child below Geometry3D node"; return nullptr; } // offset if (auto *offsetElem = geometryElement->FirstChildElement("Offset")) { std::array offset_string = { offsetElem->Attribute("x"), offsetElem->Attribute("y"), offsetElem->Attribute("z") }; if (nullptr == offset_string[0] || nullptr == offset_string[1] || nullptr == offset_string[2]) { MITK_ERROR << "Could not parse complete Geometry3D offset!"; return nullptr; } for (unsigned int d = 0; d < 3; ++d) try { offset[d] = boost::lexical_cast(offset_string[d]); } catch ( const boost::bad_lexical_cast &e ) { MITK_ERROR << "Could not parse '" << offset_string[d] << "' as number: " << e.what(); return nullptr; } } else { MITK_ERROR << "Parse error: expected Offset3D child below Geometry3D node"; return nullptr; } // bounds if (auto *boundsElem = geometryElement->FirstChildElement("Bounds")) { bool vectorsComplete; std::array bounds_string; if (auto* minElem = boundsElem->FirstChildElement("Min")) { bounds_string[0] = minElem->Attribute("x"); bounds_string[2] = minElem->Attribute("y"); bounds_string[4] = minElem->Attribute("z"); vectorsComplete = !(nullptr == bounds_string[0] || nullptr == bounds_string[2] || nullptr == bounds_string[4]); } else { vectorsComplete = false; } if (auto *maxElem = boundsElem->FirstChildElement("Max")) { bounds_string[1] = maxElem->Attribute("x"); bounds_string[3] = maxElem->Attribute("y"); bounds_string[5] = maxElem->Attribute("z"); vectorsComplete = !(nullptr == bounds_string[1] || nullptr == bounds_string[3] || nullptr == bounds_string[5]); } else { vectorsComplete = false; } if (!vectorsComplete) { MITK_ERROR << "Could not parse complete Geometry3D bounds!"; return nullptr; } for (unsigned int d = 0; d < 6; ++d) try { bounds[d] = boost::lexical_cast(bounds_string[d]); } catch ( const boost::bad_lexical_cast &e ) { MITK_ERROR << "Could not parse '" << bounds_string[d] << "' as number: " << e.what(); return nullptr; } } // build GeometryData from matrix/offset AffineTransform3D::Pointer newTransform = AffineTransform3D::New(); newTransform->SetMatrix(matrix); newTransform->SetOffset(offset); Geometry3D::Pointer newGeometry = Geometry3D::New(); newGeometry->SetFrameOfReferenceID(frameOfReferenceID); newGeometry->SetImageGeometry(isImageGeometry); newGeometry->SetIndexToWorldTransform(newTransform); newGeometry->SetBounds(bounds); return newGeometry; } diff --git a/Modules/DICOM/include/mitkDICOMReaderConfigurator.h b/Modules/DICOM/include/mitkDICOMReaderConfigurator.h index 8c2628623e..00da6131c2 100644 --- a/Modules/DICOM/include/mitkDICOMReaderConfigurator.h +++ b/Modules/DICOM/include/mitkDICOMReaderConfigurator.h @@ -1,145 +1,145 @@ /*============================================================================ 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 mitkDICOMReaderConfigurator_h #define mitkDICOMReaderConfigurator_h #include "mitkClassicDICOMSeriesReader.h" #include "mitkDICOMTagBasedSorter.h" namespace tinyxml2 { class XMLDocument; class XMLElement; } namespace mitk { /** \ingroup DICOMModule \brief Too-simple factory to create DICOMFileReader%s. This class is able to instantiate and configure (where possible) DICOMFileReader%s from XML descriptions. \note This is a bad factory example, because the factory is not extensible and needs to know all the specific readers. A flexible implementation should be provided in a future version. In its current version, the XML input is meant to be structured like \verbatim \endverbatim The root-tag \c \ names the class to be instantiated, currently this can be one of - DICOMITKSeriesGDCMReader - ThreeDnTDICOMSeriesReader Both classes bring simple configuration flags with them and a description of how images are sorted prior to loading. Flag for DICOMITKSeriesGDCMReader:
fixTiltByShearing="true|false"
Determines whether a potential gantry tilt should be "fixed" by shearing the output image. Flag for ThreeDnTDICOMSeriesReader:
group3DnT="true|false"
Determines whether images at the same spatial position should be interpreted as 3D+t images. The tags \c \ and \c \ describe the basic loading strategy of both reader mentioned above: first images are divided into incompatible groups (\c \), and afterwards the images within each group are sorted by means of DICOMSortCriterion, which most commonly mentions a tag. Tag element and group are interpreted as the exadecimal numbers found all around the DICOM standard. The numbers can be prepended by a "0x" if this is preferred by the programmer (but they are taken as hexadecimal in all cases). \section DICOMReaderConfigurator_AboutTheFuture About the future evolution of this class This first version is hard coded for the current state of the implementation. If things should evolve in a way that needs us to splitt off readers for "old" versions, time should be taken to refactor this class. Basically, a serializer class should accompany each of the configurable classes. Such serializer classes should be registered and discovered via micro-services (to support extensions). A serializer should offer both methods to serialize a class and to desirialize it again. A "version" attribute at the top-level tag should be used to distinguish versions. Usually it should be enough to keep DE-serializers for all versions. Writers for the most recent version should be enough. */ class MITKDICOM_EXPORT DICOMReaderConfigurator : public itk::LightObject { public: mitkClassMacroItkParent( DICOMReaderConfigurator, itk::LightObject ); itkNewMacro( DICOMReaderConfigurator ); DICOMFileReader::Pointer CreateFromConfigFile(const std::string& filename) const; DICOMFileReader::Pointer CreateFromUTF8ConfigString(const std::string& xmlContents) const; std::string CreateConfigStringFromReader(DICOMFileReader::ConstPointer reader) const; protected: DICOMReaderConfigurator(); ~DICOMReaderConfigurator() override; private: DICOMFileReader::Pointer CreateFromXMLDocument(tinyxml2::XMLDocument& doc) const; DICOMTag tagFromXMLElement(const tinyxml2::XMLElement*) const; std::string requiredStringAttribute(const tinyxml2::XMLElement* xmlElement, const std::string& key) const; unsigned int hexStringToUInt(const std::string& s) const; ThreeDnTDICOMSeriesReader::Pointer ConfigureThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, const tinyxml2::XMLElement*) const; DICOMITKSeriesGDCMReader::Pointer ConfigureDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, const tinyxml2::XMLElement*) const; void ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, const tinyxml2::XMLElement* element) const; void ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, const tinyxml2::XMLElement* element) const; DICOMSortCriterion::Pointer CreateDICOMSortByTag(const tinyxml2::XMLElement* xmlElement, DICOMSortCriterion::Pointer secondaryCriterion) const; DICOMSortCriterion::Pointer CreateSortByImagePositionPatient(const tinyxml2::XMLElement* xmlElement, DICOMSortCriterion::Pointer secondaryCriterion) const; mitk::DICOMTagBasedSorter::Pointer CreateDICOMTagBasedSorter(const tinyxml2::XMLElement* element) const; tinyxml2::XMLElement* CreateConfigStringFromReader(tinyxml2::XMLDocument& doc, const DICOMITKSeriesGDCMReader* reader) const; tinyxml2::XMLElement* CreateConfigStringFromReader(tinyxml2::XMLDocument& doc, const ThreeDnTDICOMSeriesReader* reader) const; tinyxml2::XMLElement* CreateConfigStringFromReader(tinyxml2::XMLDocument& doc, const ClassicDICOMSeriesReader* reader) const; tinyxml2::XMLElement* CreateConfigStringFromDICOMDatasetSorter(tinyxml2::XMLDocument& doc, const DICOMTagBasedSorter* sorter) const; tinyxml2::XMLElement* CreateConfigStringFromDICOMTag(tinyxml2::XMLDocument& doc, const DICOMTag& tag) const; tinyxml2::XMLElement* CreateDICOMFileReaderTag(tinyxml2::XMLDocument& doc, const DICOMFileReader* reader) const; - const char* toString(bool) const; + std::string toHexString(unsigned int i) const; /** Helper that queries an boolean xml attribute. If the attribute does not exist, the passed default value is used.*/ bool QueryBooleanAttribute(const tinyxml2::XMLElement* element, const char* attributeName, bool defaultValue) const; }; } // namespace #endif // mitkDICOMReaderConfigurator_h diff --git a/Modules/DICOM/src/mitkDICOMReaderConfigurator.cpp b/Modules/DICOM/src/mitkDICOMReaderConfigurator.cpp index e68b6daacf..68f7a4dccb 100644 --- a/Modules/DICOM/src/mitkDICOMReaderConfigurator.cpp +++ b/Modules/DICOM/src/mitkDICOMReaderConfigurator.cpp @@ -1,680 +1,673 @@ /*============================================================================ 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 "mitkDICOMReaderConfigurator.h" #include "mitkDICOMSortByTag.h" #include "mitkSortByImagePositionPatient.h" #include mitk::DICOMReaderConfigurator ::DICOMReaderConfigurator() { } mitk::DICOMReaderConfigurator ::~DICOMReaderConfigurator() { } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromConfigFile(const std::string& filename) const { tinyxml2::XMLDocument doc; if (tinyxml2::XML_SUCCESS == doc.LoadFile(filename.c_str())) { return this->CreateFromXMLDocument( doc ); } else { MITK_ERROR << "Unable to load file at '" << filename <<"'"; return DICOMFileReader::Pointer(); } } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromUTF8ConfigString(const std::string& xmlContents) const { tinyxml2::XMLDocument doc; doc.Parse(xmlContents.c_str()); return this->CreateFromXMLDocument( doc ); } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromXMLDocument(tinyxml2::XMLDocument& doc) const { tinyxml2::XMLHandle root(doc.RootElement()); if (auto* rootElement = root.ToElement()) { if (strcmp(rootElement->Value(), "DICOMFileReader")) // :-( no std::string methods { MITK_ERROR << "File should contain a tag at top-level! Found '" << (rootElement->Value() ? std::string(rootElement->Value()) : std::string("!nothing!")) << "' instead"; return nullptr; } const char* classnameC = rootElement->Attribute("class"); if (!classnameC) { MITK_ERROR << "File should name a reader class in the class attribute: . Found nothing instead"; return nullptr; } int version(1); if ( rootElement->QueryIntAttribute("version", &version) == tinyxml2::XML_SUCCESS) { if (version == 1) { MITK_WARN << "Warning the given configuration is for DICOMFileReaders of version 1. " << "This old version may be interpreted differently. Reason: " << "The default values for the following xml settings have been changed: " << "FixTiltByShearing (false -> true); StrictSorting (true -> false); ExpectDistanceOne (true -> false)."; } else if (version >2) { MITK_WARN << "This reader is only capable of creating DICOMFileReaders of version 1 and 2. " << "Will not continue, because given configuration is meant for version " << version << "."; return nullptr; } } else { MITK_ERROR << "File should name the version of the reader class in the version attribute: ." << " Found nothing instead, assuming version 1!"; version = 1; } std::string classname(classnameC); double decimalPlacesForOrientation(mitk::DICOMITKSeriesGDCMReader::GetDefaultDecimalPlacesForOrientation()); bool useDecimalPlacesForOrientation(false); useDecimalPlacesForOrientation = rootElement->QueryDoubleAttribute("decimalPlacesForOrientation", &decimalPlacesForOrientation) == tinyxml2::XML_SUCCESS; // attribute present and a double value if (classname == "ClassicDICOMSeriesReader") { mitk::ClassicDICOMSeriesReader::Pointer reader = mitk::ClassicDICOMSeriesReader::New(); this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader.GetPointer(), rootElement); this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader.GetPointer(), rootElement); return reader.GetPointer(); } if (classname == "ThreeDnTDICOMSeriesReader") { mitk::ThreeDnTDICOMSeriesReader::Pointer reader; if (useDecimalPlacesForOrientation) reader = mitk::ThreeDnTDICOMSeriesReader::New(decimalPlacesForOrientation); else reader = mitk::ThreeDnTDICOMSeriesReader::New(); return ConfigureThreeDnTDICOMSeriesReader(reader, rootElement).GetPointer(); } else if (classname == "DICOMITKSeriesGDCMReader") { bool simpleVolumeImport = QueryBooleanAttribute(rootElement, "simpleVolumeImport", mitk::DICOMITKSeriesGDCMReader::GetDefaultSimpleVolumeImport()); mitk::DICOMITKSeriesGDCMReader::Pointer reader; if (useDecimalPlacesForOrientation) reader = mitk::DICOMITKSeriesGDCMReader::New( decimalPlacesForOrientation, simpleVolumeImport ); else reader = mitk::DICOMITKSeriesGDCMReader::New( mitk::DICOMITKSeriesGDCMReader::GetDefaultDecimalPlacesForOrientation(), simpleVolumeImport ); // simple volume import that ignores number of frames and inter slice distance return ConfigureDICOMITKSeriesGDCMReader(reader, rootElement).GetPointer(); } else { MITK_ERROR << "DICOMFileReader tag names unknown class '" << classname << "'"; return nullptr; } } else { MITK_ERROR << "Great confusion: no root element in XML document. Expecting a DICOMFileReader tag at top-level."; return nullptr; } } #define boolStringTrue(s) \ ( s == "true" || s == "on" || s == "1" \ || s == "TRUE" || s == "ON") bool mitk::DICOMReaderConfigurator ::QueryBooleanAttribute(const tinyxml2::XMLElement* element, const char* attributeName, bool defaultValue) const { bool value(defaultValue); const auto* valueC = element->Attribute(attributeName); if (nullptr != valueC) { std::string valueS = valueC; value = boolStringTrue(valueS); } return value; } void mitk::DICOMReaderConfigurator ::ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, const tinyxml2::XMLElement* element) const { // add the "group3DnT" flag bool group3DnT = QueryBooleanAttribute(element, "group3DnT", ThreeDnTDICOMSeriesReader::GetDefaultGroup3DandT()); reader->SetGroup3DandT( group3DnT ); // add the "onlyCondenseSameSeries" flag bool onlyCondenseSameSeries = QueryBooleanAttribute(element, "onlyCondenseSameSeries", ThreeDnTDICOMSeriesReader::GetDefaultOnlyCondenseSameSeries()); reader->SetOnlyCondenseSameSeries(onlyCondenseSameSeries); } mitk::ThreeDnTDICOMSeriesReader::Pointer mitk::DICOMReaderConfigurator ::ConfigureThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, const tinyxml2::XMLElement* element) const { assert(element); // use all the base class configuration if (this->ConfigureDICOMITKSeriesGDCMReader( reader.GetPointer(), element ).IsNull()) { return nullptr; } this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader,element); return reader; } void mitk::DICOMReaderConfigurator ::ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, const tinyxml2::XMLElement* element) const { assert(element); const char* configLabelC = element->Attribute("label"); if (configLabelC) { std::string configLabel(configLabelC); reader->SetConfigurationLabel(configLabel); } const char* configDescriptionC = element->Attribute("description"); if (configDescriptionC) { reader->SetConfigurationDescription(configDescriptionC); } // "fixTiltByShearing" flag bool fixTiltByShearing = QueryBooleanAttribute(element, "fixTiltByShearing", DICOMITKSeriesGDCMReader::GetDefaultFixTiltByShearing()); reader->SetFixTiltByShearing( fixTiltByShearing ); } mitk::DICOMITKSeriesGDCMReader::Pointer mitk::DICOMReaderConfigurator ::ConfigureDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, const tinyxml2::XMLElement* element) const { assert(element); this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader, element); // "acceptTwoSlicesGroups" flag bool acceptTwoSlicesGroups = QueryBooleanAttribute(element, "acceptTwoSlicesGroups", true); reader->SetAcceptTwoSlicesGroups( acceptTwoSlicesGroups ); // "toleratedOriginError" attribute (double) bool toleratedOriginErrorIsAbsolute = QueryBooleanAttribute(element, "toleratedOriginErrorIsAbsolute", false); double toleratedOriginError(0.3); if (element->QueryDoubleAttribute("toleratedOriginError", &toleratedOriginError) == tinyxml2::XML_SUCCESS) // attribute present and a double value { if (toleratedOriginErrorIsAbsolute) { reader->SetToleratedOriginOffset( toleratedOriginError ); } else { reader->SetToleratedOriginOffsetToAdaptive( toleratedOriginError ); } } // DICOMTagBasedSorters are the only thing we create at this point // TODO for-loop over all child elements of type DICOMTagBasedSorter, BUT actually a single sorter of this type is enough. auto* dElement = element->FirstChildElement("DICOMDatasetSorter"); if (dElement) { const char* classnameC = dElement->Attribute("class"); if (!classnameC) { MITK_ERROR << "File should name a DICOMDatasetSorter class in the class attribute of . Found nothing instead"; return nullptr; } std::string classname(classnameC); if (classname == "DICOMTagBasedSorter") { DICOMTagBasedSorter::Pointer tagSorter = CreateDICOMTagBasedSorter(dElement); if (tagSorter.IsNotNull()) { reader->AddSortingElement( tagSorter ); } } else { MITK_ERROR << "DICOMDatasetSorter tag names unknown class '" << classname << "'"; return nullptr; } } return reader; } mitk::DICOMTagBasedSorter::Pointer mitk::DICOMReaderConfigurator ::CreateDICOMTagBasedSorter(const tinyxml2::XMLElement* element) const { mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New(); // "strictSorting" parameter! bool strictSorting = QueryBooleanAttribute(element, "strictSorting", mitk::DICOMTagBasedSorter::GetDefaultStrictSorting()); tagSorter->SetStrictSorting(strictSorting); // "strictSorting" parameter! bool expectDistanceOne = QueryBooleanAttribute(element, "expectDistanceOne", mitk::DICOMTagBasedSorter::GetDefaultExpectDistanceOne()); tagSorter->SetExpectDistanceOne(expectDistanceOne); auto* dElement = element->FirstChildElement("Distinguishing"); if (dElement) { for ( auto* tChild = dElement->FirstChildElement(); tChild != nullptr; tChild = tChild->NextSiblingElement() ) { try { mitk::DICOMTag tag = tagFromXMLElement(tChild); int i(5); if (tChild->QueryIntAttribute("cutDecimalPlaces", &i) == tinyxml2::XML_SUCCESS) { tagSorter->AddDistinguishingTag( tag, new mitk::DICOMTagBasedSorter::CutDecimalPlaces(i) ); } else { tagSorter->AddDistinguishingTag( tag ); } } catch(...) { return nullptr; } } } // "sorting tags" auto* sElement = element->FirstChildElement("Sorting"); if (sElement) { DICOMSortCriterion::Pointer previousCriterion; DICOMSortCriterion::Pointer currentCriterion; for ( auto* tChildNode = sElement->LastChild(); tChildNode != nullptr; tChildNode = tChildNode->PreviousSibling() ) { auto* tChild = tChildNode->ToElement(); if (!tChild) continue; if (!strcmp(tChild->Value(), "Tag")) { try { currentCriterion = this->CreateDICOMSortByTag(tChild, previousCriterion); } catch(...) { std::stringstream ss; ss << "Could not parse element at input line " << tChild->GetLineNum() << "!"; MITK_ERROR << ss.str(); return nullptr; } } else if (!strcmp(tChild->Value(), "ImagePositionPatient")) { try { currentCriterion = this->CreateSortByImagePositionPatient(tChild, previousCriterion); } catch(...) { std::stringstream ss; ss << "Could not parse element at input line " << tChild->GetLineNum() << "!"; MITK_ERROR << ss.str(); return nullptr; } } else { MITK_ERROR << "File contain unknown tag <" << tChild->Value() << "> tag as child to ! Cannot interpret..."; } previousCriterion = currentCriterion; } tagSorter->SetSortCriterion( currentCriterion.GetPointer() ); } return tagSorter; } std::string mitk::DICOMReaderConfigurator ::requiredStringAttribute(const tinyxml2::XMLElement* xmlElement, const std::string& key) const { assert(xmlElement); const char* gC = xmlElement->Attribute(key.c_str()); if (gC) { std::string gS(gC); return gS; } else { std::stringstream ss; ss << "Expected an attribute '" << key << "' at " "input line " << xmlElement->GetLineNum() << "!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } } unsigned int mitk::DICOMReaderConfigurator ::hexStringToUInt(const std::string& s) const { std::stringstream converter(s); unsigned int ui; converter >> std::hex >> ui; MITK_DEBUG << "Converted string '" << s << "' to unsigned int " << ui; return ui; } mitk::DICOMTag mitk::DICOMReaderConfigurator ::tagFromXMLElement(const tinyxml2::XMLElement* xmlElement) const { assert(xmlElement); if (strcmp(xmlElement->Value(), "Tag")) // :-( no std::string methods { std::stringstream ss; ss << "Expected a tag at " "input line " << xmlElement->GetLineNum() << "!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } std::string groupS = requiredStringAttribute(xmlElement, "group"); std::string elementS = requiredStringAttribute(xmlElement, "element"); try { // convert string to int (assuming string is in hex format with leading "0x" like "0x0020") unsigned int group = hexStringToUInt(groupS); unsigned int element = hexStringToUInt(elementS); return DICOMTag(group, element); } catch(...) { std::stringstream ss; ss << "Expected group and element values in to be hexadecimal with leading 0x, e.g. '0x0020'" "(input line " << xmlElement->GetLineNum() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } } mitk::DICOMSortCriterion::Pointer mitk::DICOMReaderConfigurator ::CreateDICOMSortByTag(const tinyxml2::XMLElement* xmlElement, DICOMSortCriterion::Pointer secondaryCriterion) const { mitk::DICOMTag tag = tagFromXMLElement(xmlElement); return DICOMSortByTag::New(tag, secondaryCriterion).GetPointer(); } mitk::DICOMSortCriterion::Pointer mitk::DICOMReaderConfigurator ::CreateSortByImagePositionPatient(const tinyxml2::XMLElement*, DICOMSortCriterion::Pointer secondaryCriterion) const { return SortByImagePositionPatient::New(secondaryCriterion).GetPointer(); } std::string mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(DICOMFileReader::ConstPointer reader) const { // check possible sub-classes from the most-specific one up to the most generic one const DICOMFileReader* cPointer = reader; tinyxml2::XMLDocument document; tinyxml2::XMLElement* root = nullptr; if (const auto* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(document, specificReader); } else if (const auto* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(document, specificReader); } else if (const auto* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(document, specificReader); } else { MITK_WARN << "Unknown reader class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize."; return ""; // no serialization, what a pity } if (nullptr != root) { document.InsertEndChild( root ); tinyxml2::XMLPrinter printer; document.Print(&printer); std::string xmltext = printer.CStr(); return xmltext; } else { MITK_WARN << "DICOMReaderConfigurator::CreateConfigStringFromReader() created empty serialization. Problem?"; return ""; } } tinyxml2::XMLElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(tinyxml2::XMLDocument &doc, const DICOMITKSeriesGDCMReader* reader) const { auto* root = this->CreateDICOMFileReaderTag(doc, reader); assert(root); - root->SetAttribute("fixTiltByShearing", toString(reader->GetFixTiltByShearing())); - root->SetAttribute("acceptTwoSlicesGroups", toString(reader->GetAcceptTwoSlicesGroups())); + root->SetAttribute("fixTiltByShearing", reader->GetFixTiltByShearing()); + root->SetAttribute("acceptTwoSlicesGroups", reader->GetAcceptTwoSlicesGroups()); root->SetAttribute("toleratedOriginError", reader->GetToleratedOriginError()); - root->SetAttribute("toleratedOriginErrorIsAbsolute", toString(reader->IsToleratedOriginOffsetAbsolute())); + root->SetAttribute("toleratedOriginErrorIsAbsolute", reader->IsToleratedOriginOffsetAbsolute()); root->SetAttribute("decimalPlacesForOrientation", reader->GetDecimalPlacesForOrientation()); // iterate DICOMDatasetSorter objects DICOMITKSeriesGDCMReader::ConstSorterList sorterList = reader->GetFreelyConfiguredSortingElements(); for(auto sorterIter = sorterList.begin(); sorterIter != sorterList.end(); ++sorterIter) { const DICOMDatasetSorter* sorter = *sorterIter; if (const auto* specificSorter = dynamic_cast(sorter)) { auto* sorterTag = this->CreateConfigStringFromDICOMDatasetSorter(doc, specificSorter); root->InsertEndChild(sorterTag); } else { MITK_WARN << "Unknown DICOMDatasetSorter class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize."; return nullptr; } } return root; } tinyxml2::XMLElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromDICOMDatasetSorter(tinyxml2::XMLDocument &doc, const DICOMTagBasedSorter* sorter) const { assert(sorter); auto *sorterTag = doc.NewElement("DICOMDatasetSorter"); sorterTag->SetAttribute("class", sorter->GetNameOfClass()); - sorterTag->SetAttribute("strictSorting", toString(sorter->GetStrictSorting())); - sorterTag->SetAttribute("expectDistanceOne", toString(sorter->GetExpectDistanceOne())); + sorterTag->SetAttribute("strictSorting", sorter->GetStrictSorting()); + sorterTag->SetAttribute("expectDistanceOne", sorter->GetExpectDistanceOne()); auto *distinguishingTagsElement = doc.NewElement("Distinguishing"); sorterTag->InsertEndChild(distinguishingTagsElement); mitk::DICOMTagList distinguishingTags = sorter->GetDistinguishingTags(); for (auto tagIter = distinguishingTags.begin(); tagIter != distinguishingTags.end(); ++tagIter) { auto* tag = this->CreateConfigStringFromDICOMTag(doc, *tagIter); distinguishingTagsElement->InsertEndChild(tag); const DICOMTagBasedSorter::TagValueProcessor* processor = sorter->GetTagValueProcessorForDistinguishingTag(*tagIter); if (const auto* specificProcessor = dynamic_cast(processor)) { tag->SetAttribute("cutDecimalPlaces", specificProcessor->GetPrecision()); } } auto *sortingElement = doc.NewElement("Sorting"); sorterTag->InsertEndChild(sortingElement); mitk::DICOMSortCriterion::ConstPointer sortCriterion = sorter->GetSortCriterion(); while (sortCriterion.IsNotNull()) { std::string classname = sortCriterion->GetNameOfClass(); if (classname == "SortByImagePositionPatient") { sortingElement->InsertEndChild( doc.NewElement("ImagePositionPatient") ); // no parameters } else if (classname == "DICOMSortByTag") { DICOMTagList pseudoTagList = sortCriterion->GetTagsOfInterest(); if (pseudoTagList.size() == 1) { DICOMTag firstTag = pseudoTagList.front(); auto* tagElement = this->CreateConfigStringFromDICOMTag(doc, firstTag); sortingElement->InsertEndChild( tagElement ); } else { MITK_ERROR << "Encountered SortByTag class with MULTIPLE tag in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize."; return nullptr; } } else { MITK_ERROR << "Encountered unknown class '" << classname << "' in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize."; return nullptr; } sortCriterion = sortCriterion->GetSecondaryCriterion(); } return sorterTag; } tinyxml2::XMLElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromDICOMTag(tinyxml2::XMLDocument& doc, const DICOMTag& tag) const { auto tagElement = doc.NewElement("Tag"); // name group element tagElement->SetAttribute("name", tag.GetName().c_str()); tagElement->SetAttribute("group", toHexString(tag.GetGroup()).c_str()); tagElement->SetAttribute("element", toHexString(tag.GetElement()).c_str()); return tagElement; } std::string mitk::DICOMReaderConfigurator ::toHexString(unsigned int i) const { std::stringstream ss; ss << "0x" << std::setfill ('0') << std::setw(4) << std::hex << i; return ss.str(); } tinyxml2::XMLElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(tinyxml2::XMLDocument& doc, const ThreeDnTDICOMSeriesReader* reader) const { auto* root = this->CreateConfigStringFromReader(doc, static_cast(reader)); assert(root); - root->SetAttribute("group3DnT", toString(reader->GetGroup3DandT())); + root->SetAttribute("group3DnT", reader->GetGroup3DandT()); return root; } -const char* -mitk::DICOMReaderConfigurator -::toString(bool b) const -{ - return b ? "true" : "false"; -} - tinyxml2::XMLElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(tinyxml2::XMLDocument& doc, const ClassicDICOMSeriesReader* reader) const { return this->CreateDICOMFileReaderTag(doc, reader); } tinyxml2::XMLElement* mitk::DICOMReaderConfigurator ::CreateDICOMFileReaderTag(tinyxml2::XMLDocument& doc, const DICOMFileReader* reader) const { auto readerTag = doc.NewElement("DICOMFileReader"); readerTag->SetAttribute("class", reader->GetNameOfClass()); readerTag->SetAttribute("label", reader->GetConfigurationLabel().c_str()); readerTag->SetAttribute("description", reader->GetConfigurationDescription().c_str()); readerTag->SetAttribute("version", "1"); return readerTag; } diff --git a/Modules/SceneSerializationBase/src/mitkBoolLookupTablePropertySerializer.cpp b/Modules/SceneSerializationBase/src/mitkBoolLookupTablePropertySerializer.cpp index b5315ab1aa..eb60909922 100644 --- a/Modules/SceneSerializationBase/src/mitkBoolLookupTablePropertySerializer.cpp +++ b/Modules/SceneSerializationBase/src/mitkBoolLookupTablePropertySerializer.cpp @@ -1,81 +1,78 @@ /*============================================================================ 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 mitkBoolLookupTablePropertySerializer_h_included #define mitkBoolLookupTablePropertySerializer_h_included #include "mitkBasePropertySerializer.h" #include "mitkProperties.h" #include namespace mitk { class BoolLookupTablePropertySerializer : public BasePropertySerializer { public: mitkClassMacro(BoolLookupTablePropertySerializer, BasePropertySerializer) itkFactorylessNewMacro(Self) itkCloneMacro(Self) tinyxml2::XMLElement *Serialize(tinyxml2::XMLDocument &doc) override { const BoolLookupTableProperty *prop = dynamic_cast(m_Property.GetPointer()); if (prop == nullptr) return nullptr; BoolLookupTable lut = prop->GetValue(); // if (lut.IsNull()) // return nullptr; // really? const BoolLookupTable::LookupTableType &map = lut.GetLookupTable(); auto *element = doc.NewElement("BoolLookupTable"); for (auto it = map.begin(); it != map.end(); ++it) { auto *tableEntry = doc.NewElement("LUTValue"); tableEntry->SetAttribute("id", it->first); - if (it->second == true) - tableEntry->SetAttribute("value", "true"); - else - tableEntry->SetAttribute("value", "false"); + tableEntry->SetAttribute("value", it->second); element->InsertEndChild(tableEntry); } return element; } BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *element) override { if (!element) return nullptr; BoolLookupTable lut; for (auto *child = element->FirstChildElement("LUTValue"); child != nullptr; child = child->NextSiblingElement("LUTValue")) { int xmlID; if (child->QueryIntAttribute("id", &xmlID) != tinyxml2::XML_SUCCESS) return nullptr; // TODO: can we do a better error handling? BoolLookupTable::IdentifierType id = static_cast(xmlID); BoolLookupTable::ValueType val = std::string(child->Attribute("value")) == std::string("true"); lut.SetTableValue(id, val); } return BoolLookupTableProperty::New(lut).GetPointer(); } protected: BoolLookupTablePropertySerializer() {} ~BoolLookupTablePropertySerializer() override {} }; } // namespace // important to put this into the GLOBAL namespace (because it starts with 'namespace mitk') MITK_REGISTER_SERIALIZER(BoolLookupTablePropertySerializer); #endif diff --git a/Modules/SceneSerializationBase/src/mitkBoolPropertySerializer.cpp b/Modules/SceneSerializationBase/src/mitkBoolPropertySerializer.cpp index 136a0d4464..f8e1fc5b32 100644 --- a/Modules/SceneSerializationBase/src/mitkBoolPropertySerializer.cpp +++ b/Modules/SceneSerializationBase/src/mitkBoolPropertySerializer.cpp @@ -1,65 +1,58 @@ /*============================================================================ 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 mitkBoolPropertySerializer_h_included #define mitkBoolPropertySerializer_h_included #include "mitkBasePropertySerializer.h" #include "mitkProperties.h" #include namespace mitk { class BoolPropertySerializer : public BasePropertySerializer { public: mitkClassMacro(BoolPropertySerializer, BasePropertySerializer) itkFactorylessNewMacro(Self) itkCloneMacro(Self) tinyxml2::XMLElement* Serialize(tinyxml2::XMLDocument& doc) override { if (const BoolProperty *prop = dynamic_cast(m_Property.GetPointer())) { auto element = doc.NewElement("bool"); - if (prop->GetValue() == true) - { - element->SetAttribute("value", "true"); - } - else - { - element->SetAttribute("value", "false"); - } + element->SetAttribute("value", prop->GetValue()); return element; } else return nullptr; } BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *element) override { if (!element) return nullptr; return BoolProperty::New(std::string(element->Attribute("value")) == "true").GetPointer(); } protected: BoolPropertySerializer() {} ~BoolPropertySerializer() override {} }; } // namespace // important to put this into the GLOBAL namespace (because it starts with 'namespace mitk') MITK_REGISTER_SERIALIZER(BoolPropertySerializer); #endif diff --git a/Modules/SceneSerializationBase/src/mitkClippingPropertySerializer.cpp b/Modules/SceneSerializationBase/src/mitkClippingPropertySerializer.cpp index 69eb023b18..8c9f84f962 100644 --- a/Modules/SceneSerializationBase/src/mitkClippingPropertySerializer.cpp +++ b/Modules/SceneSerializationBase/src/mitkClippingPropertySerializer.cpp @@ -1,128 +1,125 @@ /*============================================================================ 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 mitkClippingPropertySerializer_h_included #define mitkClippingPropertySerializer_h_included #include "mitkBasePropertySerializer.h" #include "mitkClippingProperty.h" #include "mitkNumericTypes.h" #include #include "mitkStringsToNumbers.h" #include #include namespace mitk { class ClippingPropertySerializer : public BasePropertySerializer { public: mitkClassMacro(ClippingPropertySerializer, BasePropertySerializer) itkFactorylessNewMacro(Self) itkCloneMacro(Self) tinyxml2::XMLElement* Serialize(tinyxml2::XMLDocument& doc) override { if (const ClippingProperty *prop = dynamic_cast(m_Property.GetPointer())) { LocaleSwitch localeSwitch("C"); auto *element = doc.NewElement("clipping"); - if (prop->GetClippingEnabled()) - element->SetAttribute("enabled", "true"); - else - element->SetAttribute("enabled", "false"); + element->SetAttribute("enabled", prop->GetClippingEnabled()); auto *originElement = doc.NewElement("origin"); const Point3D origin = prop->GetOrigin(); originElement->SetAttribute("x", boost::lexical_cast(origin[0]).c_str()); originElement->SetAttribute("y", boost::lexical_cast(origin[1]).c_str()); originElement->SetAttribute("z", boost::lexical_cast(origin[2]).c_str()); element->InsertEndChild(originElement); auto *normalElement = doc.NewElement("normal"); const Vector3D normal = prop->GetNormal(); normalElement->SetAttribute("x", boost::lexical_cast(normal[0]).c_str()); normalElement->SetAttribute("y", boost::lexical_cast(normal[1]).c_str()); normalElement->SetAttribute("z", boost::lexical_cast(normal[2]).c_str()); element->InsertEndChild(normalElement); return element; } else return nullptr; } BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *element) override { if (!element) return nullptr; LocaleSwitch localeSwitch("C"); bool enabled = std::string(element->Attribute("enabled")) == "true"; auto *originElement = element->FirstChildElement("origin"); if (originElement == nullptr) return nullptr; std::array origin_string = { originElement->Attribute("x"), originElement->Attribute("y"), originElement->Attribute("z") }; if (nullptr == origin_string[0] || nullptr == origin_string[1] || nullptr == origin_string[2]) return nullptr; Point3D origin; try { StringsToNumbers(3, origin_string, origin); } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse string as number: " << e.what(); return nullptr; } auto *normalElement = element->FirstChildElement("normal"); if (normalElement == nullptr) return nullptr; std::array normal_string = { normalElement->Attribute("x"), normalElement->Attribute("y"), normalElement->Attribute("z") }; if (nullptr == normal_string[0] || nullptr == normal_string[1] || nullptr == normal_string[2]) return nullptr; Vector3D normal; try { StringsToNumbers(3, normal_string, normal); } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse string as number: " << e.what(); return nullptr; } ClippingProperty::Pointer cp = ClippingProperty::New(origin, normal); cp->SetClippingEnabled(enabled); return cp.GetPointer(); } protected: ClippingPropertySerializer() {} ~ClippingPropertySerializer() override {} }; } // namespace // important to put this into the GLOBAL namespace (because it starts with 'namespace mitk') MITK_REGISTER_SERIALIZER(ClippingPropertySerializer); #endif diff --git a/Modules/SceneSerializationBase/src/mitkLevelWindowPropertySerializer.cpp b/Modules/SceneSerializationBase/src/mitkLevelWindowPropertySerializer.cpp index b4ac823d40..aef921409c 100644 --- a/Modules/SceneSerializationBase/src/mitkLevelWindowPropertySerializer.cpp +++ b/Modules/SceneSerializationBase/src/mitkLevelWindowPropertySerializer.cpp @@ -1,131 +1,124 @@ /*============================================================================ 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 mitkLevelWindowPropertySerializer_h_included #define mitkLevelWindowPropertySerializer_h_included #include "mitkBasePropertySerializer.h" #include "mitkLevelWindowProperty.h" #include #include #include namespace mitk { class LevelWindowPropertySerializer : public BasePropertySerializer { public: mitkClassMacro(LevelWindowPropertySerializer, BasePropertySerializer) itkFactorylessNewMacro(Self) itkCloneMacro(Self) tinyxml2::XMLElement *Serialize(tinyxml2::XMLDocument &doc) override { if (const LevelWindowProperty *prop = dynamic_cast(m_Property.GetPointer())) { LocaleSwitch localeSwitch("C"); auto *element = doc.NewElement("LevelWindow"); LevelWindow lw = prop->GetLevelWindow(); - std::string boolString("false"); - if (lw.IsFixed() == true) - boolString = "true"; - element->SetAttribute("fixed", boolString.c_str()); - - std::string boolStringFltImage("false"); - if (lw.IsFloatingValues() == true) - boolStringFltImage = "true"; - element->SetAttribute("isFloatingImage", boolStringFltImage.c_str()); + element->SetAttribute("fixed", lw.IsFixed()); + element->SetAttribute("isFloatingImage", lw.IsFloatingValues()); auto *child = doc.NewElement("CurrentSettings"); element->InsertEndChild(child); child->SetAttribute("level", boost::lexical_cast(lw.GetLevel()).c_str()); child->SetAttribute("window", boost::lexical_cast(lw.GetWindow()).c_str()); child = doc.NewElement("DefaultSettings"); element->InsertEndChild(child); child->SetAttribute("level", boost::lexical_cast(lw.GetDefaultLevel()).c_str()); child->SetAttribute("window", boost::lexical_cast(lw.GetDefaultWindow()).c_str()); child = doc.NewElement("CurrentRange"); element->InsertEndChild(child); child->SetAttribute("min", boost::lexical_cast(lw.GetRangeMin()).c_str()); child->SetAttribute("max", boost::lexical_cast(lw.GetRangeMax()).c_str()); return element; } else return nullptr; } BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *element) override { if (!element) return nullptr; LocaleSwitch localeSwitch("C"); bool isFixed(false); if (element->Attribute("fixed")) isFixed = std::string(element->Attribute("fixed")) == "true"; bool isFloatingImage(false); if (element->Attribute("isFloatingImage")) isFloatingImage = std::string(element->Attribute("isFloatingImage")) == "true"; auto *child = element->FirstChildElement("CurrentSettings"); const char* level_string = child->Attribute("level"); const char* window_string = child->Attribute("window"); if (nullptr == level_string || nullptr == window_string) return nullptr; child = element->FirstChildElement("DefaultSettings"); const char* defaultLevel_string = child->Attribute("level"); const char* defaultWindow_string = child->Attribute("window"); if (nullptr == defaultLevel_string || nullptr == defaultWindow_string) return nullptr; child = element->FirstChildElement("CurrentRange"); const char* minRange_string = child->Attribute("min"); const char* maxRange_string = child->Attribute("max"); if (nullptr == minRange_string || nullptr == maxRange_string) return nullptr; LevelWindow lw; try { lw.SetRangeMinMax(boost::lexical_cast(minRange_string), boost::lexical_cast(maxRange_string)); lw.SetDefaultLevelWindow(boost::lexical_cast(defaultLevel_string), boost::lexical_cast(defaultWindow_string)); lw.SetLevelWindow(boost::lexical_cast(level_string), boost::lexical_cast(window_string)); lw.SetFixed(isFixed); lw.SetFloatingValues(isFloatingImage); } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse string as number: " << e.what(); return nullptr; } return LevelWindowProperty::New(lw).GetPointer(); } protected: LevelWindowPropertySerializer() {} ~LevelWindowPropertySerializer() override {} }; } // namespace // important to put this into the GLOBAL namespace (because it starts with 'namespace mitk') MITK_REGISTER_SERIALIZER(LevelWindowPropertySerializer); #endif diff --git a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp index d6818af9f1..9edfb8e2d9 100644 --- a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp +++ b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp @@ -1,580 +1,580 @@ /*============================================================================ 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 "mitkTubeGraphIO.h" #include "mitkCircularProfileTubeElement.h" #include "mitkTubeGraphDefinitions.h" #include "mitkTubeGraphProperty.h" #include #include #include #include namespace mitk { TubeGraphIO::TubeGraphIO(const TubeGraphIO &other) : AbstractFileIO(other) {} TubeGraphIO::TubeGraphIO() : AbstractFileIO( mitk::TubeGraph::GetStaticNameOfClass(), mitk::TubeGraphIO::TUBEGRAPH_MIMETYPE(), "Tube Graph Structure File") { this->RegisterService(); } std::vector TubeGraphIO::DoRead() { std::locale::global(std::locale("C")); std::vector> result; InputStream stream(this); std::string string(std::istreambuf_iterator(stream), {}); tinyxml2::XMLDocument doc; if (tinyxml2::XML_SUCCESS == doc.Parse(string.c_str())) { TubeGraph::Pointer newTubeGraph = TubeGraph::New(); tinyxml2::XMLHandle hDoc(&doc); tinyxml2::XMLHandle hRoot = hDoc.FirstChildElement(); tinyxml2::XMLElement *pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_GEOMETRY.c_str()).ToElement(); // read geometry mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->Initialize(); // read origin mitk::Point3D origin; double temp = 0; pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X.c_str(), &temp); origin[0] = temp; pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y.c_str(), &temp); origin[1] = temp; pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z.c_str(), &temp); origin[2] = temp; geometry->SetOrigin(origin); // read spacing Vector3D spacing; pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_SPACING_X.c_str(), &temp); spacing.SetElement(0, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Y.c_str(), &temp); spacing.SetElement(1, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Z.c_str(), &temp); spacing.SetElement(2, temp); geometry->SetSpacing(spacing); // read transform vtkMatrix4x4 *m = vtkMatrix4x4::New(); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX.c_str(), &temp); m->SetElement(0, 0, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY.c_str(), &temp); m->SetElement(1, 0, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ.c_str(), &temp); m->SetElement(2, 0, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX.c_str(), &temp); m->SetElement(0, 1, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY.c_str(), &temp); m->SetElement(1, 1, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ.c_str(), &temp); m->SetElement(2, 1, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX.c_str(), &temp); m->SetElement(0, 2, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY.c_str(), &temp); m->SetElement(1, 2, temp); pElem->QueryAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ.c_str(), &temp); m->SetElement(2, 2, temp); m->SetElement(0, 3, origin[0]); m->SetElement(1, 3, origin[1]); m->SetElement(2, 3, origin[2]); m->SetElement(3, 3, 1); geometry->SetIndexToWorldTransformByVtkMatrix(m); geometry->SetImageGeometry(false); // read tube graph // read vertices pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_VERTICES.c_str()).ToElement(); if (pElem != nullptr) { // walk through the vertices for (auto *vertexElement = pElem->FirstChildElement(); vertexElement != nullptr; vertexElement = vertexElement->NextSiblingElement()) { int vertexID(0); mitk::Point3D coordinate; coordinate.Fill(0.0); double diameter(0); vertexElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID.c_str(), &vertexID); auto *tubeElement = vertexElement->FirstChildElement(); tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X.c_str(), &temp); coordinate[0] = temp; tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y.c_str(), &temp); coordinate[1] = temp; tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z.c_str(), &temp); coordinate[2] = temp; tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER.c_str(), &diameter); mitk::TubeGraphVertex vertexData; auto *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); vertexData.SetTubeElement(newElement); mitk::TubeGraph::VertexDescriptorType newVertex = newTubeGraph->AddVertex(vertexData); if (static_cast(newVertex) != vertexID) { MITK_ERROR << "Aborting tube graph creation, different vertex ids."; return result; ; } } } // read edges pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_EDGES.c_str()).ToElement(); if (pElem != nullptr) { // walk through the edges auto edgeElement = pElem->FirstChildElement(); for ( ; edgeElement != nullptr; edgeElement = edgeElement->NextSiblingElement()) { int edgeID(0), edgeSourceID(0), edgeTargetID(0); mitk::Point3D coordinate; double diameter(0); edgeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_EDGE_ID.c_str(), &edgeID); edgeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID.c_str(), &edgeSourceID); edgeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID.c_str(), &edgeTargetID); mitk::TubeGraphEdge edgeData; for (auto *tubeElement = edgeElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_ELEMENT.c_str()); tubeElement != nullptr; tubeElement = tubeElement->NextSiblingElement()) { tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X.c_str(), &temp); coordinate[0] = temp; tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y.c_str(), &temp); coordinate[1] = temp; tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z.c_str(), &temp); coordinate[2] = temp; tubeElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER.c_str(), &diameter); auto *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); edgeData.AddTubeElement(newElement); } try { newTubeGraph->AddEdge(edgeSourceID, edgeTargetID, edgeData); } catch (const std::runtime_error &error) { MITK_ERROR << error.what(); return result; } } } // Compute bounding box BoundingBox::Pointer bb = this->ComputeBoundingBox(newTubeGraph); geometry->SetBounds(bb->GetBounds()); MITK_INFO << "Tube Graph read"; MITK_INFO << "Edge numb:" << newTubeGraph->GetNumberOfEdges() << " Vertices: " << newTubeGraph->GetNumberOfVertices(); MITK_INFO << "Reading tube graph property"; mitk::TubeGraphProperty::Pointer newProperty = mitk::TubeGraphProperty::New(); // read label groups pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS.c_str()).ToElement(); if (pElem != nullptr) { // walk through the label groups for (auto *labelGroupElement = pElem->FirstChildElement(); labelGroupElement != nullptr; labelGroupElement = labelGroupElement->NextSiblingElement()) { auto *newLabelGroup = new mitk::TubeGraphProperty::LabelGroup(); const char *labelGroupName; labelGroupName = labelGroupElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) newLabelGroup->labelGroupName = labelGroupName; for (auto *labelElement = labelGroupElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_LABEL.c_str()); labelElement != nullptr; labelElement = labelElement->NextSiblingElement()) { auto *newLabel = new mitk::TubeGraphProperty::LabelGroup::Label(); const char *labelName; bool isVisible = true; Color color; labelName = labelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) newLabel->labelName = labelName; labelElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY.c_str(), &temp); if (temp == 0) isVisible = false; else isVisible = true; labelElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R.c_str(), &temp); color[0] = temp; labelElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G.c_str(), &temp); color[1] = temp; labelElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B.c_str(), &temp); color[2] = temp; newLabel->isVisible = isVisible; newLabel->labelColor = color; newLabelGroup->labels.push_back(newLabel); } newProperty->AddLabelGroup(newLabelGroup, newProperty->GetLabelGroups().size()); } } // read attributations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS.c_str()).ToElement(); if (pElem != nullptr) { std::map tubeToLabelsMap; for (auto *tubeToLabelElement = pElem->FirstChildElement(); tubeToLabelElement != nullptr; tubeToLabelElement = tubeToLabelElement->NextSiblingElement()) { TubeGraph::TubeDescriptorType tube; auto *labelGroup = new mitk::TubeGraphProperty::LabelGroup(); auto *label = new mitk::TubeGraphProperty::LabelGroup::Label(); tubeToLabelElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1.c_str(), &temp); tube.first = temp; tubeToLabelElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2.c_str(), &temp); tube.second = temp; const char *labelGroupName = tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) labelGroup = newProperty->GetLabelGroupByName(labelGroupName); const char *labelName = tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) label = newProperty->GetLabelByName(labelGroup, labelName); if (tube != TubeGraph::ErrorId && labelGroup != nullptr && label != nullptr) { TubeGraphProperty::TubeToLabelGroupType tubeToLabelGroup(tube, labelGroupName); tubeToLabelsMap.insert( std::pair(tubeToLabelGroup, labelName)); } } if (tubeToLabelsMap.size() > 0) newProperty->SetTubesToLabels(tubeToLabelsMap); } // read annotations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS.c_str()).ToElement(); if (pElem != nullptr) { for (auto *annotationElement = pElem->FirstChildElement(); annotationElement != nullptr; annotationElement = annotationElement->NextSiblingElement()) { auto *annotation = new mitk::TubeGraphProperty::Annotation(); TubeGraph::TubeDescriptorType tube; const char *annotationName = annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME.c_str()); annotation->name = nullptr != annotationName ? annotationName : ""; const char *annotationDescription = annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION.c_str()); annotation->description = nullptr != annotationDescription ? annotationDescription : ""; annotationElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1.c_str(), &temp); tube.first = temp; annotationElement->QueryAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2.c_str(), &temp); tube.second = temp; if (tube != TubeGraph::ErrorId) { annotation->tube = tube; newProperty->AddAnnotation(annotation); } } } MITK_INFO << "Tube Graph Property read"; newTubeGraph->SetGeometry(geometry); newTubeGraph->SetProperty("Tube Graph.Visualization Information", newProperty); result.push_back(newTubeGraph.GetPointer()); } else { mitkThrow() << "Parsing error at line " << doc.ErrorLineNum() << ": " << doc.ErrorStr(); } return result; } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; return Supported; } void TubeGraphIO::Write() { OutputStream out(this); if (!out.good()) { mitkThrow() << "Stream not good."; } std::locale previousLocale(out.getloc()); std::locale I("C"); out.imbue(I); const auto *tubeGraph = dynamic_cast(this->GetInput()); // Get geometry of the tube graph mitk::Geometry3D::Pointer geometry = dynamic_cast(tubeGraph->GetGeometry()); // Get property of the tube graph mitk::TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast( tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); // Create XML document tinyxml2::XMLDocument documentXML; { // Begin document documentXML.InsertEndChild(documentXML.NewDeclaration()); auto *mainXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_TUBEGRAPH_FILE.c_str()); mainXML->SetAttribute(mitk::TubeGraphDefinitions::XML_FILE_VERSION.c_str(), mitk::TubeGraphDefinitions::VERSION_STRING.c_str()); documentXML.InsertEndChild(mainXML); auto *geometryXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_GEOMETRY.c_str()); { // begin geometry geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX.c_str(), geometry->GetMatrixColumn(0)[0]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY.c_str(), geometry->GetMatrixColumn(0)[1]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ.c_str(), geometry->GetMatrixColumn(0)[2]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX.c_str(), geometry->GetMatrixColumn(1)[0]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY.c_str(), geometry->GetMatrixColumn(1)[1]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ.c_str(), geometry->GetMatrixColumn(1)[2]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX.c_str(), geometry->GetMatrixColumn(2)[0]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY.c_str(), geometry->GetMatrixColumn(2)[1]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ.c_str(), geometry->GetMatrixColumn(2)[2]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X.c_str(), geometry->GetOrigin()[0]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y.c_str(), geometry->GetOrigin()[1]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z.c_str(), geometry->GetOrigin()[2]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_SPACING_X.c_str(), geometry->GetSpacing()[0]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Y.c_str(), geometry->GetSpacing()[1]); geometryXML->SetAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Z.c_str(), geometry->GetSpacing()[2]); } // end geometry mainXML->InsertEndChild(geometryXML); auto *verticesXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_VERTICES.c_str()); { // begin vertices section std::vector vertexVector = tubeGraph->GetVectorOfAllVertices(); for (unsigned int index = 0; index < vertexVector.size(); index++) { auto *vertexXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_VERTEX.c_str()); vertexXML->SetAttribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID.c_str(), static_cast(tubeGraph->GetVertexDescriptor(vertexVector[index]))); // element of each vertex const mitk::TubeElement *element = vertexVector[index].GetTubeElement(); auto *elementXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_ELEMENT.c_str()); elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X.c_str(), element->GetCoordinates().GetElement(0)); elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y.c_str(), element->GetCoordinates().GetElement(1)); elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z.c_str(), element->GetCoordinates().GetElement(2)); if (dynamic_cast(element)) elementXML->SetAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER.c_str(), (dynamic_cast(element))->GetDiameter()); else elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER.c_str(), 2); vertexXML->InsertEndChild(elementXML); verticesXML->InsertEndChild(vertexXML); } } // end vertices section mainXML->InsertEndChild(verticesXML); auto *edgesXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_EDGES.c_str()); { // begin edges section std::vector edgeVector = tubeGraph->GetVectorOfAllEdges(); for (unsigned int index = 0; index < edgeVector.size(); index++) { auto *edgeXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_EDGE.c_str()); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_ID.c_str(), index); std::pair soureTargetPair = tubeGraph->GetVerticesOfAnEdge(tubeGraph->GetEdgeDescriptor(edgeVector[index])); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID.c_str(), static_cast(tubeGraph->GetVertexDescriptor(soureTargetPair.first))); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID.c_str(), static_cast(tubeGraph->GetVertexDescriptor(soureTargetPair.second))); // begin elements of the edge std::vector elementVector = edgeVector[index].GetElementVector(); for (unsigned int elementIndex = 0; elementIndex < elementVector.size(); elementIndex++) { auto *elementXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_ELEMENT.c_str()); elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X.c_str(), elementVector[elementIndex]->GetCoordinates().GetElement(0)); elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y.c_str(), elementVector[elementIndex]->GetCoordinates().GetElement(1)); elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z.c_str(), elementVector[elementIndex]->GetCoordinates().GetElement(2)); if (dynamic_cast(elementVector[elementIndex])) elementXML->SetAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER.c_str(), (dynamic_cast(elementVector[elementIndex]))->GetDiameter()); else elementXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER.c_str(), 2); edgeXML->InsertEndChild(elementXML); // elementsXML->InsertEndChild(elementXML); } edgesXML->InsertEndChild(edgeXML); } } // end edges section mainXML->InsertEndChild(edgesXML); auto *labelGroupsXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS.c_str()); { // begin label group section std::vector labelGroupVector = tubeGraphProperty->GetLabelGroups(); for (unsigned int index = 0; index < labelGroupVector.size(); index++) { auto *labelGroupXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_LABELGROUP.c_str()); labelGroupXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str(), labelGroupVector[index]->labelGroupName.c_str()); // begin labels of the label group std::vector labelVector = labelGroupVector[index]->labels; for (unsigned int labelIndex = 0; labelIndex < labelVector.size(); labelIndex++) { auto *labelXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_LABEL.c_str()); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str(), labelVector[labelIndex]->labelName.c_str()); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY.c_str(), - labelVector[labelIndex]->isVisible); + labelVector[labelIndex]->isVisible ? 1 : 0); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R.c_str(), labelVector[labelIndex]->labelColor[0]); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G.c_str(), labelVector[labelIndex]->labelColor[1]); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B.c_str(), labelVector[labelIndex]->labelColor[2]); labelGroupXML->InsertEndChild(labelXML); } labelGroupsXML->InsertEndChild(labelGroupXML); } } // end labe group section mainXML->InsertEndChild(labelGroupsXML); auto *attributionsXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS.c_str()); { // begin attributions section std::map tubeToLabelGroup = tubeGraphProperty->GetTubesToLabels(); for (auto it = tubeToLabelGroup.begin(); it != tubeToLabelGroup.end(); it++) { auto *attributXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTION.c_str()); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1.c_str(), static_cast(it->first.first.first)); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2.c_str(), static_cast(it->first.first.second)); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str(), it->first.second.c_str()); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str(), it->second.c_str()); attributionsXML->InsertEndChild(attributXML); } } // end attributions section mainXML->InsertEndChild(attributionsXML); auto *annotationsXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS.c_str()); { // begin annotations section std::vector annotations = tubeGraphProperty->GetAnnotations(); for (unsigned int index = 0; index < annotations.size(); index++) { auto *annotationXML = documentXML.NewElement(mitk::TubeGraphDefinitions::XML_ANNOTATION.c_str()); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME.c_str(), annotations[index]->name.c_str()); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION.c_str(), annotations[index]->description.c_str()); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1.c_str(), static_cast(annotations[index]->tube.first)); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2.c_str(), static_cast(annotations[index]->tube.second)); annotationsXML->InsertEndChild(annotationXML); } } // end annotations section mainXML->InsertEndChild(annotationsXML); } // end document tinyxml2::XMLPrinter printer; documentXML.Print(&printer); out << printer.CStr(); } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; return Supported; } TubeGraphIO *TubeGraphIO::IOClone() const { return new TubeGraphIO(*this); } } const mitk::BoundingBox::Pointer mitk::TubeGraphIO::ComputeBoundingBox(mitk::TubeGraph::Pointer graph) const { BoundingBox::Pointer boundingBox = BoundingBox::New(); BoundingBox::PointIdentifier pointid = 0; BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); ScalarType nullpoint[] = {0, 0, 0}; BoundingBox::PointType p(nullpoint); // traverse the tree and add each point to the pointscontainer mitk::Point3D pos; std::vector vertexVector = graph->GetVectorOfAllVertices(); for (auto vertex = vertexVector.begin(); vertex != vertexVector.end(); ++vertex) { pos = vertex->GetTubeElement()->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } std::vector edgeVector = graph->GetVectorOfAllEdges(); for (auto edge = edgeVector.begin(); edge != edgeVector.end(); ++edge) { std::vector allElements = edge->GetElementVector(); for (unsigned int index = 0; index < edge->GetNumberOfElements(); index++) { pos = allElements[index]->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } } boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); return boundingBox; } diff --git a/Modules/US/USModel/mitkUSDeviceWriterXML.cpp b/Modules/US/USModel/mitkUSDeviceWriterXML.cpp index 81a0ac8280..6d56cc163c 100644 --- a/Modules/US/USModel/mitkUSDeviceWriterXML.cpp +++ b/Modules/US/USModel/mitkUSDeviceWriterXML.cpp @@ -1,160 +1,157 @@ /*============================================================================ 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. ============================================================================*/ // MITK #include "mitkUSDeviceReaderWriterConstants.h" #include "mitkUSDeviceWriterXML.h" #include #include #include // Third Party #include #include #include #include mitk::USDeviceWriterXML::USDeviceWriterXML() : AbstractFileWriter(USDevice::GetStaticNameOfClass(), mitk::IGTMimeTypes::USDEVICEINFORMATIONXML_MIMETYPE(), "MITK USDevice Writer (XML)"), m_Filename("") { RegisterService(); } mitk::USDeviceWriterXML::USDeviceWriterXML(const mitk::USDeviceWriterXML& other) : AbstractFileWriter(other) { } mitk::USDeviceWriterXML::~USDeviceWriterXML() { } mitk::USDeviceWriterXML* mitk::USDeviceWriterXML::Clone() const { return new USDeviceWriterXML(*this); } void mitk::USDeviceWriterXML::Write() { if (m_Filename == "") { MITK_WARN << "Cannot write to file - empty filename!"; return; } } void mitk::USDeviceWriterXML::SetFilename(std::string filename) { m_Filename = filename; } bool mitk::USDeviceWriterXML::WriteUltrasoundDeviceConfiguration(mitk::USDeviceReaderXML::USDeviceConfigData & config) { tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); //Create the xml information of the ULTRASOUNDDEVICE-Tag: auto *ultrasoundDeviceTag = document.NewElement(USDeviceReaderWriterConstants::TAG_ULTRASOUNDDEVICE); this->CreateXmlInformationOfUltrasoundDeviceTag(document, ultrasoundDeviceTag, config); //Create the xml information of the GENERALSETTINGS-Tag: auto *generalSettingsTag = document.NewElement(USDeviceReaderWriterConstants::TAG_GENERALSETTINGS); this->CreateXmlInformationOfGeneralSettingsTag(ultrasoundDeviceTag, generalSettingsTag, config); //Create the xml information of the PROBES-Tag: this->CreateXmlInformationOfProbesTag(ultrasoundDeviceTag, config); return document.SaveFile(m_Filename.c_str()) == tinyxml2::XML_SUCCESS; } void mitk::USDeviceWriterXML::CreateXmlInformationOfUltrasoundDeviceTag( tinyxml2::XMLDocument &document, tinyxml2::XMLElement* ultrasoundDeviceTag, mitk::USDeviceReaderXML::USDeviceConfigData &config) { ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_FILEVERS, config.fileversion); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_TYPE, config.deviceType.c_str()); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_NAME, config.deviceName.c_str()); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_MANUFACTURER, config.manufacturer.c_str()); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_MODEL, config.model.c_str()); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_COMMENT, config.comment.c_str()); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_IMAGESTREAMS, config.numberOfImageStreams); if (config.deviceType.compare("oigtl") == 0) { ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_HOST, config.host.c_str()); ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_PORT, config.port); - std::string value = config.server ? "true" : "false"; - ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_SERVER, value.c_str()); + ultrasoundDeviceTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_SERVER, config.server); } document.InsertEndChild(ultrasoundDeviceTag); } void mitk::USDeviceWriterXML::CreateXmlInformationOfGeneralSettingsTag(tinyxml2::XMLElement *parentTag, tinyxml2::XMLElement *generalSettingsTag, mitk::USDeviceReaderXML::USDeviceConfigData & config) { - std::string value = config.useGreyscale ? "true" : "false"; - generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_GREYSCALE, value.c_str()); - value = config.useResolutionOverride ? "true" : "false"; - generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_RESOLUTIONOVERRIDE, value.c_str()); + generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_GREYSCALE, config.useGreyscale); + generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_RESOLUTIONOVERRIDE, config.useResolutionOverride); generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_RESOLUTIONWIDTH, config.resolutionWidth); generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_RESOLUTIONHEIGHT, config.resolutionHeight); generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_SOURCEID, config.sourceID); generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_FILEPATH, config.filepathVideoSource.c_str()); generalSettingsTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_OPENCVPORT, config.opencvPort); parentTag->InsertEndChild(generalSettingsTag); } void mitk::USDeviceWriterXML::CreateXmlInformationOfProbesTag(tinyxml2::XMLElement *parentTag, mitk::USDeviceReaderXML::USDeviceConfigData & config) { if (config.probes.size() != 0) { auto* doc = parentTag->GetDocument(); auto *probesTag = doc->NewElement(USDeviceReaderWriterConstants::TAG_PROBES); parentTag->InsertEndChild(probesTag); for (size_t index = 0; index < config.probes.size(); ++index) { auto *probeTag = doc->NewElement(USDeviceReaderWriterConstants::TAG_PROBE); probesTag->InsertEndChild(probeTag); mitk::USProbe::Pointer probe = config.probes.at(index); probeTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_NAME, probe->GetName().c_str()); std::map depthsAndSpacing = probe->GetDepthsAndSpacing(); if (depthsAndSpacing.size() != 0) { auto *depthsTag = doc->NewElement(USDeviceReaderWriterConstants::TAG_DEPTHS); probeTag->InsertEndChild(depthsTag); for (std::map::iterator it = depthsAndSpacing.begin(); it != depthsAndSpacing.end(); it++) { auto *depthTag = doc->NewElement(USDeviceReaderWriterConstants::TAG_DEPTH); depthTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_DEPTH, it->first); depthsTag->InsertEndChild(depthTag); auto *spacingTag = doc->NewElement(USDeviceReaderWriterConstants::TAG_SPACING); spacingTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_X, it->second[0]); spacingTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_Y, it->second[1]); depthTag->InsertEndChild(spacingTag); } auto *croppingTag = doc->NewElement(USDeviceReaderWriterConstants::TAG_CROPPING); probeTag->InsertEndChild(croppingTag); croppingTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_TOP, probe->GetProbeCropping().top); croppingTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_BOTTOM, probe->GetProbeCropping().bottom); croppingTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_LEFT, probe->GetProbeCropping().left); croppingTag->SetAttribute(USDeviceReaderWriterConstants::ATTR_RIGHT, probe->GetProbeCropping().right); } } } }