diff --git a/Modules/ContourModel/IO/mitkContourModelReader.cpp b/Modules/ContourModel/IO/mitkContourModelReader.cpp index df621cb530..058f9c0e99 100644 --- a/Modules/ContourModel/IO/mitkContourModelReader.cpp +++ b/Modules/ContourModel/IO/mitkContourModelReader.cpp @@ -1,157 +1,178 @@ /*============================================================================ 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 "mitkContourModelReader.h" #include #include +#include #include #include #include +namespace +{ + // Previous versions of the ContourModelSetWriter produced flawed + // XML files with multiple XML declarations. + std::string RemoveErroneousXMLDeclarations(const std::string& filename) + { + std::ifstream file(filename); + file.seekg(0, std::ios_base::end); + auto size = file.tellg(); + std::string string(size, '\0'); + file.seekg(0); + file.read(&string[0], size); + file.close(); + std::regex regex("><\\?xml.+\\?>"); + return std::regex_replace(string, regex, ">"); + } +} + mitk::ContourModelReader::ContourModelReader(const mitk::ContourModelReader &other) : mitk::AbstractFileReader(other) { } mitk::ContourModelReader::ContourModelReader() : AbstractFileReader() { std::string category = "Contour File"; mitk::CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("cnt"); this->SetDescription(category); this->SetMimeType(customMimeType); m_ServiceReg = this->RegisterService(); } mitk::ContourModelReader::~ContourModelReader() { } std::vector> mitk::ContourModelReader::DoRead() { std::vector> result; std::string location = GetInputLocation(); // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); try { + auto string = RemoveErroneousXMLDeclarations(location); + tinyxml2::XMLDocument doc; - if (tinyxml2::XML_SUCCESS == doc.LoadFile(location.c_str())) + if (tinyxml2::XML_SUCCESS == doc.Parse(string.c_str())) { tinyxml2::XMLHandle docHandle(&doc); /*++++ handle n contourModels within data tags ++++*/ for (auto *currentContourElement = docHandle.FirstChildElement("contourModel").ToElement(); currentContourElement != nullptr; currentContourElement = currentContourElement->NextSiblingElement()) { mitk::ContourModel::Pointer newContourModel = mitk::ContourModel::New(); if (currentContourElement->FirstChildElement("data")->FirstChildElement("timestep") != nullptr) { // handle geometry information // TiXmlElement* currentGeometryInfo = // currentContourElement->FirstChildElement("head")->FirstChildElement("geometryInformation")->ToElement(); ///////////// NOT SUPPORTED YET //////////////// /*++++ handle n timesteps within timestep tags ++++*/ for (auto *currentTimeSeries = currentContourElement->FirstChildElement("data")->FirstChildElement("timestep")->ToElement(); currentTimeSeries != nullptr; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); currentTimeStep = atoi(currentTimeSeries->Attribute("n")); this->ReadPoints(newContourModel, currentTimeSeries, currentTimeStep); int isClosed; currentTimeSeries->QueryIntAttribute("isClosed", &isClosed); if (isClosed) { newContourModel->Close(currentTimeStep); } } /*++++ END handle n timesteps within timestep tags ++++*/ } else { // this should not happen MITK_WARN << "wrong file format!"; // newContourModel = this->ReadPoint(newContourModel, currentContourElement, 0); } newContourModel->UpdateOutputInformation(); result.push_back(dynamic_cast(newContourModel.GetPointer())); } /*++++ END handle n contourModels within data tags ++++*/ } else { MITK_WARN << "XML parser error!"; } } catch (...) { MITK_ERROR << "Cannot read contourModel."; } return result; } mitk::ContourModelReader *mitk::ContourModelReader::Clone() const { return new ContourModelReader(*this); } void mitk::ContourModelReader::ReadPoints(mitk::ContourModel::Pointer newContourModel, const tinyxml2::XMLElement *currentTimeSeries, unsigned int currentTimeStep) { // check if the timesteps in contourModel have to be expanded if (currentTimeStep != newContourModel->GetTimeSteps()) { newContourModel->Expand(currentTimeStep + 1); } // read all points within controlPoints tag if (currentTimeSeries->FirstChildElement("controlPoints")->FirstChildElement("point") != nullptr) { for (auto *currentPoint = currentTimeSeries->FirstChildElement("controlPoints")->FirstChildElement("point")->ToElement(); currentPoint != nullptr; currentPoint = currentPoint->NextSiblingElement()) { double x(0.0); double y(0.0); double z(0.0); x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); int isActivePoint; currentPoint->QueryIntAttribute("isActive", &isActivePoint); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newContourModel->AddVertex(point, isActivePoint, currentTimeStep); } } else { // nothing to read } } diff --git a/Modules/ContourModel/IO/mitkContourModelSetWriter.cpp b/Modules/ContourModel/IO/mitkContourModelSetWriter.cpp index c437dd022b..deb595a399 100644 --- a/Modules/ContourModel/IO/mitkContourModelSetWriter.cpp +++ b/Modules/ContourModel/IO/mitkContourModelSetWriter.cpp @@ -1,82 +1,85 @@ /*============================================================================ 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 "mitkContourModelSetWriter.h" #include "mitkContourModelWriter.h" #include #include #include mitk::ContourModelSetWriter::ContourModelSetWriter() : AbstractFileWriter(ContourModelSet::GetStaticNameOfClass()) { std::string category = "ContourModelSet File"; mitk::CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("cnt_set"); this->SetDescription(category); this->SetMimeType(customMimeType); RegisterService(); } mitk::ContourModelSetWriter::ContourModelSetWriter(const mitk::ContourModelSetWriter &other) : AbstractFileWriter(other) { } mitk::ContourModelSetWriter::~ContourModelSetWriter() { } void mitk::ContourModelSetWriter::Write() { std::ostream *out; std::ofstream outStream; if (this->GetOutputStream()) { out = this->GetOutputStream(); } else { outStream.open(this->GetOutputLocation().c_str()); out = &outStream; } if (!out->good()) { mitkThrow() << "Stream not good."; } + *out << "\n"; + // Use regular ContourModel writer to write each contour of the set to a single file. // Just use a different file extension .cnt_set - mitk::ContourModelWriter writer; + bool writeXMLHeader = false; + mitk::ContourModelWriter writer(writeXMLHeader); mitk::ContourModelSet::ConstPointer contourModelSet = dynamic_cast(this->GetInput()); // // for each contour object set input of writer // for (int i = 0; i < contourModelSet->GetSize(); ++i) { const mitk::ContourModel *contour = contourModelSet->GetContourModelAt(i); writer.SetInput(contour); writer.SetOutputStream(this->GetOutputLocation(), out); writer.Write(); } } mitk::ContourModelSetWriter *mitk::ContourModelSetWriter::Clone() const { return new ContourModelSetWriter(*this); } diff --git a/Modules/ContourModel/IO/mitkContourModelWriter.cpp b/Modules/ContourModel/IO/mitkContourModelWriter.cpp index f41a1d83f4..8e7710a626 100644 --- a/Modules/ContourModel/IO/mitkContourModelWriter.cpp +++ b/Modules/ContourModel/IO/mitkContourModelWriter.cpp @@ -1,335 +1,335 @@ /*============================================================================ 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 "mitkContourModelWriter.h" #include "mitkIOMimeTypes.h" #include "mitkTimeGeometry.h" #include #include #include /* * The xml file will look like: * * * * * * * * * * * * * * * * * * * */ // // Initialization of the xml tags. // const char *mitk::ContourModelWriter::XML_CONTOURMODEL = "contourModel"; const char *mitk::ContourModelWriter::XML_HEAD = "head"; const char *mitk::ContourModelWriter::XML_GEOMETRY_INFO = "geometryInfo"; const char *mitk::ContourModelWriter::XML_DATA = "data"; const char *mitk::ContourModelWriter::XML_TIME_STEP = "timestep"; const char *mitk::ContourModelWriter::XML_CONTROL_POINTS = "controlPoints"; const char *mitk::ContourModelWriter::XML_POINT = "point"; const char *mitk::ContourModelWriter::XML_X = "x"; const char *mitk::ContourModelWriter::XML_Y = "y"; const char *mitk::ContourModelWriter::XML_Z = "z"; -mitk::ContourModelWriter::ContourModelWriter() - : AbstractFileWriter(ContourModel::GetStaticNameOfClass()), m_IndentDepth(0), m_Indent(2) +mitk::ContourModelWriter::ContourModelWriter(bool writeXMLHeader) + : AbstractFileWriter(ContourModel::GetStaticNameOfClass()), m_WriteXMLHeader(writeXMLHeader), m_IndentDepth(0), m_Indent(2) { std::string category = "Contour File"; mitk::CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("cnt"); this->SetDescription(category); this->SetMimeType(customMimeType); RegisterService(); } mitk::ContourModelWriter::ContourModelWriter(const mitk::ContourModelWriter &other) : AbstractFileWriter(other), m_IndentDepth(other.m_IndentDepth), m_Indent(other.m_Indent) { } mitk::ContourModelWriter::~ContourModelWriter() { } void mitk::ContourModelWriter::Write() { std::ostream *out; std::ofstream outStream; if (this->GetOutputStream()) { out = this->GetOutputStream(); } else { outStream.open(this->GetOutputLocation().c_str()); out = &outStream; } if (!out->good()) { mitkThrow() << "Stream not good."; } std::locale previousLocale(out->getloc()); - std::locale I("C"); - out->imbue(I); + out->imbue(std::locale::classic()); /*+++++++++++ Here the actual xml writing begins +++++++++*/ /*++++ ++++*/ - WriteXMLHeader(*out); + if (m_WriteXMLHeader) + WriteXMLHeader(*out); // // for each input object write its xml representation to // the stream // mitk::ContourModel::ConstPointer contourModel = dynamic_cast(this->GetInput()); assert(contourModel.IsNotNull()); WriteXML(contourModel.GetPointer(), *out); out->imbue(previousLocale); if (!out->good()) // some error during output { throw std::ios_base::failure("Some error during contour writing."); } } mitk::ContourModelWriter *mitk::ContourModelWriter::Clone() const { return new ContourModelWriter(*this); } void mitk::ContourModelWriter::WriteXML(const mitk::ContourModel *contourModel, std::ostream &out) { /*++++ ++++*/ WriteStartElement(XML_CONTOURMODEL, out); /*++++ ++++*/ WriteStartElement(XML_HEAD, out); /*++++ ++++*/ WriteStartElement(XML_GEOMETRY_INFO, out); WriteGeometryInformation(contourModel->GetTimeGeometry(), out); /*++++ ++++*/ WriteEndElement(XML_GEOMETRY_INFO, out); /*++++ ++++*/ WriteEndElement(XML_HEAD, out); /*++++ ++++*/ WriteStartElement(XML_DATA, out); unsigned int timecount = contourModel->GetTimeSteps(); for (unsigned int i = 0; i < timecount; i++) { /*++++ ++++*/ std::vector at; at.push_back("n"); std::vector val; val.push_back(ConvertToString(i)); at.push_back("isClosed"); val.push_back(ConvertToString(contourModel->IsClosed())); WriteStartElementWithAttribut(XML_TIME_STEP, at, val, out); /*++++ ++++*/ WriteStartElement(XML_CONTROL_POINTS, out); auto it = contourModel->IteratorBegin(); auto end = contourModel->IteratorEnd(); while (it != end) { mitk::ContourModel::VertexType *v = *it; /*++++ ++++*/ std::vector attr; attr.push_back("IsControlPoint"); std::vector value; value.push_back(ConvertToString(v->IsControlPoint)); WriteStartElementWithAttribut(XML_POINT, attr, value, out); /*++++ ++++*/ WriteStartElement(XML_X, out); WriteCharacterData(ConvertToString(v->Coordinates[0]).c_str(), out); /*++++ ++++*/ WriteEndElement(XML_X, out, false); /*++++ ++++*/ WriteStartElement(XML_Y, out); WriteCharacterData(ConvertToString(v->Coordinates[1]).c_str(), out); /*++++ ++++*/ WriteEndElement(XML_Y, out, false); /*++++ ++++*/ WriteStartElement(XML_Z, out); WriteCharacterData(ConvertToString(v->Coordinates[2]).c_str(), out); /*++++ ++++*/ WriteEndElement(XML_Z, out, false); /*++++ ++++*/ WriteEndElement(XML_POINT, out); it++; } /*++++ ++++*/ WriteEndElement(XML_CONTROL_POINTS, out); /*++++ ++++*/ WriteEndElement(XML_TIME_STEP, out); } /*++++ ++++*/ WriteEndElement(XML_DATA, out); /*++++ ++++*/ WriteEndElement(XML_CONTOURMODEL, out); } void mitk::ContourModelWriter::WriteGeometryInformation(const mitk::TimeGeometry * /*geometry*/, std::ostream &out) { WriteCharacterData("", out); } template std::string mitk::ContourModelWriter::ConvertToString(T value) { std::ostringstream o; std::locale I("C"); o.imbue(I); if (o << value) { return o.str(); } else return "conversion error"; } void mitk::ContourModelWriter::WriteXMLHeader(std::ostream &file) { file << ""; } void mitk::ContourModelWriter::WriteStartElement(const char *const tag, std::ostream &file) { file << std::endl; WriteIndent(file); file << '<' << tag << '>'; m_IndentDepth++; } void mitk::ContourModelWriter::WriteStartElementWithAttribut(const char *const tag, std::vector attributes, std::vector values, std::ostream &file) { file << std::endl; WriteIndent(file); file << '<' << tag; unsigned int attributesSize = attributes.size(); unsigned int valuesSize = values.size(); if (attributesSize == valuesSize) { auto attributesIt = attributes.begin(); auto end = attributes.end(); auto valuesIt = values.begin(); while (attributesIt != end) { file << ' '; WriteCharacterData(*attributesIt, file); file << '=' << '"'; WriteCharacterData(*valuesIt, file); file << '"'; attributesIt++; valuesIt++; } } file << '>'; m_IndentDepth++; } void mitk::ContourModelWriter::WriteEndElement(const char *const tag, std::ostream &file, const bool &indent) { m_IndentDepth--; if (indent) { file << std::endl; WriteIndent(file); } file << '<' << '/' << tag << '>'; } void mitk::ContourModelWriter::WriteCharacterData(const char *const data, std::ostream &file) { file << data; } void mitk::ContourModelWriter::WriteStartElement(std::string &tag, std::ostream &file) { WriteStartElement(tag.c_str(), file); } void mitk::ContourModelWriter::WriteEndElement(std::string &tag, std::ostream &file, const bool &indent) { WriteEndElement(tag.c_str(), file, indent); } void mitk::ContourModelWriter::WriteCharacterData(std::string &data, std::ostream &file) { WriteCharacterData(data.c_str(), file); } void mitk::ContourModelWriter::WriteIndent(std::ostream &file) { std::string spaces(m_IndentDepth * m_Indent, ' '); file << spaces.c_str(); } diff --git a/Modules/ContourModel/IO/mitkContourModelWriter.h b/Modules/ContourModel/IO/mitkContourModelWriter.h index e338c4eee8..3a1b587533 100644 --- a/Modules/ContourModel/IO/mitkContourModelWriter.h +++ b/Modules/ContourModel/IO/mitkContourModelWriter.h @@ -1,169 +1,171 @@ /*============================================================================ 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 _MITK_CONTOURMODEL_WRITER__H_ #define _MITK_CONTOURMODEL_WRITER__H_ #include #include // DEPRECATED #include namespace mitk { /** * @brief XML-based writer for mitk::ContourModels * * XML-based writer for mitk::ContourModels. Multiple ContourModels can be written in * a single XML file by simply setting multiple inputs to the filter. * * The xml file will look like: * * * * * * * * * * * * * * * * * * * * * @ingroup MitkContourModelModule */ class TimeSlicedGeometry; class ContourModelWriter : public mitk::AbstractFileWriter { public: - ContourModelWriter(); + explicit ContourModelWriter(bool writeXMLHeader = true); ~ContourModelWriter() override; using AbstractFileWriter::Write; void Write() override; protected: ContourModelWriter(const ContourModelWriter &other); mitk::ContourModelWriter *Clone() const override; /** * Converts an arbitrary type to a string. The type has to * support the << operator. This works fine at least for integral * data types as float, int, long etc. * @param value the value to convert * @returns the string representation of value */ template std::string ConvertToString(T value); /** * Writes an XML representation of the given point set to * an outstream. The XML-Header an root node is not included! * @param contourModel the point set to be converted to xml * @param out the stream to write to. */ void WriteXML(const mitk::ContourModel *contourModel, std::ostream &out); /** * Writes the geometry information of the TimeGeometry to an outstream. * The root tag is not included. * @param geometry the TimeGeometry of the contour. * @param out the stream to write to. */ void WriteGeometryInformation(const mitk::TimeGeometry *geometry, std::ostream &out); /** * Writes the geometry information of the TimeGeometry to an outstream. * The root tag is not included. * @param geometry the TimeGeometry of the contour. * @param out the stream to write to. * * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(void WriteGeometryInformation(const mitk::TimeSlicedGeometry *geometry, std::ostream &out)); /** * Writes an standard xml header to the given stream. * @param file the stream in which the header is written. */ void WriteXMLHeader(std::ostream &file); /** Write a start element tag */ void WriteStartElement(const char *const tag, std::ostream &file); void WriteStartElementWithAttribut(const char *const tag, std::vector attributes, std::vector values, std::ostream &file); /** * Write an end element tag * End-Elements following character data should pass indent = false. */ void WriteEndElement(const char *const tag, std::ostream &file, const bool &indent = true); /** Write character data inside a tag. */ void WriteCharacterData(const char *const data, std::ostream &file); /** Write a start element tag */ void WriteStartElement(std::string &tag, std::ostream &file); /** Write an end element tag */ void WriteEndElement(std::string &tag, std::ostream &file, const bool &indent = true); /** Write character data inside a tag. */ void WriteCharacterData(std::string &data, std::ostream &file); /** Writes empty spaces to the stream according to m_IndentDepth and m_Indent */ void WriteIndent(std::ostream &file); + bool m_WriteXMLHeader; + unsigned int m_IndentDepth; unsigned int m_Indent; public: static const char *XML_CONTOURMODEL; static const char *XML_HEAD; static const char *XML_GEOMETRY_INFO; static const char *XML_DATA; static const char *XML_TIME_STEP; static const char *XML_CONTROL_POINTS; static const char *XML_POINT; static const char *XML_X; static const char *XML_Y; static const char *XML_Z; }; } #endif