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