Fraxinus  16.5.0-fx-rc1
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxUsReconstructionFileReader.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 =========================================================================*/
33 
34 #include <QFile>
35 #include <QFileInfo>
36 #include <QStringList>
37 #include <QDataStream>
38 #include "cxLogger.h"
39 #include "cxTypeConversions.h"
40 #include <vtkImageData.h>
41 #include "cxImage.h"
42 #include "cxUtilHelpers.h"
44 #include "cxVolumeHelpers.h"
45 #include "cxUSFrameData.h"
46 
47 namespace cx
48 {
49 
51 {
52 
53 }
54 
56 {
57  if (calFilesPath.isEmpty())
58  {
59  QStringList list = fileName.split("/");
60  list[list.size()-1] = "";
61  calFilesPath = list.join("/")+"/";
62  }
63 
65 
66  // ignore if a directory is read - store folder name only
67  if (QFileInfo(fileName).suffix().isEmpty())
68  return retval;
69 
70  retval.mFilename = fileName;
71 
72  if (!QFileInfo(changeExtension(fileName, "fts")).exists())
73  {
74  // There may not be any files here due to the automatic calling of the function
75  reportWarning("File not found: "+changeExtension(fileName, "fts")+", reconstruct load failed");
76  return retval;
77  }
78 
79  //Read US images
80  retval.mUsRaw = this->readUsDataFile(fileName);
81 
82  std::pair<QString, ProbeDefinition> probeDefinitionFull = this->readProbeDefinitionBackwardsCompatible(changeExtension(fileName, "mhd"), calFilesPath);
83  ProbeDefinition probeDefinition = probeDefinitionFull.second;
84  // override spacing with spacing from image file. This is because the raw spacing from probe calib might have been changed by changing the sound speed.
85  bool spacingOK = similar(probeDefinition.getSpacing()[0], retval.mUsRaw->getSpacing()[0], 0.001)
86  && similar(probeDefinition.getSpacing()[1], retval.mUsRaw->getSpacing()[1], 0.001);
87  if (!spacingOK)
88  {
89  reportWarning(""
90  "Mismatch in spacing values from calibration and recorded image.\n"
91  "This might be valid if the sound speed was changed prior to recording.\n"
92  "Probe definition: "+ qstring_cast(probeDefinition.getSpacing()) + ", Acquired Image: " + qstring_cast(retval.mUsRaw->getSpacing())
93  );
94  }
95  probeDefinition.setSpacing(Vector3D(retval.mUsRaw->getSpacing()));
96  retval.mProbeDefinition.setData(probeDefinition);
97  retval.mProbeUid = probeDefinitionFull.first;
98 
99  retval.mFrames = this->readFrameTimestamps(fileName);
100  retval.mPositions = this->readPositions(fileName);
101 
102  if (!this->valid(retval))
103  {
104  return USReconstructInputData();
105  }
106 
107  //mPos is now prMs
108  if (!retval.mFrames.empty())
109  {
110  double msecs = (retval.mFrames.rbegin()->mTime - retval.mFrames.begin()->mTime);
111  report(QString("Read %1 seconds of us data from %2.").arg(msecs/1000, 0, 'g', 3).arg(fileName));
112  }
113 
114  return retval;
115 }
116 
117 bool UsReconstructionFileReader::valid(USReconstructInputData input)
118 {
119  if (input.mUsRaw->getNumImages() != input.mFrames.size())
120  {
121  reportError("Mismatch between number of images and number of image positions.\n"
122  "Images: " + qstring_cast(input.mUsRaw->getNumImages()) +
123  " image positions: " + qstring_cast(input.mFrames.size()));
124  return false;
125  }
126  return true;
127 }
128 
133 std::pair<QString, ProbeDefinition> UsReconstructionFileReader::readProbeDefinitionBackwardsCompatible(QString mhdFileName, QString calFilesPath)
134 {
135  std::pair<QString, ProbeDefinition> retval = this->readProbeDefinitionFromFile(mhdFileName);
136 
137  if (retval.second.getType()==ProbeDefinition::tNONE)
138  {
139  report(QString("Invalid probe in %1, falling back to old format").arg(retval.first));
140  QString caliFilename;
141  QStringList probeConfigPath;
142  this->readCustomMhdTags(mhdFileName, &probeConfigPath, &caliFilename);
143  ProbeXmlConfigParser::Configuration configuration = this->readProbeConfiguration(calFilesPath, probeConfigPath);
144  retval.second = createProbeDefinitionFromConfiguration(configuration);
145  }
146 
147  return retval;
148 }
149 
150 ImagePtr UsReconstructionFileReader::generateMask(USReconstructInputData data)
151 {
152  Eigen::Array3i dim(data.mUsRaw->getDimensions());
153  dim[2] = 1;
154  Vector3D spacing(data.mUsRaw->getSpacing());
155 
156  vtkImageDataPtr raw = generateVtkImageData(dim, spacing, 255);
157 
158  ImagePtr image = ImagePtr(new Image("mask", raw, "mask")) ;
159  return image;
160 }
161 
162 void UsReconstructionFileReader::readCustomMhdTags(QString mhdFileName, QStringList* probeConfigPath, QString* calFileName)
163 {
164  //Read XML info from mhd file
165  //Stored in ConfigurationID tag
166  QFile file(mhdFileName);
167  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
168  {
169  reportWarning("Error in Reconstructer::readUsDataFile(): Can't open file: "
170  + mhdFileName);
171  }
172  bool foundConfig = false;
173  QStringList& configList = *probeConfigPath;
174  bool foundCalFile = false;
175  while (!file.atEnd())
176  {
177  QString line = file.readLine();
178  if(line.startsWith("ConfigurationID", Qt::CaseInsensitive))
179  {
180  QStringList tempList = line.split("=", QString::SkipEmptyParts);
181  configList = tempList[1].trimmed().split(":", QString::SkipEmptyParts);
182  if (configList.size()>=3)
183  configList[3] = configList[3].trimmed();
184  foundConfig = true;
185  }
186  else if(line.startsWith("ProbeCalibration", Qt::CaseInsensitive))
187  {
188  QStringList list = line.split("=", QString::SkipEmptyParts);
189  *calFileName = list[1].trimmed();
190  foundCalFile = true;
191 // std::cout << "Calibration file used: " << *calFileName << std::endl;
192  }
193  }
194  if(!foundConfig)
195  {
196  reportWarning(QString("Error in Reconstructer::readUsDataFile(): ")
197  + "Can't find ConfigurationID in file: "
198  + mhdFileName);
199  }
200  if(!foundCalFile)
201  {
202  reportWarning(QString("Error in Reconstructer::readUsDataFile(): ")
203  + "Can't find ProbeCalibration in file: "
204  + mhdFileName);
205  }
206 }
207 
208 ProbeXmlConfigParser::Configuration UsReconstructionFileReader::readProbeConfiguration(QString calFilesPath, QStringList probeConfigPath)
209 {
210  if (probeConfigPath.size()!=4)
212  //Assumes ProbeCalibConfigs.xml file and calfiles have the same path
213  QString xmlPath = calFilesPath+"ProbeCalibConfigs.xml";
214  ProbeXmlConfigParserPtr xmlConfigParser(new ProbeXmlConfigParserImpl(xmlPath));
215 
217  configuration = xmlConfigParser->getConfiguration(
218  probeConfigPath[0],
219  probeConfigPath[1],
220  probeConfigPath[2],
221  probeConfigPath[3]);
222 
223  return configuration;
224 }
225 
226 std::pair<QString, ProbeDefinition> UsReconstructionFileReader::readProbeDefinitionFromFile(QString mhdFileName)
227 {
228  std::pair<QString, ProbeDefinition> retval;
229  QString filename = changeExtension(mhdFileName, "probedata.xml");
230 
231  if (!QFileInfo(filename).exists())
232  {
233  reportWarning("File not found: " + filename + ", failed to load probe data.");
234  return retval;
235  }
236 
237  XmlOptionFile file = XmlOptionFile(filename);
238  retval.second.parseXml(file.getElement("configuration"));
239 
240  retval.first = file.getElement("tool").toElement().attribute("toolID");
241 
242  return retval;
243 }
244 
245 USFrameDataPtr UsReconstructionFileReader::readUsDataFile(QString mhdFileName)
246 {
247  return USFrameData::create(mhdFileName);
248 }
249 
250 std::vector<TimedPosition> UsReconstructionFileReader::readFrameTimestamps(QString fileName)
251 {
252  bool useOldFormat = !QFileInfo(changeExtension(fileName, "fts")).exists();
253  std::vector<TimedPosition> retval;
254 
255  if (useOldFormat)
256  {
257  this->readTimeStampsFile(changeExtension(fileName, "tim"), &retval);
258  }
259  else
260  {
261  this->readTimeStampsFile(changeExtension(fileName, "fts"), &retval);
262  }
263  return retval;
264 }
265 
266 std::vector<TimedPosition> UsReconstructionFileReader::readPositions(QString fileName)
267 {
268  bool useOldFormat = !QFileInfo(changeExtension(fileName, "fts")).exists();
269  std::vector<TimedPosition> retval;
270 
271  if (useOldFormat)
272  {
273  this->readPositionFile(changeExtension(fileName, "pos"), true, &retval);
274  }
275  else
276  {
277  this->readPositionFile(changeExtension(fileName, "tp"), false, &retval);
278  this->readTimeStampsFile(changeExtension(fileName, "tts"), &retval);
279  }
280  return retval;
281 }
282 
283 void UsReconstructionFileReader::readTimeStampsFile(QString fileName,
284  std::vector<TimedPosition>* timedPos)
285 {
286  QFile file(fileName);
287  if(!file.open(QIODevice::ReadOnly))
288  {
289  reportWarning("Can't open file: " + fileName);
290  return;
291  }
292  bool ok = true;
293 
294  unsigned int i = 0;
295  while (!file.atEnd())
296  {
297  if (i>=timedPos->size())
298  {
299  timedPos->push_back(TimedPosition());
300  }
301 
302  QByteArray array = file.readLine();
303  double time = QString(array).toDouble(&ok);
304  if (!ok)
305  {
306  reportWarning("Can't read double in file: " + fileName);
307  return;
308  }
309  timedPos->at(i).mTime = time;
310  i++;
311  }
312 
313 }
314 
315 void UsReconstructionFileReader::readPositionFile(QString posFile, bool alsoReadTimestamps, std::vector<TimedPosition>* timedPos)
316 {
317  QFile file(posFile);
318  if(!file.open(QIODevice::ReadOnly))
319  {
320  reportWarning("Can't open file: "
321  + posFile);
322  return;
323  }
324  bool ok = true;
325 
326  unsigned i = 0;
327  while (!file.atEnd())
328  {
329  if (i>=timedPos->size())
330  {
331  timedPos->push_back(TimedPosition());
332  }
333 
334  TimedPosition position;
335  position.mTime = 0;
336  if (alsoReadTimestamps)
337  {
338  //old format - timestamps embedded in pos file);
339  QByteArray array = file.readLine();
340  position.mTime = QString(array).toDouble(&ok);
341  if (!ok)
342  {
343  reportWarning("Can't read double in file: "
344  + posFile);
345  return;
346  }
347  }
348 
349  QString positionString = file.readLine();
350  positionString += " " + file.readLine();
351  positionString += " " + file.readLine();
352  positionString += " 0 0 0 1";
353  position.mPos = Transform3D::fromString(positionString, &ok);
354  if (!ok)
355  {
356  reportWarning("Can't read position number: "
357  + qstring_cast(i)
358  + " from file: "
359  + posFile
360  + "values: "
361  + qstring_cast(position.mPos(0,0)));
362  return;
363  }
364  timedPos->at(i) = position;
365  i++;
366  }
367 
368  return;
369 }
370 
371 bool UsReconstructionFileReader::readMaskFile(QString mhdFileName, ImagePtr mask)
372 {
373  QString fileName = changeExtension(mhdFileName, "msk");
374 
375  if (!QFileInfo(fileName).exists())
376  return false;
377 
378  vtkImageDataPtr data = mask->getBaseVtkImageData();
379 
380 
381  QFile file(fileName);
382  file.open(QIODevice::ReadOnly);
383  QDataStream stream(&file);
384 
385  unsigned char* dataPtr = static_cast<unsigned char*>(data->GetScalarPointer());
386  char *rawchars = reinterpret_cast<char*>(dataPtr);
387 
388  stream.readRawData(rawchars, file.size());
389  setDeepModified(data);
390 
391  return true;
392 }
393 
394 
395 } // namespace cx
QString qstring_cast(const T &val)
< a easy-to-work-with struct for a specific xml configuration
void reportError(QString msg)
Definition: cxLogger.cpp:92
void setSpacing(Vector3D spacing)
static std::pair< QString, ProbeDefinition > readProbeDefinitionFromFile(QString mhdFileName)
boost::shared_ptr< class Image > ImagePtr
Definition: cxDicomWidget.h:48
boost::shared_ptr< class USFrameData > USFrameDataPtr
std::vector< TimedPosition > readFrameTimestamps(QString fileName)
std::vector< TimedPosition > mFrames
QDomElement getElement()
return the current element
bool similar(const DoubleBoundingBox3D &a, const DoubleBoundingBox3D &b, double tol)
USReconstructInputData readAllFiles(QString fileName, QString calFilesPath="")
void reportWarning(QString msg)
Definition: cxLogger.cpp:91
Implementation of abstract interface ProbeXmlConfigParser Interface to ProbeCalibConfigs.xml.
boost::shared_ptr< ProbeXmlConfigParser > ProbeXmlConfigParserPtr
vtkImageDataPtr generateVtkImageData(Eigen::Array3i dim, Vector3D spacing, const unsigned char initValue, int components)
std::vector< TimedPosition > mPositions
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition: cxVector3D.h:63
Definition of characteristics for an Ultrasound Probe Sector.
Vector3D getSpacing() const
QString changeExtension(QString name, QString ext)
void report(QString msg)
Definition: cxLogger.cpp:90
void setDeepModified(vtkImageDataPtr image)
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 USFrameDataPtr create(ImagePtr inputFrameData)
Helper class for xml files used to store ssc/cx data.
ProbeDefinition createProbeDefinitionFromConfiguration(ProbeXmlConfigParser::Configuration config)
void setData(ProbeDefinition data)