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