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