Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkCollectionWriter.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 "mitkCollectionWriter.h"
17 
18 #include <mitkIOUtil.h>
19 
20 #include "mitkImageCast.h"
21 #include "itkNrrdImageIO.h"
22 #include "itkImageFileWriter.h"
23 #include "mitkCoreObjectFactory.h"
24 
25 
26 #include <iostream>
27 #include <fstream>
28 
29 #include <QDir>
30 
31 //XML StateMachine Tags
32 // Objects
33 const std::string COLLECTION = "col";
34 const std::string SUBCOLLECTION = "subcol";
35 const std::string DATA = "data";
36 const std::string ITEM = "item";
37 // Properties
38 const std::string NAME = "name";
39 const std::string ID = "id";
40 const std::string FILEPATH = "filepath";
41 const std::string LINK = "link";
42 
43 
44 static std::string GetName(std::string fileName,std::string suffix, bool longName = false)
45 {
46  fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString();
47  if (longName)
48  return fileName.substr(0,fileName.length() -suffix.length()-11); // 10 = date length
49  else
50  return fileName.substr(0,fileName.length() -suffix.length()-9); // 8 = date length
51 }
52 
53 static std::string GetDate(std::string fileName,std::string suffix, bool longName = false)
54 {
55  fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString();
56  if (longName)
57  fileName = fileName.substr(fileName.length() - suffix.length()-10,10); // 8 = date length
58  else
59  fileName = fileName.substr(fileName.length() - suffix.length()-8,8); // 8 = date length
60  if (!longName)
61  {
62  fileName.insert(6,"-");
63  fileName.insert(4,"-");
64  }
65  return fileName;
66 }
67 
68 
69 
70 
71 bool mitk::CollectionWriter::ExportCollectionToFolder(DataCollection *dataCollection, std::string xmlFile, std::vector<std::string> filter)
72 {
73  // Quick and Dirty: Assumes three level DataCollection
74  QDir fileName = QFileInfo(xmlFile.c_str()).absoluteDir();
75  std::string outputFolder = fileName.path().toStdString() + QDir::separator().toLatin1();
76  QDir baseFolder(outputFolder.c_str());
77  baseFolder.mkpath(outputFolder.c_str());
78 
79  std::ofstream xmlFileStream;
80  xmlFileStream.open (xmlFile.c_str());
81  xmlFileStream << "<!-- MITK - DataCollection - File Version 1.0 --> \n";
82  xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << dataCollection->GetName() << "\" >\n";
83  unsigned int subColId = 0;
84 
85  unsigned int dataId = 0;
86 
87  QDir dir(QString::fromStdString(outputFolder));
88  for (size_t i = 0 ; i < dataCollection->Size(); ++i)
89  {
90  // Write Subcollection tag
91  xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << dataCollection->IndexToName(i) << "\" " << FILEPATH << "=\"" << dataCollection->GetDataFilePath(i) << "\" id=\"Col" << subColId << "\" >\n";
92  // Create Sub-Folder
93  dir.mkpath(QString::fromStdString(dataCollection->IndexToName(i)));
94 
95  // Herein create data folders
96  DataCollection* subCollections = dynamic_cast<DataCollection*> (dataCollection->GetData(i).GetPointer());
97  if (subCollections == nullptr)
98  {
99  MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting";
100  return false;
101  }
102 
103  for (size_t d = 0; d < subCollections->Size(); d++ )
104  {
105  // Create Sub Paths
106  QString subPath = QString::fromStdString(dataCollection->IndexToName(i))+"/"+QString::fromStdString(subCollections->IndexToName(d));
107  dir.mkpath(subPath);
108  xmlFileStream << " <" << DATA << " " << NAME << "=\"" << subCollections->IndexToName(d) << "\" " << FILEPATH << "=\"" << subCollections->GetDataFilePath(d) << "\" id=\"Data" << dataId << "\" >\n";
109 
110  DataCollection* itemCollections = dynamic_cast<DataCollection*> (subCollections->GetData(d).GetPointer());
111  if (itemCollections == nullptr)
112  {
113  MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting";
114  return false;
115  }
116 
117  for (size_t s = 0; s < itemCollections->Size(); s++)
118  {
119  if (filter.size() > 0)
120  {
121  bool isSelected = false;
122  for (size_t f = 0; f < filter.size(); f++)
123  {
124  if (filter.at(f) == itemCollections->IndexToName(s) )
125  {
126  isSelected = true;
127  break;
128  }
129  }
130  if (isSelected == false)
131  continue;
132  }
133 
134  QString fileName = dir.path() + dir.separator() + subPath + dir.separator() + QString::fromStdString(dataCollection->IndexToName(i)) + "_" + QString::fromStdString(subCollections->IndexToName(d)) + "_" + QString::fromStdString(itemCollections->IndexToName(s));
135  try
136  {
137  fileName += ".nrrd";
138  Image::Pointer image = itemCollections->GetMitkImage(s).GetPointer();
139  IOUtil::Save(image, fileName.toStdString());
140  }
141  catch ( const std::exception& e )
142  {
143  MITK_ERROR << "Caught exception: " << e.what();
144  }
145 
146  std::string relativeFilename = baseFolder.relativeFilePath(fileName).toStdString();
147  xmlFileStream << " <" << ITEM << " " << NAME << "=\"" <<itemCollections->IndexToName(s) << "\" " << FILEPATH << "=\"" << "\" " << LINK << "=\"" << relativeFilename << "\" />\n";
148  }
149  xmlFileStream << " </" << DATA << ">\n";
150  dataId++;
151  }
152 
153  xmlFileStream << " </" << SUBCOLLECTION << ">\n";
154  subColId++;
155  }
156  xmlFileStream << "</" << COLLECTION << ">\n";
157  xmlFileStream.flush();
158  xmlFileStream.close();
159  return true;
160 }
161 
163 {
164  std::vector<std::string> mods;
165  return ExportCollectionToFolder(dataCollection,xmlFile, mods);
166 }
167 
168 bool mitk::CollectionWriter::SaveCollection(mitk::DataCollection *dataCollection, std::vector<std::string> filter, std::string xmlFile)
169 {
170  QDir origFilename = QFileInfo(dataCollection->GetXMLFile().c_str()).absoluteDir();
171  QString originalFolder = origFilename.path() + QDir::separator();
172 
173  if (xmlFile == "")
174  xmlFile = dataCollection->GetXMLFile();
175 
176  QDir fileName = QFileInfo(xmlFile.c_str()).absoluteDir();
177  std::string outputFolder = fileName.path().toStdString() + QDir::separator().toLatin1();
178  QDir baseFolder(outputFolder.c_str());
179 
180  std::ofstream xmlFileStream;
181  xmlFileStream.open (xmlFile.c_str());
182  xmlFileStream << "<!-- MITK - DataCollection - File Version 1.0 --> \n";
183  xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << dataCollection->GetName() << "\" >\n";
184  unsigned int subColId = 0;
185 
186  unsigned int dataId = 0;
187 
188  QDir dir(QString::fromStdString(outputFolder));
189  for (size_t i = 0 ; i < dataCollection->Size(); ++i)
190  {
191  // Write Subcollection tag
192  xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << dataCollection->IndexToName(i) << "\" " << FILEPATH << "=\"" << dataCollection->GetDataFilePath(i) << "\" id=\"Col" << subColId << "\" >\n";
193  // Create Sub-Folder
194  dir.mkpath(QString::fromStdString(dataCollection->IndexToName(i)));
195 
196  // Herein create data folders
197  DataCollection* subCollections = dynamic_cast<DataCollection*> (dataCollection->GetData(i).GetPointer());
198  if (subCollections == nullptr)
199  {
200  MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting";
201  return false;
202  }
203 
204  for (size_t d = 0; d < subCollections->Size(); d++ )
205  {
206  // Create Sub Paths
207  QString subPath = QString::fromStdString(dataCollection->IndexToName(i))+"/"+QString::fromStdString(subCollections->IndexToName(d));
208  dir.mkpath(subPath);
209  xmlFileStream << " <" << DATA << " " << NAME << "=\"" << subCollections->IndexToName(d) << "\" " << FILEPATH << "=\"" << subCollections->GetDataFilePath(d) << "\" id=\"Data" << dataId << "\" >\n";
210 
211  DataCollection* itemCollections = dynamic_cast<DataCollection*> (subCollections->GetData(d).GetPointer());
212  if (itemCollections == nullptr)
213  {
214  MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting";
215  return false;
216  }
217 
218  for (size_t s = 0; s < itemCollections->Size(); s++)
219  {
220  if (filter.size() > 0)
221  {
222  bool isSelected = false;
223  for (size_t f = 0; f < filter.size(); f++)
224  {
225  if (filter.at(f) == itemCollections->IndexToName(s) )
226  {
227  isSelected = true;
228  break;
229  }
230  }
231  if (isSelected == false)
232  continue;
233  }
234 
235  QString fileName;
236  bool fullName = false;
237  if (itemCollections->GetDataFilePath(s) != "")
238  {
239  fileName = originalFolder + QString::fromStdString(itemCollections->GetDataFilePath(s));
240  fullName = true;
241  MITK_INFO << "original path: " << itemCollections->GetDataFilePath(s) ;
242  }
243  else
244  fileName = dir.path() + dir.separator() + subPath + dir.separator() + QString::fromStdString(dataCollection->IndexToName(i)) + "_" + QString::fromStdString(subCollections->IndexToName(d)) + "_" + QString::fromStdString(itemCollections->IndexToName(s));
245 
246  try
247  {
248  if (!fullName)
249  fileName += ".nrrd";
250  Image::Pointer image = itemCollections->GetMitkImage(s).GetPointer();
251  IOUtil::Save(image,fileName.toStdString());
252  }
253  catch ( const std::exception& e )
254  {
255  MITK_ERROR << "Caught exception: " << e.what();
256  }
257 
258  std::string relativeFilename =baseFolder.relativeFilePath(fileName).toStdString();
259  xmlFileStream << " <" << ITEM << " " << NAME << "=\"" <<itemCollections->IndexToName(s) << "\" " << FILEPATH << "=\"" << "\" " << LINK << "=\"" << relativeFilename << "\" />\n";
260  }
261  xmlFileStream << " </" << DATA << ">\n";
262  dataId++;
263  }
264 
265  xmlFileStream << " </" << SUBCOLLECTION << ">\n";
266  subColId++;
267  }
268  xmlFileStream << "</" << COLLECTION << ">\n";
269  xmlFileStream.flush();
270  xmlFileStream.close();
271  return true;
272 }
273 
274 bool mitk::CollectionWriter::FolderToXml(std::string folder, std::string collectionType, std::string xmlFile, std::vector<std::string> filter, std::vector<std::string> seriesNames)
275 {
276  // 1) Parse for folders
277 
278  QDir parseDir;
279  parseDir.setFilter( QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
280  parseDir.setPath(QString::fromStdString(folder));
281 
282  QFileInfoList qFileList = parseDir.entryInfoList();
283 
284 
285  std::ofstream xmlFileStream;
286  xmlFileStream.open (xmlFile.c_str());
287  xmlFileStream << "<!-- MITK - DataCollection - File Version 1.0 --> \n";
288  xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << "GEN" << "\" >\n";
289 
290  unsigned int dataId = 0;
291 
292  // now populate lists with files names, non-existing files will be marked with an empty string
293  for (int i = 0; i < qFileList.size(); ++i)
294  {
295  // 2) For Each sub folder construct collectionType sub-folder
296  std::string baseFolder = qFileList.at(i).absoluteFilePath().toStdString() + QDir::separator().toLatin1() + collectionType;
297 
298  MITK_INFO << "Processing : " << baseFolder;
299  if (!QFileInfo(QString::fromStdString(baseFolder)).isDir())
300  {
301  MITK_WARN << "Not a valid folder, skipping.";
302  continue;
303  }
304 
305  // 3) Parse each sub folder and extend XML file
306  // Parse folder and look up all data,
307  // after sanitation only fully available groups are included (that is all suffixes are found)
308 
310  if (fileList.size() <= 0 || fileList.at(0).size() <= 0)
311  continue;
312 
313  // Write Subcollection tag
314  // try to extract date out of filename
315  std::string name = GetName(fileList.at(0).at(0),filter.at(0));
316  xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << name << "\" " << FILEPATH << "=\"\" id=\"Col" << i << "\" >\n";
317 
318 
319  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.
320  {
321  std::string strDate = GetDate(fileList.at(0).at(k),filter.at(0));
322  xmlFileStream << " <" << DATA << " " << NAME << "=\"" << strDate << "\" " << " id=\"Data" << dataId << "\" >\n";
323  dataId++;
324  for (unsigned int i=0; i< filter.size(); ++i)
325  {
326  std::string fileName = fileList.at(i).at(k);
327  xmlFileStream << " <" << ITEM << " " << NAME << "=\"" << seriesNames.at(i) << "\" " << LINK << "=\"" << fileName << "\" />\n";
328  }
329  xmlFileStream << " </" << DATA << ">\n" ;
330  }
331  xmlFileStream << " </" << SUBCOLLECTION << ">\n";
332  }
333 
334  xmlFileStream << "</" << COLLECTION << ">\n";
335  xmlFileStream.flush();
336  xmlFileStream.close();
337 
338  return true;
339 }
340 
341 bool mitk::CollectionWriter::SingleFolderToXml(std::string folder, std::string xmlFile, std::vector<std::string> filter, std::vector<std::string> seriesNames, bool longDate, int skipUntil, float months)
342 {
343  std::ofstream xmlFileStream;
344  xmlFileStream.open (xmlFile.c_str());
345  xmlFileStream << "<!-- MITK - DataCollection - File Version 1.0 --> \n";
346  xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << "GEN" << "\" >\n";
347 
348  unsigned int dataId = 0;
349 
350  // 1)
351  // Parse folder and look up all data,
352  // after sanitation only fully available groups are included (that is all suffixes are found)
353 
355 
356  // Write Subcollection tag
357  // try to extract date out of filename
358  std::string name = GetName(fileList.at(0).at(0),filter.at(0),longDate);
359  xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << name << "\" " << FILEPATH << "=\"\" id=\"Col" << 0 << "\" >\n";
360 
361 
362  for (unsigned int k=skipUntil; k < fileList.at(0).size(); ++k) // all groups have the same amount of items, so looking at 0 is ok.
363  {
364  std::string strDate = GetDate(fileList.at(0).at(k),filter.at(0),true);
365  xmlFileStream << " <" << DATA << " " << NAME << "=\"" << strDate << "\" " << " id=\"Data" << dataId << "\" >\n";
366  dataId++;
367  for (unsigned int i=0; i< filter.size(); ++i)
368  {
369  std::string fileName = fileList.at(i).at(k);
370  xmlFileStream << " <" << ITEM << " " << NAME << "=\"" << seriesNames.at(i) << "\" " << LINK << "=\"" << fileName << "\" />\n";
371  }
372 
373  // size_t ind = GetIndexForinXMonths(fileList,months,k,filter);
374  // xmlFileStream << " <" << ITEM << " " << NAME << "=\"TARGET\" " << LINK << "=\"" << fileList.at(filter.size()-1).at(ind) << "\" />\n";
375 
376  xmlFileStream << " </" << DATA << ">\n" ;
377  // check if target still exists for next step
378  if (GetIndexForinXMonths(fileList,months,k+1,filter)== 0)
379  break;
380  }
381  xmlFileStream << " </" << SUBCOLLECTION << ">\n";
382 
383  xmlFileStream << "</" << COLLECTION << ">\n";
384  xmlFileStream.flush();
385  xmlFileStream.close();
386 
387  return true;
388 }
389 
390 size_t mitk::CollectionWriter::GetIndexForinXMonths(mitk::CollectionReader::FileListType fileList,float months, size_t curIndex,std::vector<std::string> filter)
391 {
392  std::string strDate0 = GetDate(fileList.at(0).at(curIndex),filter.at(0),true);
393 
394  int year0 =std::atoi(strDate0.substr(0,4).c_str());
395  int month0 =std::atoi(strDate0.substr(5,2).c_str());
396  int day0 = std::atoi(strDate0.substr(8,2).c_str());
397 
398  size_t bestIndex = 0;
399  int bestFit = 1e5;
400 
401  for (size_t i=curIndex+1; i < fileList.at(0).size(); ++i)
402  {
403  std::string strDate = GetDate(fileList.at(0).at(i),filter.at(0),true);
404  int year =std::atoi(strDate.substr(0,4).c_str());
405  int month =std::atoi(strDate.substr(5,2).c_str());
406  int day = std::atoi(strDate.substr(8,2).c_str());
407 
408  int fit = std::fabs((months * 30 ) - (((year-year0)*360) +((month-month0)*30) + (day-day0))); // days difference from x months
409  if (fit < bestFit)
410  {
411  bestFit = fit;
412  bestIndex = i;
413  }
414  }
415  return bestIndex;
416 }
vcl_size_t Size() const
Size - number of data items in collection.
float k(1.0)
#define MITK_INFO
Definition: mitkLogMacros.h:18
const std::string NAME
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 bool SaveCollection(DataCollection *dataCollection, std::vector< std::string > filter, std::string xmlFile="")
SaveCollection - Stores data collection at original location.
const std::string SUBCOLLECTION
static FileListType SanitizeFileList(FileListType list)
SanitizeFileList Removes all entries that are lacking at least one modality.
const std::string COLLECTION
mitk::Image::Pointer GetMitkImage(vcl_size_t index)
GetMitkImage - casts data to mitk::Image and returns it.
std::string GetDataFilePath(vcl_size_t index) const
static std::string GetDate(std::string fileName, std::string suffix, bool longName=false)
std::vector< std::vector< std::string > > FileListType
#define MITK_WARN
Definition: mitkLogMacros.h:19
static bool SingleFolderToXml(std::string folder, std::string xmlFile, std::vector< std::string > filter, std::vector< std::string > seriesNames, bool longDate=true, int skipUntil=0, float months=0)
const std::string ID
mitk::Image::Pointer image
static bool ExportCollectionToFolder(DataCollection *dataCollection, std::string xmlFile, std::vector< std::string > filter)
ExportCollectionToFolder.
const std::string FILEPATH
std::string IndexToName(vcl_size_t index) const
IndexToName - Get name from index.
static bool FolderToXml(std::string folder, std::string collectionType, std::string xmlFile, std::vector< std::string > filter, std::vector< std::string > seriesNames)
std::string GetName() const
static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty=false)
Save a mitk::BaseData instance.
Definition: mitkIOUtil.cpp:774
std::string GetXMLFile()
SetXMLFile - gets xml file to which data collection is supposed to be saved.
const std::string LINK
itk::DataObject::Pointer GetData(vcl_size_t index)
GetData Get original data by index.
const std::string DATA
static std::string GetName(std::string fileName, std::string suffix, bool longName=false)
const std::string ITEM