Medical Imaging Interaction Toolkit  2018.4.99-87d68d9f
Medical Imaging Interaction Toolkit
mitkPlanarFigureReader.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
13 #include "mitkPlanarFigureReader.h"
14 
15 #include "mitkPlanarAngle.h"
16 #include "mitkPlanarArrow.h"
17 #include "mitkPlanarBezierCurve.h"
18 #include "mitkPlanarCircle.h"
19 #include "mitkPlanarCross.h"
21 #include "mitkPlanarEllipse.h"
23 #include "mitkPlanarLine.h"
24 #include "mitkPlanarPolygon.h"
25 #include "mitkPlanarRectangle.h"
27 #include "mitkPlaneGeometry.h"
28 
30 #include <mitkLocaleSwitch.h>
31 
32 #include <itksys/SystemTools.hxx>
33 #include <tinyxml.h>
34 
36  : PlanarFigureSource(), FileReader(), m_FileName(""), m_FilePrefix(""), m_FilePattern(""), m_Success(false)
37 {
38  this->SetNumberOfRequiredOutputs(1);
39  this->SetNumberOfIndexedOutputs(1);
40  this->SetNthOutput(0, this->MakeOutput(0));
41 
42  m_CanReadFromMemory = true;
43 
44  // this->Modified();
45  // this->GetOutput()->Modified();
46  // this->GetOutput()->ReleaseData();
47 }
48 
50 {
51 }
52 
54 {
55  mitk::LocaleSwitch localeSwitch("C");
56  m_Success = false;
57  this->SetNumberOfIndexedOutputs(0); // reset all outputs, we add new ones depending on the file content
58 
59  TiXmlDocument document;
60 
61  if (m_ReadFromMemory)
62  {
63  if (m_MemoryBuffer == nullptr || m_MemorySize == 0)
64  {
65  // check
66  itkWarningMacro(<< "Sorry, memory buffer has not been set!");
67  return;
68  }
69  if (m_MemoryBuffer[m_MemorySize - 1] == '\0')
70  {
71  document.Parse(m_MemoryBuffer);
72  }
73  else
74  {
75  auto tmpArray = new char[(int)m_MemorySize + 1];
76  tmpArray[m_MemorySize] = '\0';
77  memcpy(tmpArray, m_MemoryBuffer, m_MemorySize);
78 
79  document.Parse(m_MemoryBuffer);
80 
81  delete[] tmpArray;
82  }
83  }
84  else
85  {
86  if (m_FileName.empty())
87  {
88  itkWarningMacro(<< "Sorry, filename has not been set!");
89  return;
90  }
91  if (this->CanReadFile(m_FileName.c_str()) == false)
92  {
93  itkWarningMacro(<< "Sorry, can't read file " << m_FileName << "!");
94  return;
95  }
96  if (!document.LoadFile(m_FileName))
97  {
98  MITK_ERROR << "Could not open/read/parse " << m_FileName << ". TinyXML reports: '" << document.ErrorDesc()
99  << "'. "
100  << "The error occurred in row " << document.ErrorRow() << ", column " << document.ErrorCol() << ".";
101  return;
102  }
103  }
104 
105  int fileVersion = 1;
106  TiXmlElement *versionObject = document.FirstChildElement("Version");
107  if (versionObject != nullptr)
108  {
109  if (versionObject->QueryIntAttribute("FileVersion", &fileVersion) != TIXML_SUCCESS)
110  {
111  MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl;
112  }
113  }
114  else
115  {
116  MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl;
117  }
118  if (fileVersion !=
119  1) // add file version selection and version specific file parsing here, if newer file versions are created
120  {
121  MITK_WARN << "File version > 1 is not supported by this reader.";
122  return;
123  }
124 
125  /* file version 1 reader code */
126  for (TiXmlElement *pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr;
127  pfElement = pfElement->NextSiblingElement("PlanarFigure"))
128  {
129  std::string type = pfElement->Attribute("type");
130 
131  mitk::PlanarFigure::Pointer planarFigure = nullptr;
132  if (type == "PlanarAngle")
133  {
134  planarFigure = mitk::PlanarAngle::New();
135  }
136  else if (type == "PlanarCircle")
137  {
138  planarFigure = mitk::PlanarCircle::New();
139  }
140  else if (type == "PlanarEllipse")
141  {
142  planarFigure = mitk::PlanarEllipse::New();
143  }
144  else if (type == "PlanarCross")
145  {
146  planarFigure = mitk::PlanarCross::New();
147  }
148  else if (type == "PlanarFourPointAngle")
149  {
150  planarFigure = mitk::PlanarFourPointAngle::New();
151  }
152  else if (type == "PlanarLine")
153  {
154  planarFigure = mitk::PlanarLine::New();
155  }
156  else if (type == "PlanarPolygon")
157  {
158  planarFigure = mitk::PlanarPolygon::New();
159  }
160  else if (type == "PlanarSubdivisionPolygon")
161  {
162  planarFigure = mitk::PlanarSubdivisionPolygon::New();
163  }
164  else if (type == "PlanarRectangle")
165  {
166  planarFigure = mitk::PlanarRectangle::New();
167  }
168  else if (type == "PlanarArrow")
169  {
170  planarFigure = mitk::PlanarArrow::New();
171  }
172  else if (type == "PlanarDoubleEllipse")
173  {
174  planarFigure = mitk::PlanarDoubleEllipse::New();
175  }
176  else if (type == "PlanarBezierCurve")
177  {
178  planarFigure = mitk::PlanarBezierCurve::New();
179  }
180  else
181  {
182  // unknown type
183  MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element.";
184  continue;
185  }
186 
187  // Read properties of the planar figure
188  for (TiXmlElement *propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr;
189  propertyElement = propertyElement->NextSiblingElement("property"))
190  {
191  const char *keya = propertyElement->Attribute("key");
192  const std::string key(keya ? keya : "");
193 
194  const char *typea = propertyElement->Attribute("type");
195  const std::string type(typea ? typea : "");
196 
197  // hand propertyElement to specific reader
198  std::stringstream propertyDeserializerClassName;
199  propertyDeserializerClassName << type << "Serializer";
200 
201  const std::list<itk::LightObject::Pointer> readers =
202  itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str());
203  if (readers.size() < 1)
204  {
205  MITK_ERROR << "No property reader found for " << type;
206  }
207  if (readers.size() > 1)
208  {
209  MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one.";
210  }
211 
212  for (auto iter = readers.cbegin(); iter != readers.cend(); ++iter)
213  {
214  if (auto *reader = dynamic_cast<BasePropertySerializer *>(iter->GetPointer()))
215  {
216  const BaseProperty::Pointer property = reader->Deserialize(propertyElement->FirstChildElement());
217  if (property.IsNotNull())
218  {
219  planarFigure->GetPropertyList()->ReplaceProperty(key, property);
220  }
221  else
222  {
223  MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type
224  << ". Your data may be corrupted";
225  }
226  break;
227  }
228  }
229  }
230 
231  // If we load a planarFigure, it has definitely been placed correctly.
232  // If we do not set this property here, we cannot load old planarFigures
233  // without messing up the interaction (PF-Interactor needs this property.
234  planarFigure->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
235 
236  // Which features (length or circumference etc) a figure has is decided by whether it is closed or not
237  // the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature
238  auto *planarPolygon = dynamic_cast<PlanarPolygon *>(planarFigure.GetPointer());
239  if (planarPolygon != nullptr)
240  {
241  bool isClosed = false;
242  planarFigure->GetPropertyList()->GetBoolProperty("closed", isClosed);
243  planarPolygon->SetClosed(isClosed);
244  }
245 
246  // Read geometry of containing plane
247  TiXmlElement *geoElement = pfElement->FirstChildElement("Geometry");
248  if (geoElement != nullptr)
249  {
250  try
251  {
252  // Create plane geometry
254 
255  // Extract and set plane transform parameters
256  const DoubleList transformList =
257  this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("transformParam"), "param", 12);
258 
259  typedef mitk::BaseGeometry::TransformType TransformType;
260  TransformType::ParametersType parameters;
261  parameters.SetSize(12);
262 
263  unsigned int i;
264  DoubleList::const_iterator it;
265  for (it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i)
266  {
267  parameters.SetElement(i, *it);
268  }
269 
270  typedef mitk::BaseGeometry::TransformType TransformType;
271  TransformType::Pointer affineGeometry = TransformType::New();
272  affineGeometry->SetParameters(parameters);
273  planeGeo->SetIndexToWorldTransform(affineGeometry);
274 
275  // Extract and set plane bounds
276  const DoubleList boundsList =
277  this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("boundsParam"), "bound", 6);
278 
280 
281  BoundsArrayType bounds;
282  for (it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i)
283  {
284  bounds[i] = *it;
285  }
286 
287  planeGeo->SetBounds(bounds);
288 
289  // Extract and set spacing and origin
290  const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing"));
291  planeGeo->SetSpacing(spacing);
292 
293  const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin"));
294  planeGeo->SetOrigin(origin);
295  planarFigure->SetPlaneGeometry(planeGeo);
296  }
297  catch (...)
298  {
299  }
300  }
301  TiXmlElement *cpElement = pfElement->FirstChildElement("ControlPoints");
302  bool first = true;
303  if (cpElement != nullptr)
304  for (TiXmlElement *vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr;
305  vertElement = vertElement->NextSiblingElement("Vertex"))
306  {
307  int id = 0;
308  mitk::Point2D::ValueType x = 0.0;
309  mitk::Point2D::ValueType y = 0.0;
310  if (vertElement->QueryIntAttribute("id", &id) == TIXML_WRONG_TYPE)
311  return; // TODO: can we do a better error handling?
312  if (vertElement->QueryDoubleAttribute("x", &x) == TIXML_WRONG_TYPE)
313  return; // TODO: can we do a better error handling?
314  if (vertElement->QueryDoubleAttribute("y", &y) == TIXML_WRONG_TYPE)
315  return; // TODO: can we do a better error handling?
316  Point2D p;
317  p.SetElement(0, x);
318  p.SetElement(1, y);
319  if (first == true) // needed to set m_FigurePlaced to true
320  {
321  planarFigure->PlaceFigure(p);
322  first = false;
323  }
324  planarFigure->SetControlPoint(id, p, true);
325  }
326 
327  // Calculate feature quantities of this PlanarFigure
328  planarFigure->EvaluateFeatures();
329 
330  // Make sure that no control point is currently selected
331  planarFigure->DeselectControlPoint();
332 
333  // \TODO: what about m_FigurePlaced and m_SelectedControlPoint ??
334  this->SetNthOutput(this->GetNumberOfOutputs(), planarFigure); // add planarFigure as new output of this filter
335  }
336 
337  m_Success = true;
338 }
339 
341 {
342  if (e == nullptr)
343  throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
344  mitk::Point3D point;
345  mitk::ScalarType p(-1.0);
346  if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE)
347  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
348  point.SetElement(0, p);
349  if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE)
350  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
351  point.SetElement(1, p);
352  if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE)
353  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
354  point.SetElement(2, p);
355  return point;
356 }
357 
359 {
360  if (e == nullptr)
361  throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
362  mitk::Vector3D vector;
363  mitk::ScalarType p(-1.0);
364  if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE)
365  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
366  vector.SetElement(0, p);
367  if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE)
368  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
369  vector.SetElement(1, p);
370  if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE)
371  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
372  vector.SetElement(2, p);
373  return vector;
374 }
375 
377  TiXmlElement *e, const char *attributeNameBase, unsigned int count)
378 {
379  DoubleList list;
380 
381  if (e == nullptr)
382  throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling?
383 
384  for (unsigned int i = 0; i < count; ++i)
385  {
386  mitk::ScalarType p(-1.0);
387  std::stringstream attributeName;
388  attributeName << attributeNameBase << i;
389 
390  if (e->QueryDoubleAttribute(attributeName.str().c_str(), &p) == TIXML_WRONG_TYPE)
391  throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling?
392  list.push_back(p);
393  }
394 
395  return list;
396 }
397 
399 {
400 }
401 
403 {
404  if (std::string(name).empty())
405  return false;
406 
407  return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(name)) ==
408  ".pf"); // assume, we can read all .pf files
409 
410  // TiXmlDocument document(name);
411  // if (document.LoadFile() == false)
412  // return false;
413  // return (document.FirstChildElement("PlanarFigure") != nullptr);
414 }
415 
416 bool mitk::PlanarFigureReader::CanReadFile(const std::string filename, const std::string, const std::string)
417 {
418  if (filename.empty())
419  return false;
420 
421  return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(filename)) ==
422  ".pf"); // assume, we can read all .pf files
423 
424  // TiXmlDocument document(filename);
425  // if (document.LoadFile() == false)
426  // return false;
427  // return (document.FirstChildElement("PlanarFigure") != nullptr);
428 }
429 
430 void mitk::PlanarFigureReader::ResizeOutputs(const unsigned int &num)
431 {
432  unsigned int prevNum = this->GetNumberOfOutputs();
433  this->SetNumberOfIndexedOutputs(num);
434  for (unsigned int i = prevNum; i < num; ++i)
435  {
436  this->SetNthOutput(i, this->MakeOutput(i).GetPointer());
437  }
438 }
std::list< double > DoubleList
mitk::Point3D GetPointFromXMLNode(TiXmlElement *e)
parses the element for the attributes x,y,z and returns a mitk::Point3D filled with these values ...
static Pointer New()
BoundingBoxType::BoundsArrayType BoundsArrayType
static Pointer New()
#define MITK_ERROR
Definition: mitkLogMacros.h:20
static Pointer New()
double ScalarType
static Pointer New()
GeometryTransformHolder::TransformType TransformType
mitkBaseDataSourceGetOutputDeclarations itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override
static Pointer New()
DoubleList GetDoubleAttributeListFromXMLNode(TiXmlElement *e, const char *attributeNameBase, unsigned int count)
parses the element for the attributes name0 to nameN, where "name" and the number of attributes to re...
static Pointer New()
Implementation of PlanarFigure representing a polygon with two or more control points.
#define MITK_WARN
Definition: mitkLogMacros.h:19
Convenience class to temporarily change the current locale.
unsigned int m_MemorySize
Base class for all filters which have an object of type mitk::PlanarFigure as output.
ValueType
Type of the value held by a Value object.
Definition: jsoncpp.h:345
static Pointer New()
mitk::Vector3D GetVectorFromXMLNode(TiXmlElement *e)
parses the element for the attributes x,y,z and returns a mitk::Vector3D filled with these values ...
const char * m_MemoryBuffer
Interface class of readers that read from files.
static Pointer New()
static Pointer New()
virtual void ResizeOutputs(const unsigned int &num)
static Pointer New()
static bool CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern)
BoundingBoxType::BoundsArrayType BoundsArrayType