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