PlusLib  2.9.0
Software library for tracked ultrasound image acquisition, calibration, and processing.
AtracsysMarkerCreator.cxx
Go to the documentation of this file.
1 #include "PlusConfigure.h"
2 #include "AtracsysTracker.h"
3 #include "vtksys/CommandLineArguments.hxx"
4 
5 // STL includes
6 #include <algorithm>
7 #include <fstream>
8 #include <iomanip>
9 #include <iostream>
10 #include <vector>
11 
12 // global handle to Atracsys API wrapper
15 
16 // constants
17 #define NUM_BACKGROUND_FRAMES 100
18 #define DEFAULT_NUM_DATA_FRAMES 100
19 #define ATRACSYS_MAX_FIDUCIALS 6
20 
21 // for convenience
22 #define ATR_SUCCESS AtracsysTracker::ATRACSYS_RESULT::SUCCESS
26 
27 // data types
28 // list of all 3d fiducials in single frame, including triangulated background stray blobs
29 typedef std::vector<Fiducial> fidsFrame;
30 // list of frames of 3d fiducials
31 typedef std::vector<fidsFrame> fidsFrameList;
32 
33 // struct to hold geometry intrinsics
34 struct MarkerGeometry
35 {
36  std::string name;
37  std::string description;
38  std::string destPath;
39  int geometryId;
40  fidsFrame fids;
41 };
42 
43 // forward declare functions
44 PlusStatus CollectFiducials(fidsFrameList& fidFrameList, int numFrames);
45 PlusStatus ProcessFiducials(fidsFrameList& fidFrameList, fidsFrame& backgroundFids);
48 PlusStatus WriteGeometryIniFile(const MarkerGeometry geom);
49 
50 int main(int argc, char** argv)
51 {
52  // Check command line arguments.
53  bool printHelp(false);
54  bool backgroundSubtraction(false);
55  std::string markerName;
56  std::string description;
57  std::string destinationPath;
58  int geometryId = -1;
59  int verboseLevel = vtkPlusLogger::LOG_LEVEL_UNDEFINED;
60  int numFrames = DEFAULT_NUM_DATA_FRAMES;
61 
62  vtksys::CommandLineArguments args;
63  args.Initialize(argc, argv);
64 
65  args.AddArgument("--help", vtksys::CommandLineArguments::NO_ARGUMENT, &printHelp, "Print this help.");
66  args.AddArgument("--background-subtraction", vtksys::CommandLineArguments::NO_ARGUMENT, &backgroundSubtraction, "Remove background fiducials from data considered by the creator.");
67  args.AddArgument("--marker-name", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &markerName, "Name of marker.");
68  args.AddArgument("--description", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &description, "Decsription of marker (i.e. purpose, color, size, and any other desired metadata).");
69  args.AddArgument("--geometryId", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &geometryId, "Id of the geometry we are creating. Must be unique.");
70  args.AddArgument("--destination-path", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &destinationPath, "Where the generated marker geometry ini file will be written to.");
71  args.AddArgument("--verbose", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &verboseLevel, "Verbose level (1=error only, 2=warning, 3=info, 4=debug, 5=trace).");
72  args.AddArgument("--num-frames", vtksys::CommandLineArguments::EQUAL_ARGUMENT, &numFrames, "Number of frames to use in generating marker geometry ini file.");
73 
74  if (!args.Parse())
75  {
76  std::cerr << "Problem parsing arguments." << std::endl;
77  std::cout << "Help: " << args.GetHelp() << std::endl;
78  exit(EXIT_FAILURE);
79  }
80 
81  if (printHelp)
82  {
83  std::cout << args.GetHelp() << std::endl;
84  exit(EXIT_SUCCESS);
85  }
86 
87  vtkPlusLogger::Instance()->SetLogLevel(verboseLevel);
88 
89  if (markerName.empty())
90  {
91  LOG_ERROR("--marker-name argument is required!");
92  std::cout << "Help: " << args.GetHelp() << std::endl;
93  exit(EXIT_FAILURE);
94  }
95 
96  if (description.empty())
97  {
98  LOG_ERROR("--description argument is required!");
99  std::cout << "Help: " << args.GetHelp() << std::endl;
100  exit(EXIT_FAILURE);
101  }
102 
103  if (geometryId == -1)
104  {
105  LOG_ERROR("--geometryId argument is required!");
106  std::cout << "Help: " << args.GetHelp() << std::endl;
107  exit(EXIT_FAILURE);
108  }
109 
110  LOG_INFO("Logging at level " << vtkPlusLogger::Instance()->GetLogLevel() << " (" << vtkPlusLogger::Instance()->GetLogLevelString() << ") to file: " << vtkPlusLogger::Instance()->GetLogFileName());
111 
112  // Connect to tracker
113  ATRACSYS_RESULT result = Tracker.Connect();
114  if (result != ATR_SUCCESS && result != AtracsysTracker::ATRACSYS_RESULT::WARNING_CONNECTED_IN_USB2)
115  {
116  LOG_ERROR(Tracker.ResultToString(result));
117  return PLUS_FAIL;
118  }
119  else if (result == AtracsysTracker::ATRACSYS_RESULT::WARNING_CONNECTED_IN_USB2)
120  {
121  LOG_WARNING(Tracker.ResultToString(result));
122  }
123 
124  // get device type
126 
127  // if spryTrack, setup for onboard processing and disable extraneous marker info streaming
128  if (DeviceType == AtracsysTracker::DEVICE_TYPE::SPRYTRACK_180)
129  {
130  Tracker.SetSpryTrackProcessingType(AtracsysTracker::SPRYTRACK_IMAGE_PROCESSING_TYPE::PROCESSING_ONBOARD);
131  }
132 
133  // set LED blue for collecting background
134  Tracker.SetUserLEDState(0, 0, 255, 0);
135 
136  // collect background frames
137  fidsFrameList backgroundfidsFrameList;
138  fidsFrame backgroundFids; // list of fids visible in background
139  if (backgroundSubtraction)
140  {
141  if (CollectFiducials(backgroundfidsFrameList, NUM_BACKGROUND_FRAMES) == PLUS_FAIL)
142  {
143  LOG_ERROR("Failed to collect background noise fiducial frames.");
144  return PLUS_FAIL;
145  }
146  ProcessFiducials(backgroundfidsFrameList, backgroundFids);
147 
148  // set LED red and wait for user to place marker in front of the camera
149  Tracker.SetUserLEDState(255, 0, 0, 0);
150  LOG_INFO("Background collection successful. Place marker in FOV of camera." << std::endl << "Press <ENTER> to continue.");
151  std::cin.get();
152  }
153 
154  LOG_INFO("Collecting data frames.");
155  // set LED blue for collecting frames
156  Tracker.SetUserLEDState(0, 0, 255, 0);
157 
158  // collect numFrames frames of fiducials
159  fidsFrameList datafidsFrameList;
160  if (CollectFiducials(datafidsFrameList, numFrames) == PLUS_FAIL)
161  {
162  LOG_ERROR("Failed to collect data fiducial frames.");
163  return PLUS_FAIL;
164  }
165  fidsFrame dataFids;
166  ProcessFiducials(datafidsFrameList, dataFids);
167 
168  // perform background subtraction and fiducial filtering
169  if (PerformBackgroundSubtraction(backgroundFids, dataFids) != PLUS_SUCCESS)
170  {
171  // problem detected with captured fiducial data, don't generate marker
172  return EXIT_FAILURE;
173  }
174 
175  // create marker
176  MarkerGeometry geom;
177  geom.name = markerName;
178  geom.description = description;
179  geom.destPath = destinationPath;
180  geom.geometryId = geometryId;
181  ZeroMeanFids(dataFids);
182  geom.fids = dataFids;
183 
184  // if too many fids, return
185  if (geom.fids.size() > ATRACSYS_MAX_FIDUCIALS)
186  {
187  LOG_ERROR("Too many fiducials in frame (there were " << geom.fids.size() << " marker fids visible). Unable to create Atracsys marker with this many fiducials.");
188  return EXIT_FAILURE;
189  }
190  else if (geom.fids.size() < 3)
191  {
192  LOG_ERROR("Too few fiducials in frame. Ensure marker is fully visible and try again.");
193  return EXIT_FAILURE;
194  }
195 
196  // write marker ini file
197  WriteGeometryIniFile(geom);
198 
199  // turn LED off & end program
200  Tracker.EnableUserLED(false);
201  return EXIT_SUCCESS;
202 }
203 
204 //----------------------------------------------------------------------------
205 PlusStatus CollectFiducials(fidsFrameList& fidFrameList, int numFrames)
206 {
207  int m = 0;
208  fidsFrame fid3dFrame;
209  std::vector<Marker> markerFrame; // unused
210  std::map<std::string, std::string> events; // unused
211  while (m < numFrames)
212  {
213  // ensure vector is empty
214  markerFrame.clear();
215  uint64_t ts = 0;
216  ATRACSYS_RESULT result = Tracker.GetMarkersInFrame(markerFrame, events, ts);
217  if (result == ATR_SUCCESS)
218  {
219  m++;
220  fidFrameList.push_back(fid3dFrame);
221  }
223  {
224  continue;
225  }
226  else
227  {
228  LOG_ERROR(Tracker.ResultToString(result));
229  return PLUS_FAIL;
230  }
231  }
232  return PLUS_SUCCESS;
233 }
234 
235 //----------------------------------------------------------------------------
237 {
238  fidsFrame frame;
239  // make sure backgroundFids is empty
240  fids.clear();
241 
242  for (fidsFrameList::size_type frameNum = 0; frameNum < fidFrameList.size(); frameNum++)
243  {
244  frame = fidFrameList[frameNum];
245  // populate backgroundFids with list of fiducials appearing in the background
246  std::copy(frame.begin(), frame.end(), std::inserter(fids, fids.end()));
247  }
248 
249  // remove duplicate points in backgroundFids && resize
250  std::sort(fids.begin(), fids.end());
251  fidsFrame::iterator it = std::unique(fids.begin(), fids.end());
252  fids.resize(std::distance(fids.begin(), it));
253  return PLUS_SUCCESS;
254 }
255 
256 //----------------------------------------------------------------------------
258 {
259  // subtract the backgroundFids from the dataFids
260  fidsFrame filteredDataFids;
261  fidsFrame::iterator backgroundIt, dataIt;
262  for (dataIt = begin(dataFids); dataIt != end(dataFids); dataIt++)
263  {
264  bool equalityFound = false;
265  for (backgroundIt = begin(backgroundFids); backgroundIt != end(backgroundFids); backgroundIt++)
266  {
267  if (*dataIt == *backgroundIt)
268  {
269  equalityFound = true;
270  }
271  }
272  if (!equalityFound)
273  {
274  // element of dataFids is not in background, add to filteredDataFids
275  filteredDataFids.push_back(*dataIt);
276  }
277  }
278 
279  // check that no data fiducials have non 1 probability
280  for (fidsFrame::size_type i = 0; i < filteredDataFids.size(); i++)
281  {
282  if (filteredDataFids[i].probability != 1)
283  {
284  LOG_ERROR("Fiducial with non 1 probability in data fiducials. Please retry marker creation ensuring that the marker is not moving and is in a good view position for the camera.");
285  return PLUS_FAIL;
286  }
287  }
288 
289  // copy filteredDataFids back to dataFids
290  dataFids.clear();
291  dataFids = filteredDataFids;
292  return PLUS_SUCCESS;
293 }
294 
295 //----------------------------------------------------------------------------
297 {
298  // zero-mean the fiducials
299  fidsFrame zeroMeanFids;
300  float cumulativeXmm = 0, cumulativeYmm = 0, cumulativeZmm = 0;
301  fidsFrame::const_iterator it;
302  for (it = begin(dataFids); it != end(dataFids); it++)
303  {
304  cumulativeXmm += it->xMm;
305  cumulativeYmm += it->yMm;
306  cumulativeZmm += it->zMm;
307  }
308  float aveXmm = cumulativeXmm /= dataFids.size();
309  float aveYmm = cumulativeYmm /= dataFids.size();
310  float aveZmm = cumulativeZmm /= dataFids.size();
311  for (it = begin(dataFids); it != end(dataFids); it++)
312  {
313  zeroMeanFids.emplace_back();
314  zeroMeanFids.back().xMm = it->xMm - aveXmm;
315  zeroMeanFids.back().yMm = it->yMm - aveYmm;
316  zeroMeanFids.back().zMm = it->zMm - aveZmm;
317  zeroMeanFids.back().probability = it->probability;
318  }
319  dataFids = zeroMeanFids;
320  return PLUS_SUCCESS;
321 }
322 
323 //----------------------------------------------------------------------------
324 PlusStatus WriteGeometryIniFile(const MarkerGeometry geom)
325 {
326  // create file path
327  std::string fileName;
328  if (geom.destPath.empty())
329  {
330  fileName = "./" + geom.name + ".ini";
331  }
332  else
333  {
334  fileName = geom.destPath + '/' + geom.name + ".ini";
335  }
336  LOG_INFO("Writing marker geometry to: " << fileName);
337  std::ofstream file;
338  file.open(fileName);
339 
340  // write metadata
341  file << ";; " << geom.name << std::endl;
342  file << ";; " << geom.description << std::endl;
343  auto t = std::time(nullptr);
344  auto tm = *std::localtime(&t);
345  file << ";; " << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << std::endl;
346 
347  // write geometry
348  file << "[geometry]" << std::endl;
349  file << "count=" << geom.fids.size() << std::endl;
350  file << "id=" << geom.geometryId << std::endl;
351 
352  for (fidsFrame::size_type i = 0; i < geom.fids.size(); i++)
353  {
354  file << "[fiducial" << i << "]" << std::endl;
355  file << "x=" << geom.fids[i].xMm << std::endl;
356  file << "y=" << geom.fids[i].yMm << std::endl;
357  file << "z=" << geom.fids[i].zMm << std::endl;
358  }
359 
360  file << "[pivot]" << std::endl;
361  file << "x=0.0000" << std::endl;
362  file << "y=0.0000" << std::endl;
363  file << "z=0.0000" << std::endl;
364 
365  file.close();
366  return PLUS_SUCCESS;
367 }
ATRACSYS_RESULT SetUserLEDState(int red, int green, int blue, int frequency, bool enabled=true)
PlusStatus ProcessFiducials(fidsFrameList &fidFrameList, fidsFrame &backgroundFids)
AtracsysTracker::ATRACSYS_RESULT ATRACSYS_RESULT
std::vector< fidsFrame > fidsFrameList
PlusStatus ZeroMeanFids(fidsFrame &dataFids)
ATRACSYS_RESULT EnableUserLED(bool enabled)
igsioStatus PlusStatus
Definition: PlusCommon.h:40
AtracsysTracker Tracker
uint32_t * distance
Definition: phidget22.h:4650
#define ATRACSYS_MAX_FIDUCIALS
ATRACSYS_RESULT GetDeviceType(DEVICE_TYPE &deviceType)
#define NUM_BACKGROUND_FRAMES
for i
AtracsysTracker::Fiducial Fiducial
#define PLUS_FAIL
Definition: PlusCommon.h:43
std::vector< Fiducial > fidsFrame
#define PLUS_SUCCESS
Definition: PlusCommon.h:44
PhidgetGPS_Time * time
Definition: phidget22.h:3623
ATRACSYS_RESULT GetMarkersInFrame(std::vector< Marker > &markers, std::map< std::string, std::string > &events, uint64_t &sdkTimestamp)
std::string ResultToString(ATRACSYS_RESULT result)
PlusStatus PerformBackgroundSubtraction(fidsFrame &backgroundFids, fidsFrame &dataFids)
#define DEFAULT_NUM_DATA_FRAMES
PlusStatus CollectFiducials(fidsFrameList &fidFrameList, int numFrames)
static vtkIGSIOLogger * Instance()
AtracsysTracker::Marker Marker
int main(int argc, char **argv)
ATRACSYS_RESULT SetSpryTrackProcessingType(SPRYTRACK_IMAGE_PROCESSING_TYPE processingType)
#define ATR_SUCCESS
for t
Definition: exploreFolders.m:9
ATRACSYS_RESULT Connect()
AtracsysTracker::DEVICE_TYPE DeviceType
PlusStatus WriteGeometryIniFile(const MarkerGeometry geom)