Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkSceneReaderV1.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 "mitkSceneReaderV1.h"
18 #include "Poco/Path.h"
19 #include "mitkBaseRenderer.h"
20 #include "mitkIOUtil.h"
21 #include "mitkProgressBar.h"
23 #include "mitkSerializerMacros.h"
25 
26 MITK_REGISTER_SERIALIZER(SceneReaderV1)
27 
28 namespace
29 {
30  typedef std::pair<mitk::DataNode::Pointer, std::list<std::string>> NodesAndParentsPair;
31 
32  bool NodeSortByLayerIsLessThan(const NodesAndParentsPair &left, const NodesAndParentsPair &right)
33  {
34  if (left.first.IsNotNull() && right.first.IsNotNull())
35  {
36  int leftLayer;
37  int rightLayer;
38  if (left.first->GetIntProperty("layer", leftLayer) && right.first->GetIntProperty("layer", rightLayer))
39  {
40  return leftLayer < rightLayer;
41  }
42  else
43  {
44  // fall back to name sort
45  return left.first->GetName() < right.first->GetName();
46  }
47  }
48 
49  // in all other cases, fall back to stupid pointer comparison
50  // this is not reasonable but at least answers the sorting
51  // question clearly
52  return left.first.GetPointer() < right.first.GetPointer();
53  }
54 }
55 
56 bool mitk::SceneReaderV1::LoadScene(TiXmlDocument &document, const std::string &workingDirectory, DataStorage *storage)
57 {
58  assert(storage);
59  bool error(false);
60 
61  // TODO prepare to detect errors (such as cycles) from wrongly written or edited xml files
62 
63  // Get number of elements to initialze progress bar
64  // 1. if there is a <data type="..." file="..."> element,
65  // - construct a name for the appropriate serializer
66  // - try to instantiate this serializer via itk object factory
67  // - if serializer could be created, use it to read the file into a BaseData object
68  // - if successful, call the new node's SetData(..)
69 
70  // create a node for the tag "data" and test if node was created
71  typedef std::vector<mitk::DataNode::Pointer> DataNodeVector;
72  DataNodeVector DataNodes;
73  unsigned int listSize = 0;
74  for (TiXmlElement *element = document.FirstChildElement("node"); element != NULL;
75  element = element->NextSiblingElement("node"))
76  {
77  ++listSize;
78  }
79 
80  ProgressBar::GetInstance()->AddStepsToDo(listSize * 2);
81 
82  for (TiXmlElement *element = document.FirstChildElement("node"); element != NULL;
83  element = element->NextSiblingElement("node"))
84  {
85  DataNodes.push_back(LoadBaseDataFromDataTag(element->FirstChildElement("data"), workingDirectory, error));
87  }
88 
89  // iterate all nodes
90  // first level nodes should be <node> elements
91  DataNodeVector::iterator nit = DataNodes.begin();
92  for (TiXmlElement *element = document.FirstChildElement("node"); element != NULL || nit != DataNodes.end();
93  element = element->NextSiblingElement("node"), ++nit)
94  {
95  mitk::DataNode::Pointer node = *nit;
96  // in case dataXmlElement is valid test whether it containts the "properties" child tag
97  // and process further if and only if yes
98  TiXmlElement *dataXmlElement = element->FirstChildElement("data");
99  if (dataXmlElement && dataXmlElement->FirstChildElement("properties"))
100  {
101  TiXmlElement *baseDataElement = dataXmlElement->FirstChildElement("properties");
102  if (node->GetData())
103  {
104  DecorateBaseDataWithProperties(node->GetData(), baseDataElement, workingDirectory);
105  }
106  else
107  {
108  MITK_WARN << "BaseData properties stored in scene file, but BaseData could not be read" << std::endl;
109  }
110  }
111 
112  // 2. check child nodes
113  const char *uida = element->Attribute("UID");
114  std::string uid("");
115 
116  if (uida)
117  {
118  uid = uida;
119  m_NodeForID[uid] = node.GetPointer();
120  m_IDForNode[node.GetPointer()] = uid;
121  }
122  else
123  {
124  MITK_ERROR << "No UID found for current node. Node will have no parents.";
125  error = true;
126  }
127 
128  // 3. if there are <properties> nodes,
129  // - instantiate the appropriate PropertyListDeSerializer
130  // - use them to construct PropertyList objects
131  // - add these properties to the node (if necessary, use renderwindow name)
132  bool success = DecorateNodeWithProperties(node, element, workingDirectory);
133  if (!success)
134  {
135  MITK_ERROR << "Could not load properties for node.";
136  error = true;
137  }
138 
139  // remember node for later adding to DataStorage
140  m_OrderedNodePairs.push_back(std::make_pair(node, std::list<std::string>()));
141 
142  // 4. if there are <source> elements, remember parent objects
143  for (TiXmlElement *source = element->FirstChildElement("source"); source != NULL;
144  source = source->NextSiblingElement("source"))
145  {
146  const char *sourceUID = source->Attribute("UID");
147  if (sourceUID)
148  {
149  m_OrderedNodePairs.back().second.push_back(std::string(sourceUID));
150  }
151  }
152 
154  } // end for all <node>
155 
156  // sort our nodes by their "layer" property
157  // (to be inserted in that order)
158  m_OrderedNodePairs.sort(&NodeSortByLayerIsLessThan);
159 
160  // remove all unknown parent UIDs
161  for (OrderedNodesList::iterator nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
162  ++nodesIter)
163  {
164  for (std::list<std::string>::iterator parentsIter = nodesIter->second.begin();
165  parentsIter != nodesIter->second.end();)
166  {
167  if (m_NodeForID.find(*parentsIter) == m_NodeForID.end())
168  {
169  parentsIter = nodesIter->second.erase(parentsIter);
170  MITK_WARN << "Found a DataNode with unknown parents. Will add it to DataStorage without any parent objects.";
171  error = true;
172  }
173  else
174  {
175  ++parentsIter;
176  }
177  }
178  }
179 
180  // repeat the following loop ...
181  // ... for all created nodes
182  unsigned int lastMapSize(0);
183  while (lastMapSize !=
185  .size()) // this is to prevent infinite loops; each iteration must at least add one node to DataStorage
186  {
187  lastMapSize = m_OrderedNodePairs.size();
188 
189  // iterate (layer) ordered nodes backwards
190  // we insert the highest layers first
191  for (OrderedNodesList::iterator nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
192  ++nodesIter)
193  {
194  bool addThisNode(true);
195 
196  // if any parent node is not yet in DataStorage, skip node for now and check later
197  for (std::list<std::string>::iterator parentsIter = nodesIter->second.begin();
198  parentsIter != nodesIter->second.end();
199  ++parentsIter)
200  {
201  if (!storage->Exists(m_NodeForID[*parentsIter]))
202  {
203  addThisNode = false;
204  break;
205  }
206  }
207 
208  if (addThisNode)
209  {
211  for (std::list<std::string>::iterator parentsIter = nodesIter->second.begin();
212  parentsIter != nodesIter->second.end();
213  ++parentsIter)
214  {
215  parents->push_back(m_NodeForID[*parentsIter]);
216  }
217 
218  // if all parents are found in datastorage (or are unknown), add node to DataStorage
219  storage->Add(nodesIter->first, parents);
220 
221  // remove this node from m_OrderedNodePairs
222  m_OrderedNodePairs.erase(nodesIter);
223 
224  // break this for loop because iterators are probably invalid
225  break;
226  }
227  }
228  }
229 
230  // All nodes that are still in m_OrderedNodePairs at this point are not part of a proper directed graph structure.
231  // We'll add such nodes without any parent information.
232  for (OrderedNodesList::iterator nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end();
233  ++nodesIter)
234  {
235  storage->Add(nodesIter->first);
236  MITK_WARN << "Encountered node that is not part of a directed graph structure. Will be added to DataStorage "
237  "without parents.";
238  error = true;
239  }
240 
241  return !error;
242 }
243 
245  const std::string &workingDirectory,
246  bool &error)
247 {
248  DataNode::Pointer node;
249 
250  if (dataElement)
251  {
252  const char *filename = dataElement->Attribute("file");
253  if (filename)
254  {
255  try
256  {
257  std::vector<BaseData::Pointer> baseData = IOUtil::Load(workingDirectory + Poco::Path::separator() + filename);
258  if (baseData.size() > 1)
259  {
260  MITK_WARN << "Discarding multiple base data results from " << filename << " except the first one.";
261  }
262  node = DataNode::New();
263  node->SetData(baseData.front());
264  }
265  catch (std::exception &e)
266  {
267  MITK_ERROR << "Error during attempt to read '" << filename << "'. Exception says: " << e.what();
268  error = true;
269  }
270 
271  if (node.IsNull())
272  {
273  MITK_ERROR << "Error during attempt to read '" << filename << "'. Factory returned NULL object.";
274  error = true;
275  }
276  }
277  }
278 
279  // in case there was no <data> element we create a new empty node (for appending a propertylist later)
280  if (node.IsNull())
281  {
282  node = DataNode::New();
283  }
284 
285  return node;
286 }
287 
289 {
290  // Basically call propertyList.Clear(), but implement exceptions (see bug 19354)
291  BaseData *data = node.GetData();
292 
293  PropertyList::Pointer propertiesToKeep = PropertyList::New();
294 
295  if (dynamic_cast<Image *>(data))
296  {
297  /*
298  Older scene files (before changes of bug 17547) could contain
299  a RenderingMode property with value "LevelWindow_Color".
300  Since bug 17547 this value has been removed and replaced by
301  the default value LookupTable_LevelWindow_Color.
302 
303  This new default value does only result in "black-to-white"
304  CT images (or others) if there is a corresponding lookup
305  table. Such a lookup table is provided as a default value
306  by the Image mapper. Since that value was never present in
307  older scene files, we do well in not removing the new
308  default value here. Otherwise the mapper would fall back
309  to another default which is all the colors of the rainbow :-(
310  */
311  BaseProperty::Pointer lutProperty = propertyList.GetProperty("LookupTable");
312  propertiesToKeep->SetProperty("LookupTable", lutProperty);
313 
314  /*
315  Older scene files (before changes of T14807) may contain
316  multi-component images without the "Image.Displayed Component"
317  property.
318 
319  As the treatment as multi-component image and the corresponding
320  visualization options hinges on that property we should not delete
321  it, if it was added by the mapper.
322  Old diffusion images might contain the "DisplayChannel" property
323  which stores the same information, however ignoring it is an acceptable
324  loss of precision as usually which channel is selected is not terribly
325  important.
326 
327  This is a fix for the issue reported in T19919.
328  */
329  BaseProperty::Pointer compProperty = propertyList.GetProperty("Image.Displayed Component");
330  if (compProperty.IsNotNull())
331  {
332  propertiesToKeep->SetProperty("Image.Displayed Component", compProperty);
333  }
334  }
335 
336  propertyList.Clear();
337 
338  propertyList.ConcatenatePropertyList(propertiesToKeep);
339 }
340 
342  TiXmlElement *nodeElement,
343  const std::string &workingDirectory)
344 {
345  assert(node);
346  assert(nodeElement);
347  bool error(false);
348 
349  for (TiXmlElement *properties = nodeElement->FirstChildElement("properties"); properties != NULL;
350  properties = properties->NextSiblingElement("properties"))
351  {
352  const char *propertiesfilea(properties->Attribute("file"));
353  std::string propertiesfile(propertiesfilea ? propertiesfilea : "");
354 
355  const char *renderwindowa(properties->Attribute("renderwindow"));
356  std::string renderwindow(renderwindowa ? renderwindowa : "");
357 
358  PropertyList::Pointer propertyList =
359  node->GetPropertyList(renderwindow); // DataNode implementation always returns a propertylist
360  ClearNodePropertyListWithExceptions(*node, *propertyList);
361 
362  // use deserializer to construct new properties
364 
365  deserializer->SetFilename(workingDirectory + Poco::Path::separator() + propertiesfile);
366  bool success = deserializer->Deserialize();
367  error |= !success;
368  PropertyList::Pointer readProperties = deserializer->GetOutput();
369 
370  if (readProperties.IsNotNull())
371  {
372  propertyList->ConcatenatePropertyList(readProperties, true); // true = replace
373  }
374  else
375  {
376  MITK_ERROR << "Property list reader did not return a property list. This is an implementation error. Please tell "
377  "your developer.";
378  error = true;
379  }
380  }
381 
382  return !error;
383 }
384 
386  TiXmlElement *baseDataNodeElem,
387  const std::string &workingDir)
388 {
389  // check given variables, initialize error variable
390  assert(baseDataNodeElem);
391  bool error(false);
392 
393  // get the file name stored in the <properties ...> tag
394  const char *baseDataPropertyFile(baseDataNodeElem->Attribute("file"));
395  // check if the filename was found
396  if (baseDataPropertyFile)
397  {
398  // PropertyList::Pointer dataPropList = data->GetPropertyList();
399 
401 
402  // initialize the property reader
403  propertyDeserializer->SetFilename(workingDir + Poco::Path::separator() + baseDataPropertyFile);
404  bool ioSuccess = propertyDeserializer->Deserialize();
405  error = !ioSuccess;
406 
407  // get the output
408  PropertyList::Pointer inProperties = propertyDeserializer->GetOutput();
409 
410  // store the read-in properties to the given node or throw error otherwise
411  if (inProperties.IsNotNull())
412  {
413  data->SetPropertyList(inProperties);
414  }
415  else
416  {
417  MITK_ERROR << "The property deserializer did not return a (valid) property list.";
418  error = true;
419  }
420  }
421  else
422  {
423  MITK_ERROR << "Function DecorateBaseDataWithProperties(...) called with false TiXmlElement. \n \t ->Given element "
424  "does not contain a 'file' attribute. \n";
425  error = true;
426  }
427 
428  return !error;
429 }
void Progress(unsigned int steps=1)
Sets the current amount of progress to current progress + steps.
virtual void Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents=nullptr)=0
Adds a DataNode containing a data object to its internal storage.
mitk::PropertyList * GetPropertyList(const mitk::BaseRenderer *renderer=nullptr) const
Get the PropertyList of the renderer. If renderer is NULL, the BaseRenderer-independent PropertyList ...
mitk::BaseProperty * GetProperty(const std::string &propertyKey) const
Get a property by its name.
#define MITK_REGISTER_SERIALIZER(classname)
Data management class that handles 'was created by' relations.
itk::SmartPointer< Self > Pointer
static Pointer New()
Base of all data objects.
Definition: mitkBaseData.h:39
#define MITK_ERROR
Definition: mitkLogMacros.h:24
void ConcatenatePropertyList(PropertyList *pList, bool replace=false)
Set a property object in the list/map by reference.
BaseData * GetData() const
Get the data object (instance of BaseData, e.g., an Image) managed by this DataNode.
Key-value list holding instances of BaseProperty.
void ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList)
Clear a default property list and handle some exceptions.
static ProgressBar * GetInstance()
static method to get the GUI dependent ProgressBar-instance so the methods for steps to do and progre...
NodeToIDMappingType m_IDForNode
bool DecorateBaseDataWithProperties(BaseData::Pointer data, TiXmlElement *baseDataNodeElem, const std::string &workingDir)
reads all properties assigned to a base data element and assigns the list to the base data object ...
#define MITK_WARN
Definition: mitkLogMacros.h:23
static Pointer New()
static const std::string filename
IDToNodeMappingType m_NodeForID
void AddStepsToDo(unsigned int steps)
Adds steps to totalSteps.
OrderedNodesList m_OrderedNodePairs
bool DecorateNodeWithProperties(DataNode *node, TiXmlElement *nodeElement, const std::string &workingDirectory)
reads all the properties from the XML document and recreates them in node
virtual bool Exists(const mitk::DataNode *node) const =0
Checks if a node exists in the DataStorage.
static DataStorage::SetOfObjects::Pointer Load(const std::string &path, DataStorage &storage)
Load a file into the given DataStorage.
Definition: mitkIOUtil.cpp:483
Class for nodes of the DataTree.
Definition: mitkDataNode.h:66
virtual bool LoadScene(TiXmlDocument &document, const std::string &workingDirectory, DataStorage *storage) override
DataNode::Pointer LoadBaseDataFromDataTag(TiXmlElement *dataElement, const std::string &workingDirectory, bool &error)
tries to create one DataNode from a given XML element
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.