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