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