PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
TimestampFilteringTest.cxx
Go to the documentation of this file.
1 /*=Plus=header=begin======================================================
2 Program: Plus
3 Copyright (c) Laboratory for Percutaneous Surgery. All rights reserved.
4 See License.txt for details.
5 =========================================================Plus=header=end*/
6 
12 // Local includes
13 #include "PlusConfigure.h"
14 #ifdef PLUS_RENDERING_ENABLED
15 #include "PlusPlotter.h"
16 #endif
17 #include "vtkPlusBuffer.h"
18 #include "vtkPlusHTMLGenerator.h"
19 #include "vtkIGSIOSequenceIO.h"
20 #include "vtkIGSIOTrackedFrameList.h"
21 
22 // VTK includes
23 #include <vtksys/CommandLineArguments.hxx>
24 #include <vtksys/SystemTools.hxx>
25 #include <vtkTable.h>
26 
27 
28 int main(int argc, char** argv)
29 {
30  int numberOfErrors(0);
31 
32  bool printHelp(false);
33  std::string inputMetafile;
34  std::string inputBaselineReportFilePath("");
35  int inputAveragedItemsForFiltering(20);
36  double inputMaxTimestampDifference(0.080);
37  double inputMinStdevReductionFactor(3.0);
38  std::string inputTransformName;
39 
40  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
41 
42  vtksys::CommandLineArguments args;
43  args.Initialize(argc, argv);
44 
45  args.AddArgument("--transform", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputTransformName, "Transform name used for generating timestamp filtering");
46  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
47  args.AddArgument("--source-seq-file", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputMetafile, "Input sequence metafile.");
48  args.AddArgument("--averaged-items-for-filtering", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputAveragedItemsForFiltering, "Number of averaged items used for filtering (Default: 20).");
49  args.AddArgument("--max-timestamp-difference", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputMaxTimestampDifference, "The maximum difference between the filtered and nonfiltered timestamps for each frame (Default: 0.08s).");
50  args.AddArgument("--min-stdev-reduction-factor", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &inputMinStdevReductionFactor, "Minimum factor that the filtering should reduces the standard deviation of the frame periods on filtered data (Default: 3.0 ).");
51  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace)");
52 
53  if (!args.Parse())
54  {
55  std::cerr << "Problem parsing arguments" << std::endl;
56  std::cout << "Help: " << args.GetHelp() << std::endl;
57  exit(EXIT_FAILURE);
58  }
59 
60  if (printHelp)
61  {
62  std::cout << args.GetHelp() << std::endl;
63  exit(EXIT_SUCCESS);
64  }
65 
66  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
67 
68  if (inputMetafile.empty())
69  {
70  std::cerr << "input-metafile argument required!" << std::endl;
71  std::cout << "Help: " << args.GetHelp() << std::endl;
72  exit(EXIT_FAILURE);
73  }
74 
75  igsioTransformName transformName;
76  if (transformName.SetTransformName(inputTransformName.c_str()) != PLUS_SUCCESS)
77  {
78  LOG_ERROR("Invalid transform name: " << inputTransformName);
79  return EXIT_FAILURE;
80  }
81 
82  // Read buffer
83  LOG_INFO("Reading meta file...");
84  vtkSmartPointer<vtkIGSIOTrackedFrameList> trackerFrameList = vtkSmartPointer<vtkIGSIOTrackedFrameList>::New();
85  if (vtkIGSIOSequenceIO::Read(inputMetafile, trackerFrameList) != PLUS_SUCCESS)
86  {
87  LOG_ERROR("Failed to read sequence metafile from file: " << inputMetafile);
88  }
89 
90  LOG_INFO("Copy buffer to tracker buffer...");
91  vtkSmartPointer<vtkPlusBuffer> trackerBuffer = vtkSmartPointer<vtkPlusBuffer>::New();
92  trackerBuffer->SetTimeStampReporting(true);
93  // compute filtered timestamps now to test the filtering
94  if (trackerBuffer->CopyTransformFromTrackedFrameList(trackerFrameList, vtkPlusBuffer::READ_UNFILTERED_COMPUTE_FILTERED_TIMESTAMPS, transformName) != PLUS_SUCCESS)
95  {
96  LOG_ERROR("CopyDefaultTrackerDataToBuffer failed");
97  numberOfErrors++;
98  }
99 
100  // Check filtering results
101  //************************
102 
103  // 1. The maximum difference between the filtered and nonfiltered timestamps for each frame shall be below a specified threshold (fabs(timestamp-unfilteredTimestamp) < maxTimestampDifference)
104  double maxTimestampDifference(0);
105  for (BufferItemUidType item = trackerBuffer->GetOldestItemUidInBuffer(); item <= trackerBuffer->GetLatestItemUidInBuffer(); ++item)
106  {
107  StreamBufferItem bufferItem;
108  if (trackerBuffer->GetStreamBufferItem(item, &bufferItem) != ITEM_OK)
109  {
110  LOG_WARNING("Failed to get buffer item with UID: " << item);
111  numberOfErrors++;
112  continue;
113  }
114 
115  double timestampDifference = fabs(bufferItem.GetUnfilteredTimestamp(0) - bufferItem.GetFilteredTimestamp(0));
116  if (timestampDifference > maxTimestampDifference)
117  {
118  maxTimestampDifference = timestampDifference;
119  }
120  if (timestampDifference > inputMaxTimestampDifference)
121  {
122  LOG_ERROR("Difference between the filtered and nonfiltered timestamps are higher than the threshold (UID: " << item
123  << ", unfilteredTimestamp: " << std::fixed << bufferItem.GetUnfilteredTimestamp(0)
124  << ", filteredTimestamp: " << std::fixed << bufferItem.GetFilteredTimestamp(0)
125  << ", timestamp diference: " << timestampDifference << ", threshold: " << inputMaxTimestampDifference << ")");
126  numberOfErrors++;
127  }
128  }
129 
130  LOG_INFO("Maximum filtered and unfiltered timestamp difference: " << maxTimestampDifference * 1000 << "ms");
131 
132  //2. The standard deviation of the frame periods in the filtered data should be better than without filtering (stdevFramePeriodsUnfiltered / stdevFramePeriodsFiltered < minStdevReductionFactor)
133  vnl_vector<double>unfilteredFramePeriods(trackerBuffer->GetNumberOfItems() - 1);
134  vnl_vector<double>filteredFramePeriods(trackerBuffer->GetNumberOfItems() - 1);
135  int i = 0;
136  for (BufferItemUidType item = trackerBuffer->GetOldestItemUidInBuffer(); item < trackerBuffer->GetLatestItemUidInBuffer(); ++item)
137  {
138  StreamBufferItem bufferItem_1;
139  if (trackerBuffer->GetStreamBufferItem(item, &bufferItem_1) != ITEM_OK)
140  {
141  LOG_WARNING("Failed to get buffer item with UID: " << item);
142  numberOfErrors++;
143  continue;
144  }
145 
146  StreamBufferItem bufferItem_2;
147  if (trackerBuffer->GetStreamBufferItem(item + 1, &bufferItem_2) != ITEM_OK)
148  {
149  LOG_WARNING("Failed to get buffer item with UID: " << item + 1);
150  numberOfErrors++;
151  continue;
152  }
153 
154  unfilteredFramePeriods.put(i, (bufferItem_2.GetUnfilteredTimestamp(0) - bufferItem_1.GetUnfilteredTimestamp(0)) / (bufferItem_2.GetIndex() - bufferItem_1.GetIndex()));
155  filteredFramePeriods.put(i, (bufferItem_2.GetFilteredTimestamp(0) - bufferItem_1.GetFilteredTimestamp(0)) / (bufferItem_2.GetIndex() - bufferItem_1.GetIndex()));
156  i++;
157  }
158 
159  // Compute frame period means
160  double unfilteredFramePeriodsMean = unfilteredFramePeriods.mean();
161  double filteredFramePeriodsMean = filteredFramePeriods.mean();
162 
163  // Compute frame period stdev
164  vnl_vector<double> diffFromMeanUnfilteredFramePeriods = unfilteredFramePeriods - unfilteredFramePeriodsMean;
165  double unfilteredFramePeriodsStd = sqrt(diffFromMeanUnfilteredFramePeriods.squared_magnitude() / diffFromMeanUnfilteredFramePeriods.size());
166 
167  LOG_INFO("Unfiltered frame periods mean: " << std::fixed << unfilteredFramePeriodsMean * 1000 << "ms stdev: " << unfilteredFramePeriodsStd * 1000 << "ms");
168 
169  vnl_vector<double> diffFromMeanFilteredFramePeriods = filteredFramePeriods - filteredFramePeriodsMean;
170  double filteredFramePeriodsStd = sqrt(diffFromMeanFilteredFramePeriods.squared_magnitude() / diffFromMeanFilteredFramePeriods.size());
171 
172  LOG_INFO("Filtered frame periods mean: " << std::fixed << filteredFramePeriodsMean * 1000 << "ms stdev: " << filteredFramePeriodsStd * 1000 << "ms");
173 
174  LOG_INFO("Filtered data frame period reduction factor: " << std::fixed << unfilteredFramePeriodsStd / filteredFramePeriodsStd);
175 
176  if (unfilteredFramePeriodsStd / filteredFramePeriodsStd < inputMinStdevReductionFactor)
177  {
178  LOG_ERROR("Filtered data frame period reduction factor is smaller than the threshold (factor: " << std::fixed << unfilteredFramePeriodsStd / filteredFramePeriodsStd << ", threshold: " << inputMinStdevReductionFactor << ")");
179  numberOfErrors++;
180  }
181 
182 
183  vtkSmartPointer<vtkTable> timestampReportTable = vtkSmartPointer<vtkTable>::New();
184  if (trackerBuffer->GetTimeStampReportTable(timestampReportTable) != PLUS_SUCCESS)
185  {
186  LOG_ERROR("Failed to get time stamp report table!");
187  numberOfErrors++;
188  }
189 
190  std::string reportFile = vtksys::SystemTools::GetCurrentWorkingDirectory() + std::string("/TimestampReport.txt");
191 
192 #ifdef PLUS_RENDERING_ENABLED
193  if (PlusPlotter::WriteTableToFile(*timestampReportTable, reportFile.c_str()) != PLUS_SUCCESS)
194  {
195  LOG_ERROR("Failed to write table to file");
196  return PLUS_FAIL;
197  }
198 #endif
199 
200  if (vtkPlusLogger::Instance()->GetLogLevel() >= vtkPlusLogger::LOG_LEVEL_DEBUG)
201  {
202  timestampReportTable->Dump();
203  }
204 
205  if (numberOfErrors != 0)
206  {
207  LOG_INFO("Test failed!");
208  return EXIT_FAILURE;
209  }
210 
211  LOG_INFO("Test completed successfully!");
212  return EXIT_SUCCESS;
213 }
static PlusStatus WriteTableToFile(vtkTable &table, const std::string &filename)
for i
#define PLUS_FAIL
Definition: PlusCommon.h:43
double GetFilteredTimestamp(double localTimeOffsetSec)
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
int main(int argc, char **argv)
unsigned long GetIndex()
double GetUnfilteredTimestamp(double localTimeOffsetSec)
static vtkIGSIOLogger * Instance()
unsigned long long BufferItemUidType