CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
55 
56 typedef vtkSmartPointer<vtkImageAppend> vtkImageAppendPtr;
57 
58 namespace cx
59 {
60 
62  mSessionDescription(sessionDescription)
63 {
64 }
65 
67 {
68 }
69 
71 {
72  return mReconstructData;
73 }
74 
80  std::vector<double> imageTimestamps,
81  TimedTransformMap trackerRecordedData,
82  ToolPtr tool,
83  bool writeColor, Transform3D rMpr)
84 {
85  if(trackerRecordedData.empty())
86  reportWarning("No tracking data for writing to reconstruction file.");
87 
89 
90  retval.mFilename = mSessionDescription; // not saved yet - no filename
91  retval.mUsRaw = USFrameData::create(mSessionDescription, imageData);
92  retval.rMpr = rMpr;
93 
94  for (TimedTransformMap::iterator it = trackerRecordedData.begin(); it != trackerRecordedData.end(); ++it)
95  {
96  TimedPosition current;
97  current.mTime = it->first;
98  current.mPos = it->second;
99  retval.mPositions.push_back(current);
100  }
101 
102  std::vector<double> fts = imageTimestamps;
103  for (unsigned i=0; i<fts.size(); ++i)
104  {
105  TimedPosition current;
106  current.mTime = fts[i];
107  current.mPos = Transform3D::Identity();
108  // current.mPos = not written - will be found from track positions during reconstruction.
109  retval.mFrames.push_back(current);
110  }
111 
112  if (tool && tool->getProbe())
113  {
114  retval.mProbeData.setData(tool->getProbe()->getProbeData());
115  }
116 
117  if (tool)
118  retval.mProbeUid = tool->getUid();
119 
120  this->fillFramePositions(&retval);
121 
122  return retval;
123 }
124 
129 void UsReconstructionFileMaker::fillFramePositions(USReconstructInputData* data) const
130 {
133 }
134 
135 bool UsReconstructionFileMaker::writeTrackerTimestamps(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
136 {
137  bool success = false;
138 
139  QFile file(reconstructionFolder+"/"+session+".tts");
140  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
141  {
142  reportError("Cannot open "+file.fileName());
143  return success;
144  }
145  QTextStream stream(&file);
146 
147  for (unsigned i=0; i<ts.size(); ++i)
148  {
149  stream << qstring_cast(ts[i].mTime);
150  stream << endl;
151  }
152 
153  file.close();
154  success = true;
155 
156  QFileInfo info(file);
157  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" tracking timestamps.";
158 
159  return success;
160 }
161 
162 bool UsReconstructionFileMaker::writeUSTransforms(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
163 {
164  return this->writeTransforms(reconstructionFolder+"/"+session+".fp", ts, "frame transforms rMu");
165 }
166 
167 bool UsReconstructionFileMaker::writeTrackerTransforms(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
168 {
169  return this->writeTransforms(reconstructionFolder+"/"+session+".tp", ts, "tracking transforms prMt");
170 }
171 
172 bool UsReconstructionFileMaker::writeTransforms(QString filename, std::vector<TimedPosition> ts, QString type)
173 {
174  bool success = false;
175  QFile file(filename);
176  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
177  {
178  reportError("Cannot open "+file.fileName());
179  return success;
180  }
181  QTextStream stream(&file);
182 
183  for (unsigned i=0; i<ts.size(); ++i)
184  {
185  Transform3D transform = ts[i].mPos;
186  stream << transform(0,0) << " ";
187  stream << transform(0,1) << " ";
188  stream << transform(0,2) << " ";
189  stream << transform(0,3);
190  stream << endl;
191  stream << transform(1,0) << " ";
192  stream << transform(1,1) << " ";
193  stream << transform(1,2) << " ";
194  stream << transform(1,3);
195  stream << endl;
196  stream << transform(2,0) << " ";
197  stream << transform(2,1) << " ";
198  stream << transform(2,2) << " ";
199  stream << transform(2,3);
200  stream << endl;
201  }
202  file.close();
203  success = true;
204 
205  QFileInfo info(file);
206  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" " + type + ".";
207 
208  return success;
209 }
210 
211 bool UsReconstructionFileMaker::writeUSTimestamps(QString reconstructionFolder, QString session, std::vector<TimedPosition> ts)
212 {
213  bool success = false;
214 
215  QFile file(reconstructionFolder+"/"+session+".fts");
216  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
217  {
218  reportError("Cannot open "+file.fileName());
219  return success;
220  }
221  QTextStream stream(&file);
222 
223  for (unsigned i=0; i<ts.size(); ++i)
224  {
225  stream << qstring_cast(ts[i].mTime);
226  stream << endl;
227  }
228  file.close();
229  success = true;
230 
231  QFileInfo info(file);
232  mReport << info.fileName()+", "+qstring_cast(info.size())+" bytes, "+qstring_cast(ts.size())+" frame timestamps.";
233 
234  return success;
235 }
236 
240 void UsReconstructionFileMaker::writeProbeConfiguration(QString reconstructionFolder, QString session, ProbeDefinition data, QString uid)
241 {
242  XmlOptionFile file = XmlOptionFile(reconstructionFolder + "/" + session + ".probedata.xml");
243  data.addXml(file.getElement("configuration"));
244  file.getElement("tool").toElement().setAttribute("toolID", uid);
245  file.save();
246 }
247 
248 QString UsReconstructionFileMaker::createUniqueFolder(QString patientFolder, QString sessionDescription)
249 {
250  QString retval("");
251  QDir patientDir(patientFolder + "/US_Acq");
252 
253  QString subfolder = sessionDescription;
254  QString subfolderAbsolutePath = patientDir.absolutePath()+"/"+subfolder;
255  QString newPathName = subfolderAbsolutePath;
256  int i=1;
257  while(!findNewSubfolder(newPathName))
258  {
259  newPathName = subfolderAbsolutePath+"_"+QString::number(i++);
260  }
261  patientDir.mkpath(newPathName);
262  patientDir.cd(newPathName);
263 
264  retval = patientDir.absolutePath();
265  return retval;
266 }
267 
268 QString UsReconstructionFileMaker::createFolder(QString patientFolder, QString sessionDescription)
269 {
270  QString retval("");
271  QDir patientDir(patientFolder + "/US_Acq");
272 
273  QString subfolder = sessionDescription;
274  QString subfolderAbsolutePath = patientDir.absolutePath()+"/"+subfolder;
275  QString newPathName = subfolderAbsolutePath;
276  patientDir.mkpath(newPathName);
277  patientDir.cd(newPathName);
278 
279  retval = patientDir.absolutePath();
280  return retval;
281 }
282 
283 bool UsReconstructionFileMaker::findNewSubfolder(QString subfolderAbsolutePath)
284 {
285  QDir dir;
286  if(dir.exists(subfolderAbsolutePath))
287  return false;
288 
289  return true;
290 }
291 
292 void UsReconstructionFileMaker::report()
293 {
294  foreach(QString string, mReport)
295  {
296  reportSuccess(string);
297  }
298 }
299 
300 void UsReconstructionFileMaker::writeUSImages(QString path, ImageDataContainerPtr images, bool compression, std::vector<TimedPosition> pos)
301 {
302  CX_ASSERT(images->size()==pos.size());
303  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
304 
305  for (unsigned i=0; i<images->size(); ++i)
306  {
307  vtkImageDataPtr currentImage = images->get(i);
308  QString filename = QString("%1/%2_%3.mhd").arg(path).arg(mSessionDescription).arg(i);
309 
310  writer->SetInputData(currentImage);
311  writer->SetFileName(cstring_cast(filename));
312  writer->SetCompression(compression);
313  {
314  StaticMutexVtkLocker lock;
315  writer->Write();
316  }
317 
318  CustomMetaImagePtr customReader = CustomMetaImage::create(filename);
319  customReader->setTransform(pos[i].mPos);
320  customReader->setModality("US");
321  customReader->setImageType(mSessionDescription);
322  }
323 }
324 
325 void UsReconstructionFileMaker::writeMask(QString path, QString session, vtkImageDataPtr mask)
326 {
327  QString filename = QString("%1/%2.mask.mhd").arg(path).arg(session);
328  if (!mask)
329  {
330  reportWarning(QString("No mask found, ignoring write to %1").arg(filename));
331  return;
332  }
333 
334  vtkMetaImageWriterPtr writer = vtkMetaImageWriterPtr::New();
335  writer->SetInputData(mask);
336  writer->SetFileName(cstring_cast(filename));
337  writer->SetCompression(false);
338  writer->Write();
339 }
340 
341 
342 void UsReconstructionFileMaker::writeREADMEFile(QString reconstructionFolder, QString session)
343 {
344  QString text = ""
345 "* ==== Format description \n"
346 "* \n"
347 "* All files describing one acquisition lie the same folder. The files all have \n"
348 "* the name format US-Acq_<index>_<TS><stream>.<type>, \n"
349 "* where \n"
350 "* - <index> is a running index, for convenience. \n"
351 "* - <TS> is a timestamp \n"
352 "* - <stream> is the uid of the video stream. Not used prior to cx3.5.0. \n"
353 "* - <type> is the format of that specific file. \n"
354 "* \n"
355 "* Together, the files contains information about the us images and their \n"
356 "* timestamps, the tracking positions and their timestamps, and the probe \n"
357 "* calibration. \n"
358 "* \n"
359 "* In the following, we use <filebase> = US-Acq_<index>_<TS><stream>. \n"
360 "* \n"
361 "* \n"
362 "* ==== <filebase>.mhd (obsolete) \n"
363 "* \n"
364 "* Used prior to version cx3.4.0. \n"
365 "* A file in the metaheader file format containing the uncompressed image data. \n"
366 "* the z-direction is the time axis, i.e. the z dim is the number of us frames. \n"
367 "* See http://www.itk.org/Wiki/MetaIO/Documentation for more. \n"
368 "* \n"
369 "* Two extra tags are added: \n"
370 "* \n"
371 "ConfigurationID = <path:inside:ProbeCalibConfigs.xml> \n"
372 "ProbeCalibration = <not used> \n"
373 "* \n"
374 "* The ConfigurationID refers to a specific configuration within \n"
375 "* ProbeCalibConfigs.xml, using colon separators. \n"
376 "* \n"
377 "* ==== <filebase>_<index>.mhd \n"
378 "* \n"
379 "* A sequence of files in the metaheader file format containing the image data, one file\n"
380 "* for each frame. The frame index is given by the index in the file name. \n"
381 "* See http://www.itk.org/Wiki/MetaIO/Documentation for more. \n"
382 "* Replaces single image files. \n"
383 "* \n"
384 "* ==== ProbeCalibConfigs.xml (obsolete) \n"
385 "* \n"
386 "* This file contains the probe definition, and is copied from the \n"
387 "* config/tool/Tools folder \n"
388 "* \n"
389 "* ==== <filebase>.probedata.xml \n"
390 "* \n"
391 "* This file contains the probe definition. Replaces ProbeCalibConfigs.xml. \n"
392 "* \n"
393 "* ==== <filebase>.fts \n"
394 "* \n"
395 "* This file contains the frame timestamps. This is a sequence of \n"
396 "* newline-separated floating-point numbers in milliceconds. The starting point \n"
397 "* is irrelevant. The number of timestamps must equal the number of us frames. \n"
398 " \n"
399 "* \n"
400 "* ==== <filebase>.tp \n"
401 "* \n"
402 "* This file contains the tracking positions. This is a newline-separated \n"
403 "* sequence of matrices, one for each tracking sample. Each matrix is the prMt, \n"
404 "* i.e. the transform from tool to patient reference. \n"
405 "* The last line of the matrix (always containing 0 0 0 1) is omitted. The matrix \n"
406 "* numbers is whitespace-separated with newline between rows. Thus the number of \n"
407 "* lines in this file is (# tracking positions) x 3. \n"
408 "* \n"
409 "* \n"
410 "* ==== <filebase>.tts \n"
411 "* \n"
412 "* This file contains the tracking timestamps. The format equals .fts , \n"
413 "* but the number of timestamps equals the number of tracking positions. \n"
414 "* \n"
415 "* ==== <filebase>.fp \n"
416 "* \n"
417 "* This file contains the frame positions. This is a newline-separated \n"
418 "* sequence of matrices, one for each US frame. Each matrix is the rMu, \n"
419 "* i.e. the transform from lower-left centered image space to \n"
420 "* global reference. \n"
421 "* The last line of the matrix (always containing 0 0 0 1) is omitted. The matrix \n"
422 "* numbers is whitespace-separated with newline between rows. Thus the number of \n"
423 "* lines in this file is (# tracking positions) x 3. \n"
424 "* \n"
425 "* ==== <filebase>.mask.mhd \n"
426 "* \n"
427 "* This file contains the image mask. The binary image shows what parts \n"
428 "* of the frame images contain valid US data. This file is only written,\n"
429 "* not read. It can be constructed from the probe data. \n"
430 "* \n"
431 "* \n"
432 "*/ \n";
433 
434  QFile file(reconstructionFolder+"/"+session+".README.txt");
435  if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
436  {
437  reportError("Cannot open "+file.fileName());
438  return;
439  }
440  QTextStream stream(&file);
441  stream << text;
442  file.close();
443 }
444 
445 QString UsReconstructionFileMaker::writeToNewFolder(QString path, bool compression)
446 {
447  TimeKeeper timer;
448  mReconstructData.mFilename = path+"/"+mSessionDescription+".fts"; // use fts since this is a single unique file.
449 
450  mReport.clear();
451  mReport << "Made reconstruction folder: " + path;
452  QString session = mSessionDescription;
453 
454  this->writeTrackerTimestamps(path, session, mReconstructData.mPositions);
455  this->writeTrackerTransforms(path, session, mReconstructData.mPositions);
456  this->writeUSTimestamps(path, session, mReconstructData.mFrames);
457  this->writeUSTransforms(path, session, mReconstructData.mFrames);
458  this->writeProbeConfiguration(path, session, mReconstructData.mProbeData.mData, mReconstructData.mProbeUid);
459  this->writeMask(path, session, mReconstructData.getMask());
460  this->writeREADMEFile(path, session);
461 
462  ImageDataContainerPtr imageData = mReconstructData.mUsRaw->getImageContainer();
463  if (imageData)
464  this->writeUSImages(path, imageData, compression, mReconstructData.mFrames);
465  else
466  mReport << "failed to find frame data, save failed.";
467 
468  int time = std::max(1, timer.getElapsedms());
469  mReport << QString("Completed save to %1. Spent %2s, %3fps").arg(mSessionDescription).arg(time/1000).arg(imageData->size()*1000/time);
470 
471  this->report();
472  mReport.clear();
473 
474  return mReconstructData.mFilename;
475 }
476 
477 
478 
479 }//namespace cx
QString qstring_cast(const T &val)
DoubleBoundingBox3D transform(const Transform3D &m, const DoubleBoundingBox3D &bb)
void reportError(QString msg)
Definition: cxLogger.cpp:92
UsReconstructionFileMaker(QString sessionDescription)
#define CX_ASSERT(statement)
Definition: cxLogger.h:128
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)
static std::vector< double > interpolateFramePositionsFromTracking(USReconstructInputData *data)
boost::shared_ptr< class CustomMetaImage > CustomMetaImagePtr
Transform3D rMpr
patient registration
int getElapsedms() const
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
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
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)
std::map< double, Transform3D > TimedTransformMap
static CustomMetaImagePtr create(QString filename)
void setData(ProbeDefinition data)
boost::shared_ptr< class ImageDataContainer > ImageDataContainerPtr
Definition: cxUSFrameData.h:45
boost::shared_ptr< class Tool > ToolPtr