CustusX  2023.01.05-dev+develop.0da12
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) SINTEF Department of Medical Technology.
5 All rights reserved.
6 
7 CustusX is released under a BSD 3-Clause license.
8 
9 See Lisence.txt (https://github.com/SINTEFMedtek/CustusX/blob/master/License.txt) for details.
10 =========================================================================*/
11 
13 
14 #include <QTextStream>
15 #include <QDir>
16 #include <QFile>
17 #include <QFileInfo>
18 #include <vtkImageChangeInformation.h>
19 #include <vtkImageData.h>
20 #include "vtkImageAppend.h"
21 #include "vtkMetaImageWriter.h"
22 #include "cxTypeConversions.h"
23 #include "cxLogger.h"
24 #include "cxSettings.h"
25 #include "cxXmlOptionItem.h"
26 #include "cxTimeKeeper.h"
27 #include "cxUSFrameData.h"
28 #include "cxSavingVideoRecorder.h"
29 #include "cxImageDataContainer.h"
31 #include "cxCustomMetaImage.h"
32 #include "cxErrorObserver.h"
33 
34 
35 typedef vtkSmartPointer<vtkImageAppend> vtkImageAppendPtr;
36 
37 namespace cx
38 {
39 
41  mSessionDescription(sessionDescription)
42 {
43 }
44 
46 {
47 }
48 
50 {
51  return mReconstructData;
52 }
53 
59  std::vector<TimeInfo> imageTimestamps,
60  TimedTransformMap trackerRecordedData,
61  std::map<double, ToolPositionMetadata> trackerRecordedMetadata,
62  std::map<double, ToolPositionMetadata> referenceRecordedMetadata,
63  ToolPtr tool, QString streamUid,
64  bool writeColor, Transform3D rMpr)
65 {
66  if(trackerRecordedData.empty())
67  reportWarning("No tracking data for writing to reconstruction file.");
68 
70 
71  retval.mFilename = mSessionDescription; // not saved yet - no filename
72  retval.mUsRaw = USFrameData::create(mSessionDescription, imageData);
73  retval.rMpr = rMpr;
74  retval.mTrackerRecordedMetadata = trackerRecordedMetadata;
75  retval.mReferenceRecordedMetadata = referenceRecordedMetadata;
76 
77  for (TimedTransformMap::iterator it = trackerRecordedData.begin(); it != trackerRecordedData.end(); ++it)
78  {
79  TimedPosition current;
80  current.mTime = it->first;
81  current.mTimeInfo.setAcquisitionTime(it->first);
82  current.mPos = it->second;
83  retval.mPositions.push_back(current);
84  }
85 
86  std::vector<TimeInfo> fts = imageTimestamps;
87  for (unsigned i=0; i<fts.size(); ++i)
88  {
89  TimedPosition current;
90  current.mTimeInfo = fts[i];
91  current.mTime = current.mTimeInfo.getAcquisitionTime();
92  current.mPos = Transform3D::Identity();
93  // current.mPos = not written - will be found from track positions during reconstruction.
94  retval.mFrames.push_back(current);
95  }
96 
97  if (tool && tool->getProbe())
98  {
99  retval.mProbeDefinition.setData(tool->getProbe()->getProbeDefinition(streamUid));
100  }
101 
102  if (tool)
103  retval.mProbeUid = tool->getUid();
104 
105  this->fillFramePositions(&retval);
106 
107  return retval;
108 }
109 
114 void UsReconstructionFileMaker::fillFramePositions(USReconstructInputData* data) const
115 {
118 }
119 
120 bool UsReconstructionFileMaker::writeTrackerTimestamps(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
121 {
122  return this->writeTimestamps(reconstructionFolder+"/"+session+".tts",
123  ts, "tracking timestamps");
124 }
125 
126 bool UsReconstructionFileMaker::writeUSTimestamps(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
127 {
128  bool retval = true;
129 
130  retval = this->writeTimestamps(reconstructionFolder+"/"+session+".fts",
131  ts, "frame timestamps", Modified);
132 
133  retval &= this->writeTimestamps(reconstructionFolder+"/"+session+".scanner.frameTimestamps",
134  ts, "frame scanner timestamps", Scanner);
135 
136  retval &= this->writeTimestamps(reconstructionFolder+"/"+session+".softwareArrive.frameTimestamps",
137  ts, "frame arrive in software timestamps", SoftwareArrive);
138  return retval;
139 }
140 
141 bool UsReconstructionFileMaker::writeTimestamps(QString filename, std::vector<TimedPosition> ts, QString type, TimeStampType timeStampType)
142 {
143  bool success = false;
144 
145  QFile file(filename);
146  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
147  {
148  reportError("Cannot open "+file.fileName());
149  return success;
150  }
151  QTextStream stream(&file);
152 
153  for (unsigned i=0; i<ts.size(); ++i)
154  {
155  switch(timeStampType)
156  {
157  case Modified:
158  stream << qstring_cast(ts[i].mTimeInfo.getAcquisitionTime());
159  break;
160  case SoftwareArrive:
161  stream << qstring_cast(ts[i].mTimeInfo.getSoftwareAcquisitionTime());
162  break;
163  case Scanner:
164  stream << qstring_cast(ts[i].mTimeInfo.getScannerAcquisitionTime());
165  break;
166  default:
167  stream << qstring_cast(ts[i].mTime); // Is the same as mTimeInfo.getAcquisitionTime()
168  break;
169  }
170  stream << endl;
171  }
172  file.close();
173  success = true;
174 
175  QFileInfo info(file);
176  mReport << QString("%1, %2 bytes, %3 %4.")
177  .arg(info.fileName())
178  .arg(info.size())
179  .arg(ts.size())
180  .arg(type);
181 // mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" frame timestamps.";
182 
183  return success;
184 }
185 
186 bool UsReconstructionFileMaker::writeUSTransforms(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
187 {
188  return this->writeTransforms(reconstructionFolder+"/"+session+".fp", ts, "frame transforms rMu");
189 }
190 
191 bool UsReconstructionFileMaker::writeTrackerMetadata(QString reconstructionFolder, QString session, const std::map<double, ToolPositionMetadata>& ts)
192 {
193  QString filename = reconstructionFolder+"/"+session+".probe.toolmeta";
194  return this->writeMetadata(filename, ts, "tracker metadata");
195 }
196 
197 bool UsReconstructionFileMaker::writeReferenceMetadata(QString reconstructionFolder, QString session, const std::map<double, ToolPositionMetadata>& ts)
198 {
199  QString filename = reconstructionFolder+"/"+session+".ref.toolmeta";
200  return this->writeMetadata(filename, ts, "tracker metadata");
201 }
202 
203 bool UsReconstructionFileMaker::writeMetadata(QString filename, const std::map<double, ToolPositionMetadata>& ts, QString type)
204 {
205  bool success = false;
206  QFile file(filename);
207  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
208  {
209  reportError("Cannot open "+file.fileName());
210  return success;
211  }
212  QTextStream stream(&file);
213 
214  for (std::map<double, ToolPositionMetadata>::const_iterator i=ts.begin(); i!=ts.end(); ++i)
215  {
216  stream << i->second.toString() << endl;
217  }
218  file.close();
219  success = true;
220 
221  QFileInfo info(file);
222  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" " + type + ".";
223 
224  return success;
225 }
226 
227 bool UsReconstructionFileMaker::writeTrackerTransforms(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
228 {
229  return this->writeTransforms(reconstructionFolder+"/"+session+".tp", ts, "tracking transforms prMt");
230 }
231 
232 bool UsReconstructionFileMaker::writeTransforms(QString filename, std::vector<TimedPosition> ts, QString type)
233 {
234  bool success = false;
235  QFile file(filename);
236  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
237  {
238  reportError("Cannot open "+file.fileName());
239  return success;
240  }
241  QTextStream stream(&file);
242 
243  for (unsigned i=0; i<ts.size(); ++i)
244  {
245  Transform3D transform = ts[i].mPos;
246  stream << transform(0,0) << " ";
247  stream << transform(0,1) << " ";
248  stream << transform(0,2) << " ";
249  stream << transform(0,3);
250  stream << endl;
251  stream << transform(1,0) << " ";
252  stream << transform(1,1) << " ";
253  stream << transform(1,2) << " ";
254  stream << transform(1,3);
255  stream << endl;
256  stream << transform(2,0) << " ";
257  stream << transform(2,1) << " ";
258  stream << transform(2,2) << " ";
259  stream << transform(2,3);
260  stream << endl;
261  }
262  file.close();
263  success = true;
264 
265  QFileInfo info(file);
266  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" " + type + ".";
267 
268  return success;
269 }
270 
274 void UsReconstructionFileMaker::writeProbeConfiguration(QString reconstructionFolder, QString session, ProbeDefinition data, QString uid)
275 {
276  XmlOptionFile file = XmlOptionFile(reconstructionFolder + "/" + session + ".probedata.xml");
277  data.addXml(file.getElement("configuration"));
278  file.getElement("tool").toElement().setAttribute("toolID", uid);
279  file.save();
280 }
281 
282 QString UsReconstructionFileMaker::createUniqueFolder(QString patientFolder, QString sessionDescription)
283 {
284  QString retval("");
285  QDir patientDir(patientFolder + "/US_Acq");
286 
287  QString subfolder = sessionDescription;
288  QString subfolderAbsolutePath = patientDir.absolutePath()+"/"+subfolder;
289  QString newPathName = subfolderAbsolutePath;
290  int i=1;
291  while(!findNewSubfolder(newPathName))
292  {
293  newPathName = subfolderAbsolutePath+"_"+QString::number(i++);
294  }
295  patientDir.mkpath(newPathName);
296  patientDir.cd(newPathName);
297 
298  retval = patientDir.absolutePath();
299  return retval;
300 }
301 
302 QString UsReconstructionFileMaker::createFolder(QString patientFolder, QString sessionDescription)
303 {
304  QString retval("");
305  QDir patientDir(patientFolder + "/US_Acq");
306 
307  QString subfolder = sessionDescription;
308  QString subfolderAbsolutePath = patientDir.absolutePath()+"/"+subfolder;
309  QString newPathName = subfolderAbsolutePath;
310  patientDir.mkpath(newPathName);
311  patientDir.cd(newPathName);
312 
313  retval = patientDir.absolutePath();
314  return retval;
315 }
316 
317 bool UsReconstructionFileMaker::findNewSubfolder(QString subfolderAbsolutePath)
318 {
319  QDir dir;
320  if(dir.exists(subfolderAbsolutePath))
321  return false;
322 
323  return true;
324 }
325 
326 void UsReconstructionFileMaker::report()
327 {
328  foreach(QString string, mReport)
329  {
330  reportSuccess(string);
331  }
332 }
333 
334 void UsReconstructionFileMaker::writeUSImages(QString path, ImageDataContainerPtr images, bool compression, std::vector<TimedPosition> pos)
335 {
336  CX_ASSERT(images->size()==pos.size());
337  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
338 
339  for (unsigned i=0; i<images->size(); ++i)
340  {
341  vtkImageDataPtr currentImage = images->get(i);
342  QString filename = QString("%1/%2_%3.mhd").arg(path).arg(mSessionDescription).arg(i);
343 
344  writer->SetInputData(currentImage);
345  writer->SetFileName(cstring_cast(filename));
346  writer->SetCompression(compression);
347  {
349  writer->Write();
350  }
351 
352  CustomMetaImagePtr customReader = CustomMetaImage::create(filename);
353  customReader->setTransform(pos[i].mPos);
354  customReader->setModality(imUS);
355  customReader->setImageType(convertToImageSubType(mSessionDescription));
356  }
357 }
358 
359 void UsReconstructionFileMaker::writeMask(QString path, QString session, vtkImageDataPtr mask)
360 {
361  QString filename = QString("%1/%2.mask.mhd").arg(path).arg(session);
362  if (!mask)
363  {
364  reportWarning(QString("No mask found, ignoring write to %1").arg(filename));
365  return;
366  }
367 
368  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
369  writer->SetInputData(mask);
370  writer->SetFileName(cstring_cast(filename));
371  writer->SetCompression(false);
372  writer->Write();
373 }
374 
375 
376 void UsReconstructionFileMaker::writeREADMEFile(QString reconstructionFolder, QString session)
377 {
378  QString text = ""
379 "* ==== Format description \n"
380 "* Refer to \n"
381 "* http://custusx.org/uploads/developer_doc/nightly/org_custusx_acquisition.html \n"
382 "* for a description of the files. \n"
383 "*/ \n";
384 
385  QFile file(reconstructionFolder+"/"+session+".README.txt");
386  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
387  {
388  reportError("Cannot open "+file.fileName());
389  return;
390  }
391  QTextStream stream(&file);
392  stream << text;
393  file.close();
394 }
395 
396 QString UsReconstructionFileMaker::writeToNewFolder(QString path, bool compression)
397 {
398  TimeKeeper timer;
399  mReconstructData.mFilename = path+"/"+mSessionDescription+".fts"; // use fts since this is a single unique file.
400 
401  mReport.clear();
402  mReport << "Made reconstruction folder: " + path;
403  QString session = mSessionDescription;
404 
405  this->writeTrackerMetadata(path, session, mReconstructData.mTrackerRecordedMetadata);
406  this->writeReferenceMetadata(path, session, mReconstructData.mReferenceRecordedMetadata);
407  this->writeTrackerTimestamps(path, session, mReconstructData.mPositions);
408  this->writeTrackerTransforms(path, session, mReconstructData.mPositions);
409  this->writeUSTimestamps(path, session, mReconstructData.mFrames);
410  this->writeUSTransforms(path, session, mReconstructData.mFrames);
411  this->writeProbeConfiguration(path, session, mReconstructData.mProbeDefinition.mData, mReconstructData.mProbeUid);
412  this->writeMask(path, session, mReconstructData.getMask());
413  this->writeREADMEFile(path, session);
414 
415  ImageDataContainerPtr imageData = mReconstructData.mUsRaw->getImageContainer();
416  if (imageData)
417  this->writeUSImages(path, imageData, compression, mReconstructData.mFrames);
418  else
419  mReport << "failed to find frame data, save failed.";
420 
421  int time = std::max(1, timer.getElapsedms());
422  mReport << QString("Completed save to %1. Spent %2s, %3fps").arg(mSessionDescription).arg(time/1000).arg(imageData->size()*1000/time);
423 
424  this->report();
425  mReport.clear();
426 
427  return mReconstructData.mFilename;
428 }
429 
430 
431 
432 }//namespace cx
QString qstring_cast(const T &val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
void reportError(QString msg)
Definition: cxLogger.cpp:71
void addXml(QDomNode dataNode) const
std::map< double, ToolPositionMetadata > mTrackerRecordedMetadata
UsReconstructionFileMaker(QString sessionDescription)
#define CX_ASSERT(statement)
Definition: cxLogger.h:116
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:66
int getElapsedms() const
void reportWarning(QString msg)
Definition: cxLogger.cpp:70
double getAcquisitionTime() const
Definition: cxData.h:62
IMAGE_SUBTYPE convertToImageSubType(QString imageTypeSubString)
vtkSmartPointer< class vtkMetaImageWriter > vtkMetaImageWriterPtr
static QString createFolder(QString patientFolder, QString sessionDescription)
void reportSuccess(QString msg)
Definition: cxLogger.cpp:72
std::vector< TimedPosition > mPositions
ProbeDefinition mData
Definition: cxProbeSector.h:54
Definition of characteristics for an Ultrasound Probe Sector.
imUS
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:24
Namespace for all CustusX production code.
boost::shared_ptr< class Tool > ToolPtr