Fraxinus  17.12-rc4
An IGT application
cxUsReconstructionFileMaker.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
34 
35 #include <QTextStream>
36 #include <QDir>
37 #include <QFile>
38 #include <QFileInfo>
39 #include <vtkImageChangeInformation.h>
40 #include <vtkImageData.h>
41 #include "vtkImageAppend.h"
42 #include "vtkMetaImageWriter.h"
43 #include "cxTypeConversions.h"
44 #include "cxLogger.h"
45 #include "cxSettings.h"
46 #include "cxXmlOptionItem.h"
47 #include "cxTimeKeeper.h"
48 #include "cxDataReaderWriter.h"
49 #include "cxUSFrameData.h"
50 #include "cxSavingVideoRecorder.h"
51 #include "cxImageDataContainer.h"
53 #include "cxCustomMetaImage.h"
54 #include "cxErrorObserver.h"
55 
56 
57 typedef vtkSmartPointer<vtkImageAppend> vtkImageAppendPtr;
58 
59 namespace cx
60 {
61 
63  mSessionDescription(sessionDescription)
64 {
65 }
66 
68 {
69 }
70 
72 {
73  return mReconstructData;
74 }
75 
81  std::vector<TimeInfo> imageTimestamps,
82  TimedTransformMap trackerRecordedData,
83  std::map<double, ToolPositionMetadata> trackerRecordedMetadata,
84  std::map<double, ToolPositionMetadata> referenceRecordedMetadata,
85  ToolPtr tool, QString streamUid,
86  bool writeColor, Transform3D rMpr)
87 {
88  if(trackerRecordedData.empty())
89  reportWarning("No tracking data for writing to reconstruction file.");
90 
92 
93  retval.mFilename = mSessionDescription; // not saved yet - no filename
94  retval.mUsRaw = USFrameData::create(mSessionDescription, imageData);
95  retval.rMpr = rMpr;
96  retval.mTrackerRecordedMetadata = trackerRecordedMetadata;
97  retval.mReferenceRecordedMetadata = referenceRecordedMetadata;
98 
99  for (TimedTransformMap::iterator it = trackerRecordedData.begin(); it != trackerRecordedData.end(); ++it)
100  {
101  TimedPosition current;
102  current.mTime = it->first;
103  current.mTimeInfo.setAcquisitionTime(it->first);
104  current.mPos = it->second;
105  retval.mPositions.push_back(current);
106  }
107 
108  std::vector<TimeInfo> fts = imageTimestamps;
109  for (unsigned i=0; i<fts.size(); ++i)
110  {
111  TimedPosition current;
112  current.mTimeInfo = fts[i];
113  current.mTime = current.mTimeInfo.getAcquisitionTime();
114  current.mPos = Transform3D::Identity();
115  // current.mPos = not written - will be found from track positions during reconstruction.
116  retval.mFrames.push_back(current);
117  }
118 
119  if (tool && tool->getProbe())
120  {
121  retval.mProbeDefinition.setData(tool->getProbe()->getProbeDefinition(streamUid));
122  }
123 
124  if (tool)
125  retval.mProbeUid = tool->getUid();
126 
127  this->fillFramePositions(&retval);
128 
129  return retval;
130 }
131 
136 void UsReconstructionFileMaker::fillFramePositions(USReconstructInputData* data) const
137 {
140 }
141 
142 bool UsReconstructionFileMaker::writeTrackerTimestamps(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
143 {
144  return this->writeTimestamps(reconstructionFolder+"/"+session+".tts",
145  ts, "tracking timestamps");
146 }
147 
148 bool UsReconstructionFileMaker::writeUSTimestamps(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
149 {
150  bool retval = true;
151 
152  retval = this->writeTimestamps(reconstructionFolder+"/"+session+".fts",
153  ts, "frame timestamps", Modified);
154 
155  retval &= this->writeTimestamps(reconstructionFolder+"/"+session+".scanner.frameTimestamps",
156  ts, "frame scanner timestamps", Scanner);
157 
158  retval &= this->writeTimestamps(reconstructionFolder+"/"+session+".softwareArrive.frameTimestamps",
159  ts, "frame arrive in software timestamps", SoftwareArrive);
160  return retval;
161 }
162 
163 bool UsReconstructionFileMaker::writeTimestamps(QString filename, std::vector<TimedPosition> ts, QString type, TimeStampType timeStampType)
164 {
165  bool success = false;
166 
167  QFile file(filename);
168  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
169  {
170  reportError("Cannot open "+file.fileName());
171  return success;
172  }
173  QTextStream stream(&file);
174 
175  for (unsigned i=0; i<ts.size(); ++i)
176  {
177  switch(timeStampType)
178  {
179  case Modified:
180  stream << qstring_cast(ts[i].mTimeInfo.getAcquisitionTime());
181  break;
182  case SoftwareArrive:
183  stream << qstring_cast(ts[i].mTimeInfo.getSoftwareAcquisitionTime());
184  break;
185  case Scanner:
186  stream << qstring_cast(ts[i].mTimeInfo.getScannerAcquisitionTime());
187  break;
188  default:
189  stream << qstring_cast(ts[i].mTime); // Is the same as mTimeInfo.getAcquisitionTime()
190  break;
191  }
192  stream << endl;
193  }
194  file.close();
195  success = true;
196 
197  QFileInfo info(file);
198  mReport << QString("%1, %2 bytes, %3 %4.")
199  .arg(info.fileName())
200  .arg(info.size())
201  .arg(ts.size())
202  .arg(type);
203 // mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" frame timestamps.";
204 
205  return success;
206 }
207 
208 bool UsReconstructionFileMaker::writeUSTransforms(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
209 {
210  return this->writeTransforms(reconstructionFolder+"/"+session+".fp", ts, "frame transforms rMu");
211 }
212 
213 bool UsReconstructionFileMaker::writeTrackerMetadata(QString reconstructionFolder, QString session, const std::map<double, ToolPositionMetadata>& ts)
214 {
215  QString filename = reconstructionFolder+"/"+session+".probe.toolmeta";
216  return this->writeMetadata(filename, ts, "tracker metadata");
217 }
218 
219 bool UsReconstructionFileMaker::writeReferenceMetadata(QString reconstructionFolder, QString session, const std::map<double, ToolPositionMetadata>& ts)
220 {
221  QString filename = reconstructionFolder+"/"+session+".ref.toolmeta";
222  return this->writeMetadata(filename, ts, "tracker metadata");
223 }
224 
225 bool UsReconstructionFileMaker::writeMetadata(QString filename, const std::map<double, ToolPositionMetadata>& ts, QString type)
226 {
227  bool success = false;
228  QFile file(filename);
229  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
230  {
231  reportError("Cannot open "+file.fileName());
232  return success;
233  }
234  QTextStream stream(&file);
235 
236  for (std::map<double, ToolPositionMetadata>::const_iterator i=ts.begin(); i!=ts.end(); ++i)
237  {
238  stream << i->second.toString() << endl;
239  }
240  file.close();
241  success = true;
242 
243  QFileInfo info(file);
244  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" " + type + ".";
245 
246  return success;
247 }
248 
249 bool UsReconstructionFileMaker::writeTrackerTransforms(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
250 {
251  return this->writeTransforms(reconstructionFolder+"/"+session+".tp", ts, "tracking transforms prMt");
252 }
253 
254 bool UsReconstructionFileMaker::writeTransforms(QString filename, std::vector<TimedPosition> ts, QString type)
255 {
256  bool success = false;
257  QFile file(filename);
258  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
259  {
260  reportError("Cannot open "+file.fileName());
261  return success;
262  }
263  QTextStream stream(&file);
264 
265  for (unsigned i=0; i<ts.size(); ++i)
266  {
267  Transform3D transform = ts[i].mPos;
268  stream << transform(0,0) << " ";
269  stream << transform(0,1) << " ";
270  stream << transform(0,2) << " ";
271  stream << transform(0,3);
272  stream << endl;
273  stream << transform(1,0) << " ";
274  stream << transform(1,1) << " ";
275  stream << transform(1,2) << " ";
276  stream << transform(1,3);
277  stream << endl;
278  stream << transform(2,0) << " ";
279  stream << transform(2,1) << " ";
280  stream << transform(2,2) << " ";
281  stream << transform(2,3);
282  stream << endl;
283  }
284  file.close();
285  success = true;
286 
287  QFileInfo info(file);
288  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" " + type + ".";
289 
290  return success;
291 }
292 
296 void UsReconstructionFileMaker::writeProbeConfiguration(QString reconstructionFolder, QString session, ProbeDefinition data, QString uid)
297 {
298  XmlOptionFile file = XmlOptionFile(reconstructionFolder + "/" + session + ".probedata.xml");
299  data.addXml(file.getElement("configuration"));
300  file.getElement("tool").toElement().setAttribute("toolID", uid);
301  file.save();
302 }
303 
304 QString UsReconstructionFileMaker::createUniqueFolder(QString patientFolder, QString sessionDescription)
305 {
306  QString retval("");
307  QDir patientDir(patientFolder + "/US_Acq");
308 
309  QString subfolder = sessionDescription;
310  QString subfolderAbsolutePath = patientDir.absolutePath()+"/"+subfolder;
311  QString newPathName = subfolderAbsolutePath;
312  int i=1;
313  while(!findNewSubfolder(newPathName))
314  {
315  newPathName = subfolderAbsolutePath+"_"+QString::number(i++);
316  }
317  patientDir.mkpath(newPathName);
318  patientDir.cd(newPathName);
319 
320  retval = patientDir.absolutePath();
321  return retval;
322 }
323 
324 QString UsReconstructionFileMaker::createFolder(QString patientFolder, QString sessionDescription)
325 {
326  QString retval("");
327  QDir patientDir(patientFolder + "/US_Acq");
328 
329  QString subfolder = sessionDescription;
330  QString subfolderAbsolutePath = patientDir.absolutePath()+"/"+subfolder;
331  QString newPathName = subfolderAbsolutePath;
332  patientDir.mkpath(newPathName);
333  patientDir.cd(newPathName);
334 
335  retval = patientDir.absolutePath();
336  return retval;
337 }
338 
339 bool UsReconstructionFileMaker::findNewSubfolder(QString subfolderAbsolutePath)
340 {
341  QDir dir;
342  if(dir.exists(subfolderAbsolutePath))
343  return false;
344 
345  return true;
346 }
347 
348 void UsReconstructionFileMaker::report()
349 {
350  foreach(QString string, mReport)
351  {
352  reportSuccess(string);
353  }
354 }
355 
356 void UsReconstructionFileMaker::writeUSImages(QString path, ImageDataContainerPtr images, bool compression, std::vector<TimedPosition> pos)
357 {
358  CX_ASSERT(images->size()==pos.size());
359  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
360 
361  for (unsigned i=0; i<images->size(); ++i)
362  {
363  vtkImageDataPtr currentImage = images->get(i);
364  QString filename = QString("%1/%2_%3.mhd").arg(path).arg(mSessionDescription).arg(i);
365 
366  writer->SetInputData(currentImage);
367  writer->SetFileName(cstring_cast(filename));
368  writer->SetCompression(compression);
369  {
371  writer->Write();
372  }
373 
374  CustomMetaImagePtr customReader = CustomMetaImage::create(filename);
375  customReader->setTransform(pos[i].mPos);
376  customReader->setModality("US");
377  customReader->setImageType(mSessionDescription);
378  }
379 }
380 
381 void UsReconstructionFileMaker::writeMask(QString path, QString session, vtkImageDataPtr mask)
382 {
383  QString filename = QString("%1/%2.mask.mhd").arg(path).arg(session);
384  if (!mask)
385  {
386  reportWarning(QString("No mask found, ignoring write to %1").arg(filename));
387  return;
388  }
389 
390  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
391  writer->SetInputData(mask);
392  writer->SetFileName(cstring_cast(filename));
393  writer->SetCompression(false);
394  writer->Write();
395 }
396 
397 
398 void UsReconstructionFileMaker::writeREADMEFile(QString reconstructionFolder, QString session)
399 {
400  QString text = ""
401 "* ==== Format description \n"
402 "* Refer to \n"
403 "* http://custusx.org/uploads/developer_doc/nightly/org_custusx_acquisition.html \n"
404 "* for a description of the files. \n"
405 "*/ \n";
406 
407  QFile file(reconstructionFolder+"/"+session+".README.txt");
408  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
409  {
410  reportError("Cannot open "+file.fileName());
411  return;
412  }
413  QTextStream stream(&file);
414  stream << text;
415  file.close();
416 }
417 
418 QString UsReconstructionFileMaker::writeToNewFolder(QString path, bool compression)
419 {
420  TimeKeeper timer;
421  mReconstructData.mFilename = path+"/"+mSessionDescription+".fts"; // use fts since this is a single unique file.
422 
423  mReport.clear();
424  mReport << "Made reconstruction folder: " + path;
425  QString session = mSessionDescription;
426 
427  this->writeTrackerMetadata(path, session, mReconstructData.mTrackerRecordedMetadata);
428  this->writeReferenceMetadata(path, session, mReconstructData.mReferenceRecordedMetadata);
429  this->writeTrackerTimestamps(path, session, mReconstructData.mPositions);
430  this->writeTrackerTransforms(path, session, mReconstructData.mPositions);
431  this->writeUSTimestamps(path, session, mReconstructData.mFrames);
432  this->writeUSTransforms(path, session, mReconstructData.mFrames);
433  this->writeProbeConfiguration(path, session, mReconstructData.mProbeDefinition.mData, mReconstructData.mProbeUid);
434  this->writeMask(path, session, mReconstructData.getMask());
435  this->writeREADMEFile(path, session);
436 
437  ImageDataContainerPtr imageData = mReconstructData.mUsRaw->getImageContainer();
438  if (imageData)
439  this->writeUSImages(path, imageData, compression, mReconstructData.mFrames);
440  else
441  mReport << "failed to find frame data, save failed.";
442 
443  int time = std::max(1, timer.getElapsedms());
444  mReport << QString("Completed save to %1. Spent %2s, %3fps").arg(mSessionDescription).arg(time/1000).arg(imageData->size()*1000/time);
445 
446  this->report();
447  mReport.clear();
448 
449  return mReconstructData.mFilename;
450 }
451 
452 
453 
454 }//namespace cx
QString qstring_cast(const T &val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
void reportError(QString msg)
Definition: cxLogger.cpp:92
void addXml(QDomNode dataNode) const
std::map< double, ToolPositionMetadata > mTrackerRecordedMetadata
UsReconstructionFileMaker(QString sessionDescription)
#define CX_ASSERT(statement)
Definition: cxLogger.h:137
One position with timestamp.
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
vtkSmartPointer< vtkImageAppend > vtkImageAppendPtr
std::vector< TimedPosition > mFrames
cstring_cast_Placeholder cstring_cast(const T &val)
QString writeToNewFolder(QString path, bool compression)
std::map< double, ToolPositionMetadata > mReferenceRecordedMetadata
QDomElement getElement()
return the current element
static std::vector< double > interpolateFramePositionsFromTracking(USReconstructInputData *data)
boost::shared_ptr< class CustomMetaImage > CustomMetaImagePtr
Transform3D rMpr
patient registration
void setAcquisitionTime(double mSecsSinceEpoch)
Definition: cxData.h:86
int getElapsedms() const
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
double getAcquisitionTime() const
Definition: cxData.h:82
vtkSmartPointer< class vtkMetaImageWriter > vtkMetaImageWriterPtr
static QString createFolder(QString patientFolder, QString sessionDescription)
void reportSuccess(QString msg)
Definition: cxLogger.cpp:93
std::vector< TimedPosition > mPositions
ProbeDefinition mData
Definition: cxProbeSector.h:75
Definition of characteristics for an Ultrasound Probe Sector.
void save()
save entire document.
QString mFilename
filename used for current data read
vtkSmartPointer< class vtkImageData > vtkImageDataPtr
USFrameDataPtr mUsRaw
All imported US data frames with pointers to each frame.
static void transformFramePositionsTo_rMu(USReconstructInputData *data)
static USFrameDataPtr create(ImagePtr inputFrameData)
static QString createUniqueFolder(QString patientFolder, QString sessionDescription)
Helper class for xml files used to store ssc/cx data.
std::map< double, Transform3D > TimedTransformMap
static CustomMetaImagePtr create(QString filename)
void setData(ProbeDefinition data)
boost::shared_ptr< class ImageDataContainer > ImageDataContainerPtr
Definition: cxUSFrameData.h:45
Namespace for all CustusX production code.
boost::shared_ptr< class Tool > ToolPtr