OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
org_osvr_unifiedvideoinertial.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 "AdditionalReports.h"
27 #include "ConfigurationParser.h"
28 #include "HDKData.h"
29 #include "MakeHDKTrackingSystem.h"
30 #include "TrackerThread.h"
31 
32 // ImageSources mini-library
35 
36 // uvbi-core mini-library
37 #include "CameraParameters.h"
39 #include "TrackingSystem.h"
40 
47 
48 #include <osvr/Util/EigenInterop.h>
49 
50 // Generated JSON header file
51 #include "org_osvr_unifiedvideoinertial_json.h"
52 
53 // Library/third-party includes
54 #include <json/reader.h>
55 #include <json/value.h>
56 #include <json/writer.h>
57 #include <opencv2/core/core.hpp> // for basic OpenCV types
58 #include <opencv2/core/operations.hpp>
59 #include <opencv2/highgui/highgui.hpp> // for image capture
60 #include <opencv2/imgproc/imgproc.hpp> // for image scaling
61 
62 #include <boost/assert.hpp>
63 #include <boost/noncopyable.hpp>
64 #include <boost/variant.hpp>
65 
66 #include <util/Stride.h>
67 
68 // Standard includes
69 #include <fstream>
70 #include <iomanip>
71 #include <iostream>
72 #include <memory>
73 #include <sstream>
74 #include <stdexcept>
75 #include <string>
76 
77 // Anonymous namespace to avoid symbol collision
78 namespace {
79 static const auto DRIVER_NAME = "UnifiedTrackingSystem";
80 
81 static const auto DEBUGGABLE_BEACONS = 34;
82 static const auto DATAPOINTS_PER_BEACON = 5;
83 using TrackingSystemPtr = std::unique_ptr<osvr::vbtracker::TrackingSystem>;
85 using osvr::vbtracker::BodyReportingVector;
89 
90 class UnifiedVideoInertialTracker : boost::noncopyable {
91  public:
92  using size_type = std::size_t;
93 
94  private:
96  OSVR_ClientContext m_clientCtx;
97  OSVR_ClientInterface m_clientInterface;
100  osvr::vbtracker::ImageSourcePtr m_source;
101  cv::Mat m_frame;
102  cv::Mat m_imageGray;
103  TrackingSystemPtr m_trackingSystem;
105  TrackedBody *m_mainBody = nullptr;
107  TrackedBodyIMU *m_imu = nullptr;
108  const double m_additionalPrediction;
109  const std::int32_t m_camUsecOffset = 0;
110  const std::int32_t m_oriUsecOffset = 0;
111  const std::int32_t m_angvelUsecOffset = 0;
112  const bool m_continuousReporting;
113  const bool m_debugData;
114  BodyReportingVector m_bodyReportingVector;
115  std::unique_ptr<TrackerThread> m_trackerThreadManager;
116  bool m_threadLoopStarted = false;
117  std::thread m_trackerThread;
118 
119  public:
120  UnifiedVideoInertialTracker(OSVR_PluginRegContext ctx,
121  osvr::vbtracker::ImageSourcePtr &&source,
123  TrackingSystemPtr &&trackingSystem)
124  : m_source(std::move(source)),
125  m_trackingSystem(std::move(trackingSystem)),
126  m_additionalPrediction(params.additionalPrediction),
127  m_camUsecOffset(params.cameraMicrosecondsOffset),
128  m_oriUsecOffset(params.imu.orientationMicrosecondsOffset),
129  m_angvelUsecOffset(params.imu.angularVelocityMicrosecondsOffset),
130  m_continuousReporting(params.continuousReporting),
131  m_debugData(params.streamBeaconDebugInfo) {
132  if (params.numThreads > 0) {
133  // Set the number of threads for OpenCV to use.
134  cv::setNumThreads(params.numThreads);
135  }
136 
139 
140  // Configure the tracker interface.
141  osvrDeviceTrackerConfigure(opts, &m_tracker);
142  if (m_debugData) {
143  osvrDeviceAnalogConfigure(opts, &m_analog,
144  osvr::vbtracker::DEBUG_ANALOGS_REQUIRED);
145  }
146 
148  OSVR_DeviceToken dev;
149  if (OSVR_RETURN_FAILURE ==
150  osvrAnalysisSyncInit(ctx, DRIVER_NAME, opts, &dev, &m_clientCtx)) {
151  throw std::runtime_error("Could not initialize analysis plugin!");
152  }
153  m_dev = osvr::pluginkit::DeviceToken(dev);
154 
156  m_dev.sendJsonDescriptor(createDeviceDescriptor());
157 
158  m_mainBody = &(m_trackingSystem->getBody(BodyId(0)));
159  if (m_mainBody->hasIMU()) {
160  m_imu = &(m_mainBody->getIMU());
162  if (OSVR_RETURN_FAILURE ==
163  osvrClientGetInterface(m_clientCtx, params.imu.path.c_str(),
164  &m_clientInterface)) {
165  throw std::runtime_error(
166  "Could not get client interface for analysis plugin!");
167  }
169  m_clientInterface, &UnifiedVideoInertialTracker::oriCallback,
170  this);
172  m_clientInterface, &UnifiedVideoInertialTracker::angVelCallback,
173  this);
174  }
175 
177  setupBodyReporting();
178 
181  startTrackerThread();
182 
184  m_dev.registerUpdateCallback(this);
185  }
186 
188  template <typename ReportType>
189  void handleData(OSVR_TimeValue const &timestamp, ReportType const &report) {
190  m_trackerThreadManager->submitIMUReport(*m_imu, timestamp, report);
191  }
192 
193  static void oriCallback(void *userdata, const OSVR_TimeValue *timestamp,
194  const OSVR_OrientationReport *report) {
195  auto &self = *static_cast<UnifiedVideoInertialTracker *>(userdata);
196  OSVR_TimeValue tv = *timestamp;
197  if (self.m_oriUsecOffset != 0) {
198  // apply offset, if non-zero.
199  const OSVR_TimeValue offset{0, self.m_oriUsecOffset};
200  osvrTimeValueSum(&tv, &offset);
201  }
202  self.handleData(tv, *report);
203  }
204 
205  static void angVelCallback(void *userdata, const OSVR_TimeValue *timestamp,
206  const OSVR_AngularVelocityReport *report) {
207  auto &self = *static_cast<UnifiedVideoInertialTracker *>(userdata);
208  OSVR_TimeValue tv = *timestamp;
209  if (self.m_angvelUsecOffset != 0) {
210  // apply offset, if non-zero.
211  const OSVR_TimeValue offset{0, self.m_angvelUsecOffset};
212  osvrTimeValueSum(&tv, &offset);
213  }
214  self.handleData(tv, *report);
215  }
216 
217  ~UnifiedVideoInertialTracker() { stopTrackerThread(); }
218 
220  void setupBodyReporting() {
221  m_bodyReportingVector.clear();
222  auto n = totalNumBodies();
223  for (decltype(n) i = 0; i < n; ++i) {
224  m_bodyReportingVector.emplace_back(
226  }
227  }
228 
229  size_type numTrackedBodies() const {
230  return m_trackingSystem->getNumBodies();
231  }
232 
233  int totalNumBodies() const {
234  static const auto EXTRA_TRACKING_SENSORS =
235  osvr::vbtracker::extra_outputs::numExtraOutputs;
236  // Add the extra sensors as configured in AdditionalReports.h
237  return static_cast<int>(numTrackedBodies()) + EXTRA_TRACKING_SENSORS;
238  }
239 
240  static std::string getTrackerPath(int sensor) {
241  std::ostringstream os;
242  os << "tracker/" << sensor;
243  return os.str();
244  }
245 
249  std::string createDeviceDescriptor() const {
250  auto n = numTrackedBodies();
251  auto origJson = std::string{org_osvr_unifiedvideoinertial_json};
252  Json::Value root;
253  {
254  Json::Reader reader;
256  if (!reader.parse(origJson, root)) {
257  BOOST_ASSERT_MSG(false,
258  "Not possible for parsing to fail: loading "
259  "a file that was parsed before "
260  "transformation into string literal!");
261  return origJson;
262  }
263  }
264  auto &semantic = root["semantic"];
265  auto &body = semantic["body"];
266  body = Json::Value(Json::objectValue);
269  for (decltype(n) i = 0; i < n; ++i) {
270  body[std::to_string(i)] = getTrackerPath(i);
271  }
272 
274  namespace extra_outputs = osvr::vbtracker::extra_outputs;
275  if (extra_outputs::outputCam) {
276  semantic["camera"] =
277  getTrackerPath(n + extra_outputs::outputCamIndex);
278  }
279 
280  if (n > 0 && extra_outputs::haveHMDExtraOutputs) {
282  auto &body0 = body["0"];
283  std::string oldTarget = body0.asString();
284  body0 = Json::Value(Json::objectValue);
285  body0["$target"] = oldTarget;
286 
287  if (extra_outputs::outputImu) {
288  body0["imu"] =
289  getTrackerPath(n + extra_outputs::outputImuIndex);
290  }
291  if (extra_outputs::haveHMDCameraSpaceExtraOutputs) {
292  auto &cameraSpace = body0["cameraSpace"];
293  cameraSpace = Json::Value(Json::objectValue);
294 
295  if (extra_outputs::outputImuCam) {
296  cameraSpace["imu"] =
297  getTrackerPath(n + extra_outputs::outputImuCamIndex);
298  }
299  if (extra_outputs::outputHMDCam) {
300  cameraSpace["body"] =
301  getTrackerPath(n + extra_outputs::outputHMDCamIndex);
302  }
303  }
304  }
305 
307  Json::FastWriter writer;
308  return writer.write(root);
309  }
310 
311  OSVR_ReturnCode update();
312 
313  void startTrackerThread() {
314  if (m_trackerThreadManager) {
315  throw std::logic_error("Trying to start the tracker thread when "
316  "it's already started!");
317  }
318  std::cout << "Starting the tracker thread..." << std::endl;
319  m_trackerThreadManager.reset(new TrackerThread(
320  *m_trackingSystem, *m_source, m_bodyReportingVector,
321  osvr::vbtracker::getHDKCameraParameters(), m_camUsecOffset,
322  !m_continuousReporting, m_debugData));
323 
326  m_trackerThread =
327  std::thread([&] { m_trackerThreadManager->threadAction(); });
328  }
329  void stopTrackerThread() {
330  if (m_trackerThreadManager) {
331  std::cout << "Shutting down the tracker thread..." << std::endl;
332  m_trackerThreadManager->triggerStop();
333  if (m_trackerThread.joinable()) {
334  m_trackerThread.join();
335  }
336  m_trackerThreadManager.reset();
337  m_trackerThread = std::thread();
338  }
339  }
340 };
341 
342 inline OSVR_ReturnCode UnifiedVideoInertialTracker::update() {
343  if (!m_threadLoopStarted) {
345  m_threadLoopStarted = true;
346  m_trackerThreadManager->permitStart();
347  return OSVR_RETURN_SUCCESS;
348  }
349  namespace ei = osvr::util::eigen_interop;
350  std::size_t numSensors = m_bodyReportingVector.size();
353  for (std::size_t i = 0; i < numSensors; ++i) {
355  auto gotReport =
356  m_bodyReportingVector[i]->getReport(m_additionalPrediction, report);
357  if (!gotReport) {
359  // std::cout << "Couldn't get report for " << i << std::endl;
360  continue;
361  }
362  osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &report.pose, i,
363  &report.timestamp);
364  if (OSVR_TRUE == report.vel.angularVelocityValid ||
365  OSVR_TRUE == report.vel.linearVelocityValid) {
367  m_dev, m_tracker, &report.vel, i, &report.timestamp);
368  }
369  }
370  if (m_debugData) {
371  osvr::vbtracker::DebugArray arr;
373  while (m_trackerThreadManager->checkForDebugData(arr)) {
374  osvrDeviceAnalogSetValues(m_dev, m_analog, arr.data(), arr.size());
375  }
376  }
377  return OSVR_RETURN_SUCCESS;
378 }
379 
380 class ConfiguredDeviceConstructor {
381  public:
384  OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx, const char *params) {
385 
386  // Read the JSON data from parameters.
387  Json::Value root;
388  if (params) {
389  Json::Reader r;
390  if (!r.parse(params, root)) {
391  std::cerr << "Could not parse parameters!" << std::endl;
392  }
393  }
394 
395  // Read these parameters from a "params" field in the device Json
396  // configuration file.
397 
398  // This is in a separate function/header for sharing and for clarity.
399  auto config = osvr::vbtracker::parseConfigParams(root);
400 
401 #ifdef _WIN32
402  auto cam = osvr::vbtracker::openHDKCameraDirectShow(config.highGain);
403 #else // !_WIN32
404 
409  auto cam = osvr::vbtracker::openOpenCVCamera(0);
410 #endif
411 
412  if (!cam || !cam->ok()) {
413  std::cerr << "Could not access the tracking camera, skipping "
414  "video-based tracking!"
415  << std::endl;
416  return OSVR_RETURN_FAILURE;
417  }
418 
419  auto trackingSystem = osvr::vbtracker::makeHDKTrackingSystem(config);
420  // OK, now that we have our parameters, create the device.
421  osvr::pluginkit::PluginContext context(ctx);
423  ctx, new UnifiedVideoInertialTracker(ctx, std::move(cam), config,
424  std::move(trackingSystem)));
425 
426  return OSVR_RETURN_SUCCESS;
427  }
428 };
429 
430 } // namespace
431 
432 OSVR_PLUGIN(org_osvr_unifiedvideoinertial) {
433  osvr::pluginkit::PluginContext context(ctx);
434 
437  ctx, DRIVER_NAME, new ConfiguredDeviceConstructor);
438 
439  return OSVR_RETURN_SUCCESS;
440 }
OSVR_ReturnCode osvrDeviceAnalogSetValues(OSVR_DeviceToken dev, OSVR_AnalogDeviceInterface iface, OSVR_AnalogState val[], OSVR_ChannelCount chans)
Report the value of multiple channels.
OSVR_ReturnCode osvrRegisterAngularVelocityCallback(OSVR_ClientInterface iface, OSVR_AngularVelocityCallback cb, void *userdata)
Register a callback for AngularVelocity reports on an interface.
Wrapper class for OSVR_DeviceToken.
OSVR_ReturnCode osvrClientGetInterface(OSVR_ClientContext ctx, const char path[], OSVR_ClientInterface *iface)
Get the interface associated with the given path.
Definition: InterfaceC.cpp:37
OSVR_PLUGIN(org_osvr_unifiedvideoinertial)
OSVR_ReturnCode osvrRegisterOrientationCallback(OSVR_ClientInterface iface, OSVR_OrientationCallback cb, void *userdata)
Register a callback for Orientation reports on an interface.
A DeviceToken connects the generic device interaction code in PluginKit's C API with the workings of ...
Definition: DeviceToken.h:56
STL namespace.
C++ wrapper class for the opaque plugin context.
Definition: PluginKit.h:59
#define OSVR_TRUE
Canonical "true" value for OSVR_CBool.
Definition: BoolC.h:52
Structure used internally to construct the desired type of device.
Header.
#define OSVR_RETURN_FAILURE
The "failure" value for an OSVR_ReturnCode.
Definition: ReturnCodesC.h:47
IMUInputParams imu
IMU input-related parameters.
Definition: ConfigParams.h:254
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.
void registerUpdateCallback(DeviceObjectType *object)
Given a pointer to your object that has a public OSVR_ReturnCode update() method, registers that inst...
static std::unique_ptr< BodyReporting > make()
Factory function.
Report type for an orientation callback on a tracker interface.
OSVR_CBool linearVelocityValid
Whether the data source reports valid data for #OSVR_VelocityState::linearVelocity.
Header including the full PluginKit C++ interface.
OSVR_CBool angularVelocityValid
Whether the data source reports valid data for #OSVR_VelocityState::angularVelocity.
Header.
Header for interoperation between the Eigen math library, the internal mini math library, and VRPN's quatlib.
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...
Report type for an angular velocity callback on a tracker interface.
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
Definition: TimeValueC.h:81
void osvrTimeValueSum(OSVR_TimeValue *tvA, const OSVR_TimeValue *tvB)
Sums two time values, replacing the first with the result.
Definition: TimeValueC.cpp:65
void sendJsonDescriptor(const char *json, size_t len)
Submit a JSON self-descriptor string for the device.
OSVR_ANALYSISPLUGINKIT_EXPORT OSVR_ReturnCode osvrAnalysisSyncInit(OSVR_PluginRegContext ctx, const char *name, OSVR_DeviceInitOptions options, OSVR_DeviceToken *device, OSVR_ClientContext *clientCtx)
Initialize a synchronous analysis device token with the options specified, also returning the associa...
OSVR_ReturnCode osvrDeviceTrackerSendVelocityTimestamped(OSVR_DeviceToken dev, OSVR_TrackerDeviceInterface iface, OSVR_VelocityState const *val, OSVR_ChannelCount sensor, OSVR_TimeValue const *timestamp)
Report the linear and angular velocity of a sensor, using the supplied timestamp. ...
OSVR_ReturnCode osvrDeviceAnalogConfigure(OSVR_DeviceInitOptions opts, OSVR_AnalogDeviceInterface *iface, OSVR_ChannelCount numChan)
Specify that your device will implement the Analog interface.