Medical Imaging Interaction Toolkit  2018.4.99-08619e4f
Medical Imaging Interaction Toolkit
mitkPlanarFigureIOTest.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 "mitkTestingMacros.h"
14 
15 #include "mitkPlanarAngle.h"
16 #include "mitkPlanarCircle.h"
17 #include "mitkPlanarCross.h"
19 #include "mitkPlanarLine.h"
20 #include "mitkPlanarPolygon.h"
21 #include "mitkPlanarRectangle.h"
23 
24 #include "mitkPlaneGeometry.h"
25 
26 #include "mitkGeometry3D.h"
27 #include "mitkAbstractFileIO.h"
28 #include "mitkFileReaderRegistry.h"
29 #include "mitkFileWriterRegistry.h"
30 #include "mitkIOUtil.h"
31 
32 #include <itksys/SystemTools.hxx>
33 
35 class PlanarFigureIOTestClass
36 {
37 public:
38  typedef std::map<const std::string, mitk::PlanarFigure::Pointer> PlanarFigureMap;
39  typedef std::map<const std::string, std::string> PlanarFigureToStreamMap;
40 
41  static PlanarFigureMap CreatePlanarFigures()
42  {
43  PlanarFigureMap planarFigures;
44 
45  // Create PlaneGeometry on which to place the PlanarFigures
47  planeGeometry->InitializeStandardPlane(100.0, 100.0);
48 
49  // Create a few sample points for PlanarFigure placement
50  mitk::Point2D p0;
51  p0[0] = 20.0;
52  p0[1] = 20.0;
53  mitk::Point2D p1;
54  p1[0] = 80.0;
55  p1[1] = 80.0;
56  mitk::Point2D p2;
57  p2[0] = 90.0;
58  p2[1] = 10.0;
59  mitk::Point2D p3;
60  p3[0] = 10.0;
61  p3[1] = 90.0;
62 
63  // Create PlanarAngle
65  planarAngle->SetPlaneGeometry(planeGeometry);
66  planarAngle->PlaceFigure(p0);
67  planarAngle->SetCurrentControlPoint(p1);
68  planarAngle->AddControlPoint(p2);
69  planarAngle->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
70  planarFigures.emplace("planarAngle",planarAngle.GetPointer());
71 
72  // Create PlanarCircle
74  planarCircle->SetPlaneGeometry(planeGeometry);
75  planarCircle->PlaceFigure(p0);
76  planarCircle->SetCurrentControlPoint(p1);
77  planarCircle->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
78  planarFigures.emplace("planarCircle",planarCircle.GetPointer());
79 
80  // Create PlanarCross
82  planarCross->SetSingleLineMode(false);
83  planarCross->SetPlaneGeometry(planeGeometry);
84  planarCross->PlaceFigure(p0);
85  planarCross->SetCurrentControlPoint(p1);
86  planarCross->AddControlPoint(p2);
87  planarCross->AddControlPoint(p3);
88  planarCross->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
89  planarFigures.emplace("planarCross",planarCross.GetPointer());
90 
91  // Create PlanarFourPointAngle
93  planarFourPointAngle->SetPlaneGeometry(planeGeometry);
94  planarFourPointAngle->PlaceFigure(p0);
95  planarFourPointAngle->SetCurrentControlPoint(p1);
96  planarFourPointAngle->AddControlPoint(p2);
97  planarFourPointAngle->AddControlPoint(p3);
98  planarFourPointAngle->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
99  planarFigures.emplace("planarFourPointAngle",planarFourPointAngle.GetPointer());
100 
101  // Create PlanarLine
103  planarLine->SetPlaneGeometry(planeGeometry);
104  planarLine->PlaceFigure(p0);
105  planarLine->SetCurrentControlPoint(p1);
106  planarLine->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
107  planarFigures.emplace("planarLine",planarLine.GetPointer());
108 
109  // Create PlanarPolygon
111  planarPolygon->SetClosed(false);
112  planarPolygon->SetPlaneGeometry(planeGeometry);
113  planarPolygon->PlaceFigure(p0);
114  planarPolygon->SetCurrentControlPoint(p1);
115  planarPolygon->AddControlPoint(p2);
116  planarPolygon->AddControlPoint(p3);
117  planarPolygon->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
118  planarFigures.emplace("planarPolygon",planarPolygon.GetPointer());
119 
120  // Create PlanarSubdivisionPolygon
122  planarSubdivisionPolygon->SetClosed(false);
123  planarSubdivisionPolygon->SetPlaneGeometry(planeGeometry);
124  planarSubdivisionPolygon->PlaceFigure(p0);
125  planarSubdivisionPolygon->SetCurrentControlPoint(p1);
126  planarSubdivisionPolygon->AddControlPoint(p2);
127  planarSubdivisionPolygon->AddControlPoint(p3);
128  planarSubdivisionPolygon->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
129  planarFigures.emplace("planarSubdivisionPolygon",planarSubdivisionPolygon.GetPointer());
130 
131  // Create PlanarRectangle
133  planarRectangle->SetPlaneGeometry(planeGeometry);
134  planarRectangle->PlaceFigure(p0);
135  planarRectangle->SetCurrentControlPoint(p1);
136  planarRectangle->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
137  planarFigures.emplace("planarRectangle",planarRectangle.GetPointer());
138 
139  // create preciseGeometry which is using float coordinates
141  mitk::Vector3D right;
142  right[0] = 0.0;
143  right[1] = 1.23456;
144  right[2] = 0.0;
145 
146  mitk::Vector3D down;
147  down[0] = 1.23456;
148  down[1] = 0.0;
149  down[2] = 0.0;
150 
151  mitk::Vector3D spacing;
152  spacing[0] = 0.0123456;
153  spacing[1] = 0.0123456;
154  spacing[2] = 1.123456;
155  preciseGeometry->InitializeStandardPlane(right, down, &spacing);
156 
157  // convert points into the precise coordinates
158  mitk::Point2D p0precise;
159  p0precise[0] = p0[0] * spacing[0];
160  p0precise[1] = p0[1] * spacing[1];
161  mitk::Point2D p1precise;
162  p1precise[0] = p1[0] * spacing[0];
163  p1precise[1] = p1[1] * spacing[1];
164  mitk::Point2D p2precise;
165  p2precise[0] = p2[0] * spacing[0];
166  p2precise[1] = p2[1] * spacing[1];
167  mitk::Point2D p3precise;
168  p3precise[0] = p3[0] * spacing[0];
169  p3precise[1] = p3[1] * spacing[1];
170 
171  // Now all PlanarFigures are create using the precise Geometry
172  // Create PlanarCross
174  nochncross->SetSingleLineMode(false);
175  nochncross->SetPlaneGeometry(preciseGeometry);
176  nochncross->PlaceFigure(p0precise);
177  nochncross->SetCurrentControlPoint(p1precise);
178  nochncross->AddControlPoint(p2precise);
179  nochncross->AddControlPoint(p3precise);
180  nochncross->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
181  planarFigures.emplace("nochncross", nochncross.GetPointer());
182 
183  // Create PlanarAngle
184  mitk::PlanarAngle::Pointer planarAnglePrecise = mitk::PlanarAngle::New();
185  planarAnglePrecise->SetPlaneGeometry(preciseGeometry);
186  planarAnglePrecise->PlaceFigure(p0precise);
187  planarAnglePrecise->SetCurrentControlPoint(p1precise);
188  planarAnglePrecise->AddControlPoint(p2precise);
189  planarAnglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
190  planarFigures.emplace("planarAnglePrecise",planarAnglePrecise.GetPointer());
191 
192  // Create PlanarCircle
194  planarCirclePrecise->SetPlaneGeometry(preciseGeometry);
195  planarCirclePrecise->PlaceFigure(p0precise);
196  planarCirclePrecise->SetCurrentControlPoint(p1precise);
197  planarCirclePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
198  planarFigures.emplace("planarCirclePrecise",planarCirclePrecise.GetPointer());
199 
200  // Create PlanarFourPointAngle
202  planarFourPointAnglePrecise->SetPlaneGeometry(preciseGeometry);
203  planarFourPointAnglePrecise->PlaceFigure(p0precise);
204  planarFourPointAnglePrecise->SetCurrentControlPoint(p1precise);
205  planarFourPointAnglePrecise->AddControlPoint(p2precise);
206  planarFourPointAnglePrecise->AddControlPoint(p3precise);
207  planarFourPointAnglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
208  planarFigures.emplace("planarFourPointAnglePrecise",planarFourPointAnglePrecise.GetPointer());
209 
210  // Create PlanarLine
211  mitk::PlanarLine::Pointer planarLinePrecise = mitk::PlanarLine::New();
212  planarLinePrecise->SetPlaneGeometry(preciseGeometry);
213  planarLinePrecise->PlaceFigure(p0precise);
214  planarLinePrecise->SetCurrentControlPoint(p1precise);
215  planarLinePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
216  planarFigures.emplace("planarLinePrecise",planarLinePrecise.GetPointer());
217 
218  // Create PlanarPolygon
220  planarPolygonPrecise->SetClosed(false);
221  planarPolygonPrecise->SetPlaneGeometry(preciseGeometry);
222  planarPolygonPrecise->PlaceFigure(p0precise);
223  planarPolygonPrecise->SetCurrentControlPoint(p1precise);
224  planarPolygonPrecise->AddControlPoint(p2precise);
225  planarPolygonPrecise->AddControlPoint(p3precise);
226  planarPolygonPrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
227  planarFigures.emplace("planarPolygonPrecise",planarPolygonPrecise.GetPointer());
228 
229  // Create PlanarSubdivisionPolygon
231  planarSubdivisionPolygonPrecise->SetClosed(false);
232  planarSubdivisionPolygonPrecise->SetPlaneGeometry(preciseGeometry);
233  planarSubdivisionPolygonPrecise->PlaceFigure(p0precise);
234  planarSubdivisionPolygonPrecise->SetCurrentControlPoint(p1precise);
235  planarSubdivisionPolygonPrecise->AddControlPoint(p2precise);
236  planarSubdivisionPolygonPrecise->AddControlPoint(p3precise);
237  planarSubdivisionPolygonPrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
238  planarFigures.emplace("planarSubdivisionPolygonPrecise",planarSubdivisionPolygonPrecise.GetPointer());
239 
240  // Create PlanarRectangle
242  planarRectanglePrecise->SetPlaneGeometry(preciseGeometry);
243  planarRectanglePrecise->PlaceFigure(p0precise);
244  planarRectanglePrecise->SetCurrentControlPoint(p1precise);
245  planarRectanglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true);
246  planarFigures.emplace("planarRectanglePrecise",planarRectanglePrecise.GetPointer());
247 
248  return planarFigures;
249  }
250 
251  static PlanarFigureMap CreateClonedPlanarFigures(PlanarFigureMap original)
252  {
253  PlanarFigureMap copiedPlanarFigures;
254 
255  for (const auto& pf : original)
256  {
257  mitk::PlanarFigure::Pointer copiedFigure = pf.second->Clone();
258 
259  copiedPlanarFigures[pf.first] = copiedFigure;
260  }
261  return copiedPlanarFigures;
262  }
263 
264  static void VerifyPlanarFigures(PlanarFigureMap &referencePfs, PlanarFigureMap &testPfs)
265  {
266  PlanarFigureMap::iterator it1, it2;
267 
268  int i = 0;
269  for (it1 = referencePfs.begin(); it1 != referencePfs.end(); ++it1)
270  {
271  bool planarFigureFound = false;
272  int j = 0;
273  for (it2 = testPfs.begin(); it2 != testPfs.end(); ++it2)
274  {
275  // Compare PlanarFigures (returns false if different types)
276  if (ComparePlanarFigures(it1->second, it2->second))
277  {
278  planarFigureFound = true;
279  }
280  ++j;
281  }
282 
283  // Test if (at least) on PlanarFigure of the first type was found in the second list
284  MITK_TEST_CONDITION_REQUIRED(planarFigureFound,
285  "Testing if " << it1->second->GetNameOfClass() << " has a counterpart " << i);
286  ++i;
287  }
288  }
289 
290  static bool ComparePlanarFigures(const mitk::PlanarFigure *referencePf, const mitk::PlanarFigure *testPf)
291  {
292  // Test if PlanarFigures are of same type; otherwise return
293  if (strcmp(referencePf->GetNameOfClass(), testPf->GetNameOfClass()) != 0)
294  {
295  return false;
296  }
297 
298  if (strcmp(referencePf->GetNameOfClass(), "PlanarCross") == 0)
299  {
300  std::cout << "Planar Cross Found" << std::endl;
301  }
302 
303  // Test for equal number of control points
304  if (referencePf->GetNumberOfControlPoints() != testPf->GetNumberOfControlPoints())
305  {
306  return false;
307  }
308 
309  // Test if all control points are equal
310  for (unsigned int i = 0; i < referencePf->GetNumberOfControlPoints(); ++i)
311  {
312  mitk::Point2D point1 = referencePf->GetControlPoint(i);
313  mitk::Point2D point2 = testPf->GetControlPoint(i);
314 
315  if (point1.EuclideanDistanceTo(point2) >= mitk::eps)
316  {
317  return false;
318  }
319  }
320 
321  // Test for equal number of properties
322  typedef mitk::PropertyList::PropertyMap PropertyMap;
323  const PropertyMap *refProperties = referencePf->GetPropertyList()->GetMap();
324  const PropertyMap *testProperties = testPf->GetPropertyList()->GetMap();
325 
326  MITK_INFO << "List 1:";
327  for (auto i1 = refProperties->begin(); i1 != refProperties->end(); ++i1)
328  {
329  std::cout << i1->first << std::endl;
330  }
331 
332  MITK_INFO << "List 2:";
333  for (auto i2 = testProperties->begin(); i2 != testProperties->end(); ++i2)
334  {
335  std::cout << i2->first << std::endl;
336  }
337 
338  MITK_INFO << "-------";
339 
340  //remark test planar figures may have additional properties
341  //(e.g. reader meta information), but they are not relevant
342  //for the test. Only check of all properties of the reference
343  //are present and correct.
344  for (const auto& prop : *refProperties)
345  {
346  auto finding = testProperties->find(prop.first);
347  if (finding == testProperties->end())
348  {
349  return false;
350  }
351 
352  MITK_INFO << "Comparing " << prop.first << "(" << prop.second->GetValueAsString() << ") and " << finding->first
353  << "(" << finding->second->GetValueAsString() << ")";
354  // Compare property objects contained in the map entries (see mitk::PropertyList)
355  if (!(*(prop.second) == *(finding->second))) return false;
356  }
357 
358  // Test if Geometry is equal
359  const auto *planeGeometry1 = dynamic_cast<const mitk::PlaneGeometry *>(referencePf->GetPlaneGeometry());
360  const auto *planeGeometry2 = dynamic_cast<const mitk::PlaneGeometry *>(testPf->GetPlaneGeometry());
361 
362  // Test Geometry transform parameters
363  typedef mitk::Geometry3D::TransformType TransformType;
364  const TransformType *affineGeometry1 = planeGeometry1->GetIndexToWorldTransform();
365  const TransformType::ParametersType &parameters1 = affineGeometry1->GetParameters();
366  const TransformType::ParametersType &parameters2 = planeGeometry2->GetIndexToWorldTransform()->GetParameters();
367  for (unsigned int i = 0; i < affineGeometry1->GetNumberOfParameters(); ++i)
368  {
369  if (fabs(parameters1.GetElement(i) - parameters2.GetElement(i)) >= mitk::eps)
370  {
371  return false;
372  }
373  }
374 
375  // Test Geometry bounds
377  const BoundsArrayType &bounds1 = planeGeometry1->GetBounds();
378  const BoundsArrayType &bounds2 = planeGeometry2->GetBounds();
379  for (unsigned int i = 0; i < 6; ++i)
380  {
381  if (fabs(bounds1.GetElement(i) - bounds2.GetElement(i)) >= mitk::eps)
382  {
383  return false;
384  };
385  }
386 
387  // Test Geometry spacing and origin
388  mitk::Vector3D spacing1 = planeGeometry1->GetSpacing();
389  mitk::Vector3D spacing2 = planeGeometry2->GetSpacing();
390  if ((spacing1 - spacing2).GetNorm() >= mitk::eps)
391  {
392  return false;
393  }
394 
395  mitk::Point3D origin1 = planeGeometry1->GetOrigin();
396  mitk::Point3D origin2 = planeGeometry2->GetOrigin();
397 
398  if (origin1.EuclideanDistanceTo(origin2) >= mitk::eps)
399  {
400  return false;
401  }
402  return true;
403  }
404 
405  static PlanarFigureToStreamMap SerializePlanarFiguresToMemoryBuffers(PlanarFigureMap &planarFigures)
406  {
407  PlanarFigureToStreamMap pfMemoryStreams;
408 
409  for (const auto& pf : planarFigures)
410  {
411  mitk::FileWriterRegistry writerRegistry;
412  auto writers = writerRegistry.GetWriters(pf.second.GetPointer(), "");
413 
414  std::ostringstream stream;
415  writers[0]->SetOutputStream("",&stream);
416  writers[0]->SetInput(pf.second);
417  writers[0]->Write();
418  pfMemoryStreams.emplace(pf.first, stream.str());
419  }
420 
421  return pfMemoryStreams;
422  }
423 
424  static PlanarFigureMap DeserializePlanarFiguresFromMemoryBuffers(PlanarFigureToStreamMap pfMemoryStreams)
425  {
426  // Store them in the list and return it
427  PlanarFigureMap planarFigures;
428 
429  mitk::FileReaderRegistry readerRegistry;
430  std::vector<mitk::IFileReader*> readers =
432 
433  for (const auto& pfStream : pfMemoryStreams)
434  {
435  std::istringstream stream;
436  stream.str(pfStream.second);
437  readers[0]->SetInput("", &stream);
438  auto pfRead = readers[0]->Read();
439  MITK_TEST_CONDITION(pfRead.size() == 1, "One planar figure should be read from stream.");
440  auto pf = dynamic_cast<mitk::PlanarFigure*>(pfRead.front().GetPointer());
441  MITK_TEST_CONDITION(pf != nullptr, "Loaded data should be a planar figure.");
442  planarFigures.emplace(pfStream.first, pf);
443  }
444 
445  return planarFigures;
446  }
447 
448 }; // end test helper class
449 
459 int mitkPlanarFigureIOTest(int /* argc */, char * /*argv*/ [])
460 {
461  MITK_TEST_BEGIN("PlanarFigureIO");
462 
463  // Create a number of PlanarFigure objects
464  PlanarFigureIOTestClass::PlanarFigureMap originalPlanarFigures = PlanarFigureIOTestClass::CreatePlanarFigures();
465 
466  // Create a number of cloned planar figures to test the Clone function
467  PlanarFigureIOTestClass::PlanarFigureMap clonedPlanarFigures =
468  PlanarFigureIOTestClass::CreateClonedPlanarFigures(originalPlanarFigures);
469 
470  PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, clonedPlanarFigures);
471 
472 
473  std::map <const std::string, const std::string> pfFileNameMap;
474  for (const auto& pf : originalPlanarFigures)
475  {
476  std::string filename = mitk::IOUtil::CreateTemporaryFile(pf.first+"_XXXXXX.pf", itksys::SystemTools::GetCurrentWorkingDirectory());
477  mitk::IOUtil::Save(pf.second, filename);
478  pfFileNameMap.emplace(pf.first, filename);
479  }
480 
481  // Write PlanarFigure objects to memory buffers
482  PlanarFigureIOTestClass::PlanarFigureToStreamMap writersStreams =
483  PlanarFigureIOTestClass::SerializePlanarFiguresToMemoryBuffers(originalPlanarFigures);
484 
485  // Read PlanarFigure objects from temp file
486  PlanarFigureIOTestClass::PlanarFigureMap retrievedPlanarFigures;
487  for (const auto& files : pfFileNameMap)
488  {
489  auto pf = mitk::IOUtil::Load<mitk::PlanarFigure>(files.second);
490  retrievedPlanarFigures.emplace(files.first, pf);
491  }
492 
493  // Read PlanarFigure objects from memory buffers
494  PlanarFigureIOTestClass::PlanarFigureMap retrievedPlanarFiguresFromMemory =
495  PlanarFigureIOTestClass::DeserializePlanarFiguresFromMemoryBuffers(writersStreams);
496 
497  // Test if original and retrieved PlanarFigure objects are the same
498  PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, retrievedPlanarFigures);
499 
500  // Test if original and memory retrieved PlanarFigure objects are the same
501  PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, retrievedPlanarFiguresFromMemory);
502 
503  // empty the originalPlanarFigures
504  originalPlanarFigures.clear();
505 
506  // Test if cloned and retrieved PlanarFigure objects are the same
507  PlanarFigureIOTestClass::VerifyPlanarFigures(clonedPlanarFigures, retrievedPlanarFigures);
508 
509  MITK_TEST_END()
510 }
Point2D GetControlPoint(unsigned int index) const
Returns specified control point in 2D world coordinates.
static Pointer New()
BoundingBoxType::BoundsArrayType BoundsArrayType
#define MITK_INFO
Definition: mitkLogMacros.h:18
virtual const PlaneGeometry * GetPlaneGeometry() const
Returns (previously set) 2D geometry of this figure.
std::vector< mitk::IFileReader * > GetReaders(const MimeType &mimeType, us::ModuleContext *context=us::GetModuleContext())
static MimeType GetMimeTypeForFile(const std::string &path, us::ModuleContext *context=us::GetModuleContext())
Get the highest ranked mime-type for the given file name.
static Pointer New()
#define MITK_TEST_CONDITION_REQUIRED(COND, MSG)
std::vector< IFileWriter * > GetWriters(const BaseData *baseData, const std::string &mimeType, us::ModuleContext *context=us::GetModuleContext())
GeometryTransformHolder::TransformType TransformType
static Pointer New()
section GeneralTestsDeprecatedOldTestingStyle Deprecated macros All tests with MITK_TEST_BEGIN()
int mitkPlanarFigureIOTest(int, char *[])
Test for PlanarFigure reader and writer classes.
static Pointer New()
unsigned int GetNumberOfControlPoints() const
Returns the current number of 2D control points defining this figure.
std::map< std::string, BaseProperty::Pointer > PropertyMap
#define MITK_TEST_CONDITION(COND, MSG)
static Pointer New()
mitk::PropertyList::Pointer GetPropertyList() const
Get the data&#39;s property list.
Base-class for geometric planar (2D) figures, such as lines, circles, rectangles, polygons...
static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty=false)
Save a mitk::BaseData instance.
Definition: mitkIOUtil.cpp:774
static std::string CreateTemporaryFile(std::ofstream &tmpStream, const std::string &templateName="XXXXXX", std::string path=std::string())
Definition: mitkIOUtil.cpp:413
static Pointer New()
MITKCORE_EXPORT const ScalarType eps
Describes a two-dimensional, rectangular plane.
static Pointer New()
and MITK_TEST_END()
mitk::AffineTransform3D * GetIndexToWorldTransform()
Get the transformation used to convert from index to world coordinates.
BoundingBoxType::BoundsArrayType BoundsArrayType