Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
mitkCollectionReader.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 #pragma warning (disable : 4996)
17 
18 #include "mitkCollectionReader.h"
19 #include <vtkXMLDataElement.h>
20 
21 #include <mitkIOUtil.h>
22 #include <mitkBaseDataIOFactory.h>
23 #include <mitkFiberBundle.h>
25 
26 #include <QDir>
27 
28 //XML StateMachine Tags
29 // Objects
30 const std::string COLLECTION = "col";
31 const std::string SUBCOLLECTION = "subcol";
32 const std::string DATA = "data";
33 const std::string ITEM = "item";
34 // Properties
35 const std::string NAME = "name";
36 const std::string ID = "id";
37 const std::string FILEPATH = "description";
38 const std::string LINK = "link";
39 
40 
41 static std::string GetName(std::string fileName,std::string suffix)
42 {
43  fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString();
44  return fileName.substr(0,fileName.length() -suffix.length()-9); // 8 = date length
45 }
46 
47 static std::string GetDate(std::string fileName,std::string suffix)
48 {
49  fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString();
50  fileName = fileName.substr(fileName.length() - suffix.length()-8,8); // 8 = date length
51  fileName.insert(6,"-");
52  fileName.insert(4,"-");
53  return fileName;
54 }
55 
56 
58  : m_Collection(NULL),
59  m_SubCollection(NULL),
60  m_DataItemCollection(NULL),
61  m_ColIgnore(false), m_ItemIgnore(false)
62 {
63 }
64 
66 {
67  this->Clear();
68 }
69 
70 
75 {
76  QDir fileName = QFileInfo(xmlFileName.c_str()).absoluteDir();
77  m_BaseDir = fileName.path().toStdString() + QDir::separator().toLatin1();
78  this->SetFileName(xmlFileName.c_str());
79  this->Parse();
80  if (m_Collection.IsNotNull())
81  m_Collection->SetXMLFile(xmlFileName);
82  return m_Collection;
83 }
84 
85 void mitk::CollectionReader::AddDataElementIds(std::vector<std::string> dataElemetIds)
86 {
87  m_SelectedDataItemIds.insert( m_SelectedDataItemIds.end(), dataElemetIds.begin(), dataElemetIds.end() );
88 }
89 
90 void mitk::CollectionReader::AddSubColIds(std::vector<std::string> subColIds)
91 {
92  m_SelectedSubColIds.insert( m_SelectedSubColIds.end(), subColIds.begin(), subColIds.end() );
93 }
94 
95 void mitk::CollectionReader::SetDataItemNames(std::vector<std::string> itemNames)
96 {
97  m_SelectedDataItemNames = itemNames;
98 }
99 
101 {
102  m_SelectedDataItemIds.clear();
103 }
104 
106 {
107  m_SelectedSubColIds.clear();
108 }
109 
111 {
112  m_DataItemCollection = NULL;
113  m_SubCollection = NULL;
114  m_Collection = NULL;
115 }
116 
117 mitk::DataCollection::Pointer mitk::CollectionReader::FolderToCollection(std::string folder, std::vector<std::string> suffixes,std::vector<std::string> seriesNames, bool allowGaps)
118 {
119  // Parse folder and look up all data,
120  // after sanitation only fully available groups are included (that is all suffixes are found)
121  FileListType fileList = SanitizeFileList(GenerateFileLists(folder, suffixes, allowGaps));
122 
123  if (fileList.size() <= 0)
124  return NULL;
125 
127  collection->SetName(GetName(fileList.at(0).at(0),suffixes.at(0)));
128 
129  for (unsigned int k=0; k < fileList.at(0).size(); ++k) // all groups have the same amount of items, so looking at 0 is ok.
130  {
132  for (unsigned int i=0; i< suffixes.size(); ++i)
133  {
134  std::string fileName = fileList.at(i).at(k);
135  if (fileName.find(".fib") >= fileName.length())
136  {
137  Image::Pointer image = IOUtil::LoadImage(fileList.at(i).at(k));
138  subCollection->AddData(image.GetPointer(),seriesNames.at(i), fileList.at(i).at(k));
139  }
140  else
141  {
142  const std::string s1="", s2="";
143 
144  std::vector<mitk::BaseData::Pointer> fiber_infile = mitk::BaseDataIO::LoadBaseDataFromFile( fileName, s1, s2, false );
145  if( fiber_infile.empty() )
146  {
147  MITK_INFO << "Fiber bundle could not be read " << fileName ;
148  }
149  mitk::BaseData* fiber_baseData = fiber_infile.at(0);
150  subCollection->AddData(fiber_baseData,seriesNames.at(i), fileList.at(i).at(k));
151  }
152  }
153  std::string sDate = GetDate(fileList.at(0).at(k),suffixes.at(0));
154  collection->AddData(subCollection.GetPointer(),sDate,"--");
155  }
156  return collection;
157 }
158 
159 void mitk::CollectionReader::StartElement(const char* elementName, const char **atts)
160 {
161  std::string name(elementName);
162 
163  if (name == COLLECTION)
164  {
165  m_Collection = DataCollection::New();
166  std::string colName = ReadXMLStringAttribut(NAME, atts);
167  m_Collection->SetName(colName);
168  }
169  else if (name == SUBCOLLECTION)
170  {
171  m_ColIgnore = false;
172  m_ItemIgnore = false;
173 
174  std::string subColName = ReadXMLStringAttribut(NAME, atts);
175  std::string subColId = ReadXMLStringAttribut(ID, atts);
176 
177  if (m_SelectedSubColIds.size() > 0 && std::find(m_SelectedSubColIds.begin(), m_SelectedSubColIds.end(), subColId) == m_SelectedSubColIds.end() )
178  { // a) a selection list is provided AND b) the item is not in the list
179  m_ColIgnore = true;
180  return;
181  }
182 
183  // Create subcollection
184  m_SubCollection = DataCollection::New();
185  m_SubCollection->Init(subColName);
186  }
187  else if (name == DATA)
188  {
189  if (m_ColIgnore)
190  return;
191 
192  std::string dataId = ReadXMLStringAttribut(ID, atts);
193  if (m_SelectedDataItemIds.size() > 0 && std::find(m_SelectedDataItemIds.begin(), m_SelectedDataItemIds.end(), dataId) == m_SelectedDataItemIds.end() )
194  { // a) a selection list is provided AND b) the item is not in the list
195  m_ItemIgnore = true;
196  return;
197  }
198  m_ItemIgnore = false;
199  std::string dataName = ReadXMLStringAttribut(NAME, atts);
200 
201  m_DataItemCollection = DataCollection::New();
202  m_DataItemCollection->Init(dataName);
203  }
204  else if (name == ITEM)
205  {
206  if (m_ColIgnore || m_ItemIgnore)
207  return;
208 
209 
210  std::string relativeItemLink = ReadXMLStringAttribut(LINK, atts);
211  std::string itemLink = m_BaseDir + relativeItemLink;
212  std::string itemName = ReadXMLStringAttribut(NAME, atts);
213 
214  // if item names are provided and name is not in list, do not load it
215  if (m_SelectedDataItemNames.size() != 0 && std::find(m_SelectedDataItemNames.begin(), m_SelectedDataItemNames.end(), itemName) == m_SelectedDataItemNames.end() )
216  return;
217 
218  // Populate Sub-Collection
219  if (itemLink.find(".fib") >= itemLink.length())
220  {
221  Image::Pointer image = IOUtil::LoadImage(itemLink);
222  if (image.IsNotNull())
223  m_DataItemCollection->AddData(image.GetPointer(),itemName,relativeItemLink);
224  else
225  MITK_ERROR << "File could not be loaded: " << itemLink << ". Wihtin Sub-Collection " << m_SubCollection->GetName() << ", within " << m_DataItemCollection->GetName() ;
226  }
227  else
228  {
229  const std::string s1="", s2="";
230 
231  std::vector<mitk::BaseData::Pointer> fiber_infile = mitk::BaseDataIO::LoadBaseDataFromFile( itemLink, s1, s2, false );
232  if( fiber_infile.empty() )
233  {
234  MITK_INFO << "Fiber bundle could not be read " << itemLink ;
235  }
236  mitk::BaseData* fiber_baseData = fiber_infile.at(0);
237  m_DataItemCollection->AddData(fiber_baseData,itemName, relativeItemLink);
238  }
239  }
240  else
241  MITK_WARN<< "Malformed description ? -- unknown tag: " << name;
242 }
243 
244 void mitk::CollectionReader::EndElement(const char* elementName)
245 {
246  std::string name(elementName);
247  if (name == SUBCOLLECTION)
248  {
249  if (m_SubCollection.IsNull())
250  return;
251  if (m_ColIgnore || m_SubCollection->Size() == 0)
252  return;
253 
254  m_Collection->AddData(m_SubCollection.GetPointer(),m_SubCollection->GetName());
255  m_SubCollection = DataCollection::New();
256  }
257  if (name == DATA)
258  {
259  if (m_DataItemCollection.IsNull())
260  return;
261  if (m_DataItemCollection->Size() == 0)
262  return;
263 
264  m_SubCollection->AddData(m_DataItemCollection.GetPointer(),m_DataItemCollection->GetName());
265  m_DataItemCollection = DataCollection::New();
266  }
267 }
268 
269 std::string mitk::CollectionReader::ReadXMLStringAttribut(std::string name, const char** atts)
270 {
271  if (atts)
272  {
273  const char** attsIter = atts;
274 
275  while (*attsIter)
276  {
277  if (name == *attsIter)
278  {
279  attsIter++;
280  return *attsIter;
281  }
282  attsIter++;
283  attsIter++;
284  }
285  }
286  return std::string();
287 }
288 
289 bool mitk::CollectionReader::ReadXMLBooleanAttribut(std::string name, const char** atts)
290 {
291  std::string s = ReadXMLStringAttribut(name, atts);
292  std::transform(s.begin(), s.end(), s.begin(), ::toupper);
293  if (s == "TRUE")
294  return true;
295  else
296  return false;
297 }
298 
299 
300 int mitk::CollectionReader::ReadXMLIntegerAttribut(std::string name, const char** atts)
301 {
302  std::string s = ReadXMLStringAttribut(name, atts);
303  return atoi(s.c_str());
304 }
305 
306 
307 
308 mitk::CollectionReader::FileListType mitk::CollectionReader::GenerateFileLists(std::string folder, std::vector<std::string> suffixes, bool allowGaps)
309 {
310  FileListType fileList;
311  QString qFolder = QString::fromStdString(folder);
312  if (!QFileInfo(qFolder).isDir())
313  {
314  MITK_ERROR << "Folder does not exist.";
315  return fileList;
316  }
317  // Add vector for each suffix
318  for (unsigned int i=0; i< suffixes.size(); ++i)
319  {
320  std::vector<std::string> list;
321  fileList.push_back(list);
322  }
323 
324  // if gaps are allowed, file names are build up from reference file (first suffix)
325  // else all lists a file, file by file with regular sorting of the files,
326  // if one suffix has more/less images than the others loading is aborted
327  if (allowGaps)
328  {
329  QDir parseDir;
330  parseDir.setFilter(QDir::Files);
331  parseDir.setPath(qFolder);
332  QStringList filterMorph;
333  filterMorph << "*" + QString::fromStdString( suffixes.at(0) );
334  parseDir.setNameFilters( filterMorph );
335 
336  QFileInfoList qFileList = parseDir.entryInfoList();
337 
338  // now populate lists with files names, non-existing files will be marked with an empty string
339  for (int i = 0; i < qFileList.size(); ++i)
340  {
341  std::string baseFileName = qFileList.at(i).absoluteFilePath().toStdString();
342  fileList.at(0).push_back( baseFileName );
343 
344  //check for different suffixes
345  for (unsigned int suffNo=1; suffNo < suffixes.size(); ++suffNo)
346  {
347  std::string derivedFileName = baseFileName.substr(0,baseFileName.length() -suffixes.at(0).length()) + suffixes.at(suffNo);
348 
349  // checking if file exists
350  if (QFileInfo(QString::fromStdString(derivedFileName)).isFile())
351  fileList.at(suffNo).push_back(derivedFileName);
352  else
353  fileList.at(suffNo).push_back("");
354  }
355  }
356  }
357  else
358  {
359  int numberOfFiles=-1;
360  for (unsigned int i=0; i< suffixes.size(); ++i)
361  {
362  QDir parseDir;
363  parseDir.setFilter(QDir::Files);
364  parseDir.setPath(qFolder);
365  QStringList filterMorph;
366  filterMorph << "*" + QString::fromStdString( suffixes.at(i) );
367  parseDir.setNameFilters( filterMorph );
368 
369  QFileInfoList qFileList = parseDir.entryInfoList();
370  if (numberOfFiles == -1)
371  numberOfFiles = qFileList.size();
372 
373  if (numberOfFiles != qFileList.size() )
374  {
375  MITK_ERROR << "Series contain different number of images. Loading aborting.";
376  fileList.clear();
377  break;
378  }
379 
380  for (int fileNo=0; fileNo<qFileList.size(); ++fileNo)
381  {
382  fileList.at(i).push_back(qFileList.at(fileNo).absoluteFilePath().toStdString());
383  }
384  }
385  }
386  return fileList;
387 }
388 
390 {
391  std::vector<int> indexRemoval;
392  // Parse through all items and check for empty strings, if one occurs mark this index
393  // for removal.
394  int modalities = list.size();
395  int timeSteps = list.at(0).size();
396  MITK_INFO << "Modalities " << modalities;
397  MITK_INFO << "TimeSteps " << timeSteps;
398  if (timeSteps == 0)
399  MITK_ERROR << "No files found. Fatal.";
400  for (int listIndex = 0 ; listIndex < timeSteps; listIndex++)
401  {
402  for (int modalityIndex = 0 ; modalityIndex < modalities; modalityIndex++)
403  {
404  if (list.at(modalityIndex).at(listIndex) == "")
405  {
406  MITK_INFO << "Marked Index " << listIndex << " for removal.";
407  indexRemoval.push_back(listIndex);
408  break;
409  }
410  }
411  }
412 
413 
414  for (int listIndex = indexRemoval.size()-1 ; listIndex >= 0; --listIndex)
415  {
416  for (int i = 0 ; i < modalities; i++)
417  {
418  list.at(i).erase(list.at(i).begin()+indexRemoval.at(listIndex)) ;
419  }
420  }
421  MITK_INFO << "Time Steps after sanitizing: " << list.at(0).size();
422  return list;
423 }
DataCollection::Pointer LoadCollection(const std::string &xmlFileName)
Build up a mitk::DataCollection from a XML resource.
#define MITK_INFO
Definition: mitkLogMacros.h:22
void SetDataItemNames(std::vector< std::string > itemNames)
const std::string COLLECTION
Base of all data objects.
Definition: mitkBaseData.h:39
static FileListType GenerateFileLists(std::string folder, std::vector< std::string > suffixes, bool allowGaps=false)
GenerateFileLists Returns a collection of lists with valid files names in a folder.
#define MITK_ERROR
Definition: mitkLogMacros.h:24
static FileListType SanitizeFileList(FileListType list)
SanitizeFileList Removes all entries that are lacking at least one modality.
const std::string SUBCOLLECTION
std::vector< std::vector< std::string > > FileListType
#define MITK_WARN
Definition: mitkLogMacros.h:23
void EndElement(const char *elementName)
Derived from XMLReader.
const std::string DATA
const std::string ITEM
const std::string ID
void StartElement(const char *elementName, const char **atts)
Derived from XMLReader.
void AddSubColIds(std::vector< std::string > subColIds)
void AddDataElementIds(std::vector< std::string > dataElemetIds)
static std::vector< mitk::BaseData::Pointer > LoadBaseDataFromFile(const std::string path, const std::string filePrefix, const std::string filePattern, bool series)
static std::string GetDate(std::string fileName, std::string suffix)
const std::string FILEPATH
const std::string NAME
static std::string GetName(std::string fileName, std::string suffix)
static DataCollection::Pointer FolderToCollection(std::string folder, std::vector< std::string > suffixes, std::vector< std::string > seriesNames, bool allowGaps)
Build up a mitk::DataCollection from a folder providing suffixes to group the files.
const std::string LINK
itk::SmartPointer< Self > Pointer
Definition: mitkBaseData.h:42
static mitk::Image::Pointer LoadImage(const std::string &path)
LoadImage Convenience method to load an arbitrary mitkImage.
Definition: mitkIOUtil.cpp:597
static itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor Pointer New()
Basic interaction methods for mitk::GeometryData.