OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
com_osvr_VideoBasedHMDTracker.cpp
Go to the documentation of this file.
1 
11 // Copyright 2015 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 // Internal Includes
26 #include "VideoBasedTracker.h"
28 #include "CameraParameters.h"
29 #include "ImageSource.h"
30 #include "ImageSourceFactories.h"
34 #include "HDKData.h"
35 #include "SetupSensors.h"
36 
37 #include "ConfigurationParser.h"
38 
39 // Generated JSON header file
40 #include "com_osvr_VideoBasedHMDTracker_json.h"
41 
42 // Library/third-party includes
43 #include <opencv2/core/core.hpp> // for basic OpenCV types
44 #include <opencv2/core/operations.hpp>
45 #include <opencv2/highgui/highgui.hpp> // for image capture
46 #include <opencv2/imgproc/imgproc.hpp> // for image scaling
47 #include <json/value.h>
48 #include <json/reader.h>
49 
50 #include <boost/noncopyable.hpp>
51 
52 // Standard includes
53 #include <iostream>
54 #include <fstream>
55 #include <iomanip>
56 #include <sstream>
57 #include <memory>
58 
59 // Define the constant below to print timing information (how many updates
60 // per second we are getting).
61 //#define VBHMD_TIMING
62 
63 // Define the constant below to set a directory to save the video frames that
64 // are acquired with files in a format that can later be read by
65 // VBHMD_FAKE_IMAGES
66 //#define VBHMD_SAVE_IMAGES "./Frames"
67 
68 // Anonymous namespace to avoid symbol collision
69 namespace {
70 
71 static const auto DEBUGGABLE_BEACONS = 34;
72 static const auto DATAPOINTS_PER_BEACON = 5;
73 
74 class VideoBasedHMDTracker : boost::noncopyable {
75  public:
76  VideoBasedHMDTracker(OSVR_PluginRegContext ctx,
77  osvr::vbtracker::ImageSourcePtr &&source,
78  int devNumber = 0,
79  osvr::vbtracker::ConfigParams const &params =
81  : m_source(std::move(source)), m_vbtracker(params), m_params(params) {
82  if (params.numThreads > 0) {
83  // Set the number of threads for OpenCV to use.
84  cv::setNumThreads(params.numThreads);
85  }
86 
89 
90  // Configure the tracker interface.
91  osvrDeviceTrackerConfigure(opts, &m_tracker);
92 
93  osvrDeviceAnalogConfigure(opts, &m_analog,
94  DEBUGGABLE_BEACONS * DATAPOINTS_PER_BEACON);
95 
97  std::ostringstream os;
98  os << "TrackedCamera" << devNumber << "_" << 0;
99 
101  m_dev.initAsync(ctx, os.str(), opts);
102 
104  m_dev.sendJsonDescriptor(com_osvr_VideoBasedHMDTracker_json);
105 
107  m_dev.registerUpdateCallback(this);
108  }
109 
110  OSVR_ReturnCode update();
111 
114  osvr::vbtracker::VideoBasedTracker &vbtracker() { return m_vbtracker; }
115 
116  private:
118  OSVR_TrackerDeviceInterface m_tracker;
120  osvr::vbtracker::ImageSourcePtr m_source;
121  osvr::vbtracker::ConfigParams const m_params;
122 #ifdef VBHMD_SAVE_IMAGES
123  int m_imageNum = 1;
124 #endif
125  cv::Mat m_frame;
126  cv::Mat m_imageGray;
127 
129 };
130 
131 inline OSVR_ReturnCode VideoBasedHMDTracker::update() {
132  if (!m_source->ok()) {
133  // Couldn't open the camera. Failing silently for now. Maybe the
134  // camera will be plugged back in later.
135  return OSVR_RETURN_SUCCESS;
136  }
137 
138  //==================================================================
139  // Trigger a camera grab.
140  if (!m_source->grab()) {
141  // Couldn't open the camera. Failing silently for now. Maybe the
142  // camera will be plugged back in later.
143  return OSVR_RETURN_SUCCESS;
144  }
145 
146  //==================================================================
147  // Keep track of when we got the image, since that is our
148  // best estimate for when the tracker was at the specified
149  // pose.
150  // TODO: Back-date the aquisition time by the expected image
151  // transfer time and perhaps by half the exposure time to say
152  // when the photons actually arrived.
153  OSVR_TimeValue timestamp;
154  osvrTimeValueGetNow(&timestamp);
155  // Pull the image into an OpenCV matrix named m_frame.
156  m_source->retrieve(m_frame, m_imageGray);
157 
158 #ifdef VBHMD_SAVE_IMAGES
159  // If we're supposed to save images, make file names that match the
160  // format we need to read them back in again and save the images.
161  std::ostringstream fileName;
162  fileName << VBHMD_SAVE_IMAGES << "/";
163  fileName << std::setfill('0') << std::setw(4) << m_imageNum++;
164  fileName << ".tif";
165  if (!cv::imwrite(fileName.str(), m_frame)) {
166  std::cerr << "Could not write image to " << fileName.str() << std::endl;
167  }
168 
169 #endif
170 
171 #ifdef VBHMD_TIMING
172  //==================================================================
173  // Time our performance
174  static struct timeval last = {0, 0};
175  if (last.tv_sec == 0) {
176  vrpn_gettimeofday(&last, NULL);
177  }
178  static unsigned count = 0;
179  if (++count == 100) {
180  struct timeval now;
181  vrpn_gettimeofday(&now, NULL);
182  double duration = vrpn_TimevalDurationSeconds(now, last);
183  std::cout << "Video-based tracker: update rate " << count / duration
184  << " hz" << std::endl;
185  count = 0;
186  last = now;
187  }
188 #endif
189  bool shouldSendDebug = false;
190  m_vbtracker.processImage(
191  m_frame, m_imageGray, timestamp,
192  [&](OSVR_ChannelCount sensor, OSVR_Pose3 const &pose) {
193 
194  //==================================================================
195  // Report the new pose, time-stamped with the time we
196  // received the image from the camera.
197  osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &pose,
198  sensor, &timestamp);
199  if (sensor == 0) {
200  shouldSendDebug = true;
201  }
202  });
203  if (shouldSendDebug && m_params.streamBeaconDebugInfo) {
204  double data[DEBUGGABLE_BEACONS * DATAPOINTS_PER_BEACON];
205  auto &debug = m_vbtracker.getFirstEstimator().getBeaconDebugData();
206  auto now = osvr::util::time::getNow();
207  auto n = std::min(size_t(DEBUGGABLE_BEACONS), debug.size());
208  for (std::size_t i = 0; i < n; ++i) {
209  double *buf = &data[i];
210  auto j = i * DATAPOINTS_PER_BEACON;
211  // yes, using postincrement since we want the previous value
212  // returned. Borderline "too clever" but it's debug code.
213  data[j] = debug[i].variance;
214  data[j + 1] = debug[i].measurement.x;
215  data[j + 2] = debug[i].measurement.y;
216  data[j + 3] = debug[i].residual.x;
217  data[j + 4] = debug[i].residual.y;
218  }
219  osvrDeviceAnalogSetValuesTimestamped(m_dev, m_analog, data, n, &now);
220  }
221 
222  return OSVR_RETURN_SUCCESS;
223 }
224 
225 class HardwareDetection {
226  public:
227  using CameraFactoryType = std::function<osvr::vbtracker::ImageSourcePtr()>;
228  using SensorSetupType = std::function<void(VideoBasedHMDTracker &)>;
229  HardwareDetection(CameraFactoryType camFactory, SensorSetupType setup,
230  int cameraID = 0,
231  osvr::vbtracker::ConfigParams const &params =
233  : m_found(false), m_cameraFactory(camFactory), m_sensorSetup(setup),
234  m_cameraID(cameraID), m_params(params) {}
235 
236  OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {
237  if (m_found) {
238  return OSVR_RETURN_SUCCESS;
239  }
240  auto src = m_cameraFactory();
241  if (!src || !src->ok()) {
242  if (!m_reportedNoCamera) {
243  m_reportedNoCamera = true;
244  std::cout
245  << "\nVideo-based tracker: Could not open the tracking "
246  "camera. If you intend to use it, make sure that "
247  "all cables to it are plugged in firmly.\n";
248 
249 #ifdef _WIN32
250  std::cout << "Video-based tracker: Windows users may need to "
257  "exit other camera-using applications or "
258  "activities until after the tracking camera is "
259  "turned on by this plugin. (This is the most "
260  "common cause of messages regarding the 'filter "
261  "graph')\n";
262 #endif
263  std::cout << std::endl;
264  }
265  return OSVR_RETURN_FAILURE;
266  }
267  std::cout << "Video-based tracker: Camera turned on!" << std::endl;
268  m_found = true;
269 
271  std::cout << "Opening camera " << m_cameraID << std::endl;
273  ctx, new VideoBasedHMDTracker(ctx, std::move(src), m_cameraID,
274  m_params));
275  m_sensorSetup(*newTracker);
276  return OSVR_RETURN_SUCCESS;
277  }
278 
279  private:
282  bool m_found = false;
283  bool m_reportedNoCamera = false;
284 
285  CameraFactoryType m_cameraFactory;
286  SensorSetupType m_sensorSetup;
287 
288  int m_cameraID; //< Which OpenCV camera should we open?
289  osvr::vbtracker::ConfigParams const m_params;
290 };
291 
292 class ConfiguredDeviceConstructor {
293  public:
296  OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx, const char *params) {
297  // Read the JSON data from parameters.
298  Json::Value root;
299  if (params) {
300  Json::Reader r;
301  if (!r.parse(params, root)) {
302  std::cerr << "Could not parse parameters!" << std::endl;
303  }
304  }
305 
306  // Read these parameters from a "params" field in the device Json
307  // configuration file.
308 
309  // Using `get` here instead of `[]` lets us provide a default value.
310  int cameraID = root.get("cameraID", 0).asInt();
311 
312  // This is in a separate function/header foro sharing and for clarity.
313  auto config = osvr::vbtracker::parseConfigParams(root);
314 
317  auto backPanelFixedBeacon = [](int) { return true; };
318  auto frontPanelFixedBeacon = [](int id) {
319  return (id == 16) || (id == 17) || (id == 19) || (id == 20);
320  };
321 
323  bool fakeImages = false;
324  if (fakeImages) {
326  auto path = std::string{};
327  // fake images
328  auto src = osvr::vbtracker::openImageFileSequence(path);
329  if (!src) {
330  return OSVR_RETURN_FAILURE;
331  }
333  ctx, new VideoBasedHMDTracker(ctx, std::move(src), cameraID,
334  config));
335  auto camParams = osvr::vbtracker::getSimulatedHDKCameraParameters();
336  newTracker->vbtracker().addSensor(
337  osvr::vbtracker::createHDKLedIdentifierSimulated(0), camParams,
338  osvr::vbtracker::OsvrHdkLedLocations_SENSOR0,
339  osvr::vbtracker::OsvrHdkLedDirections_SENSOR0,
340  frontPanelFixedBeacon, 4, 2);
341  // There are sometimes only four beacons on the back unit (two of
342  // the LEDs are disabled), so we let things work with just those.
343  newTracker->vbtracker().addSensor(
344  osvr::vbtracker::createHDKLedIdentifierSimulated(1), camParams,
345  osvr::vbtracker::OsvrHdkLedLocations_SENSOR1,
346  osvr::vbtracker::OsvrHdkLedDirections_SENSOR1,
347  backPanelFixedBeacon, 4, 0);
348  return OSVR_RETURN_SUCCESS;
349  }
350 #if 0
351  bool isOculusCamera = (width == 376) && (height == 480);
352 #endif
353 
354 #ifdef _WIN32
355  auto cameraFactory = &osvr::vbtracker::openHDKCameraDirectShow;
357 
358 #else // !_WIN32
359 
364  auto cameraFactory = [=] {
365  return osvr::vbtracker::openOpenCVCamera(cameraID);
366  };
367 #endif
368 
370  std::function<void(VideoBasedHMDTracker & newTracker)>
371  setupHDKParamsAndSensors;
372 
373  if (config.includeRearPanel) {
374  setupHDKParamsAndSensors = [config](
375  VideoBasedHMDTracker &newTracker) {
376  osvr::vbtracker::setupSensorsIncludeRearPanel(
377  newTracker.vbtracker(), config);
378  };
379  } else {
380  // OK, so if we don't have to include the rear panel as part of the
381  // single sensor, that's easy.
382  setupHDKParamsAndSensors = [config](
383  VideoBasedHMDTracker &newTracker) {
384  osvr::vbtracker::setupSensorsWithoutRearPanel(
385  newTracker.vbtracker(), config);
386  };
387  }
388 
389  // OK, now that we have our parameters, create the device.
390  osvr::pluginkit::PluginContext context(ctx);
391  context.registerHardwareDetectCallback(new HardwareDetection(
392  cameraFactory, setupHDKParamsAndSensors, cameraID, config));
393 
394  return OSVR_RETURN_SUCCESS;
395  }
396 };
397 
398 } // namespace
399 
400 OSVR_PLUGIN(com_osvr_VideoBasedHMDTracker) {
401  osvr::pluginkit::PluginContext context(ctx);
402 
405  ctx, "VideoBasedHMDTracker", new ConfiguredDeviceConstructor);
406 
407  return OSVR_RETURN_SUCCESS;
408 }
uint32_t OSVR_ChannelCount
The integer type specifying a number of channels/sensors or a channel/sensor index.
Definition: ChannelCountC.h:51
Wrapper class for OSVR_DeviceToken.
void getNow(TimeValue &tv)
Set the given TimeValue to the current time.
Definition: TimeValue.h:51
void osvrTimeValueGetNow(OSVR_TimeValue *dest)
Gets the current time in the TimeValue. Parallel to gettimeofday.
Definition: TimeValueC.cpp:100
C++ wrapper class for the opaque plugin context.
Definition: PluginKit.h:59
Structure used internally to construct the desired type of device.
double duration(TimeValue const &a, TimeValue const &b)
Get a double containing seconds between the time points.
Definition: TimeValue.h:62
Header.
#define OSVR_RETURN_FAILURE
The "failure" value for an OSVR_ReturnCode.
Definition: ReturnCodesC.h:47
T * registerObjectForDeletion(OSVR_PluginRegContext ctx, T *obj)
Registers an object to be destroyed with delete when the plugin is unloaded.
General configuration parameters.
Definition: ConfigParams.h:82
void registerDriverInstantiationCallback(OSVR_PluginRegContext ctx, const char driverName[], T functor)
Registers a function object to be called when the server is told to instantiate a driver by name with...
#define OSVR_RETURN_SUCCESS
The "success" value for an OSVR_ReturnCode.
Definition: ReturnCodesC.h:45
OSVR_DeviceInitOptions osvrDeviceCreateInitOptions(OSVR_PluginRegContext ctx)
Create a OSVR_DeviceInitOptions object.
Header including the full PluginKit C++ interface.
OSVR_PLUGIN(com_osvr_VideoBasedHMDTracker)
OSVR_ReturnCode osvrDeviceAnalogSetValuesTimestamped(OSVR_DeviceToken dev, OSVR_AnalogDeviceInterface iface, OSVR_AnalogState val[], OSVR_ChannelCount chans, OSVR_TimeValue const *timestamp)
Report the value of multiple channels with the supplied timestamp.
A structure defining a 3D (6DOF) rigid body pose: translation and rotation.
Definition: Pose3C.h:54
OSVR_ReturnCode osvrDeviceTrackerSendPoseTimestamped(OSVR_DeviceToken dev, OSVR_TrackerDeviceInterface iface, OSVR_PoseState const *val, OSVR_ChannelCount sensor, OSVR_TimeValue const *timestamp)
Report the full rigid body pose of a sensor, using the supplied timestamp.
OSVR_ReturnCode osvrDeviceTrackerConfigure(OSVR_DeviceInitOptions opts, OSVR_TrackerDeviceInterface *iface)
Specify that your device will implement the Tracker interface.
OSVR_EXTERN_C_BEGIN typedef void * OSVR_PluginRegContext
A context pointer passed in to your plugin's entry point and other locations of control flow transfer...
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
Definition: TimeValueC.h:81
OSVR_ReturnCode osvrDeviceAnalogConfigure(OSVR_DeviceInitOptions opts, OSVR_AnalogDeviceInterface *iface, OSVR_ChannelCount numChan)
Specify that your device will implement the Analog interface.