51 #include "org_osvr_unifiedvideoinertial_json.h"
54 #include <json/reader.h>
55 #include <json/value.h>
56 #include <json/writer.h>
57 #include <opencv2/core/core.hpp>
58 #include <opencv2/core/operations.hpp>
59 #include <opencv2/highgui/highgui.hpp>
60 #include <opencv2/imgproc/imgproc.hpp>
62 #include <boost/assert.hpp>
63 #include <boost/noncopyable.hpp>
64 #include <boost/variant.hpp>
66 #include <util/Stride.h>
79 static const auto DRIVER_NAME =
"UnifiedTrackingSystem";
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;
90 class UnifiedVideoInertialTracker : boost::noncopyable {
92 using size_type = std::size_t;
100 osvr::vbtracker::ImageSourcePtr m_source;
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;
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) {
144 osvr::vbtracker::DEBUG_ANALOGS_REQUIRED);
151 throw std::runtime_error(
"Could not initialize analysis plugin!");
158 m_mainBody = &(m_trackingSystem->getBody(BodyId(0)));
159 if (m_mainBody->hasIMU()) {
160 m_imu = &(m_mainBody->getIMU());
164 &m_clientInterface)) {
165 throw std::runtime_error(
166 "Could not get client interface for analysis plugin!");
169 m_clientInterface, &UnifiedVideoInertialTracker::oriCallback,
172 m_clientInterface, &UnifiedVideoInertialTracker::angVelCallback,
177 setupBodyReporting();
181 startTrackerThread();
188 template <
typename ReportType>
189 void handleData(
OSVR_TimeValue const ×tamp, ReportType
const &report) {
190 m_trackerThreadManager->submitIMUReport(*m_imu, timestamp, report);
193 static void oriCallback(
void *userdata,
const OSVR_TimeValue *timestamp,
195 auto &
self = *
static_cast<UnifiedVideoInertialTracker *
>(userdata);
197 if (
self.m_oriUsecOffset != 0) {
202 self.handleData(tv, *report);
205 static void angVelCallback(
void *userdata,
const OSVR_TimeValue *timestamp,
207 auto &
self = *
static_cast<UnifiedVideoInertialTracker *
>(userdata);
209 if (
self.m_angvelUsecOffset != 0) {
214 self.handleData(tv, *report);
217 ~UnifiedVideoInertialTracker() { stopTrackerThread(); }
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(
229 size_type numTrackedBodies()
const {
230 return m_trackingSystem->getNumBodies();
233 int totalNumBodies()
const {
234 static const auto EXTRA_TRACKING_SENSORS =
235 osvr::vbtracker::extra_outputs::numExtraOutputs;
237 return static_cast<int>(numTrackedBodies()) + EXTRA_TRACKING_SENSORS;
240 static std::string getTrackerPath(
int sensor) {
241 std::ostringstream os;
242 os <<
"tracker/" << sensor;
249 std::string createDeviceDescriptor()
const {
250 auto n = numTrackedBodies();
251 auto origJson = std::string{org_osvr_unifiedvideoinertial_json};
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!");
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);
275 if (extra_outputs::outputCam) {
277 getTrackerPath(n + extra_outputs::outputCamIndex);
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;
287 if (extra_outputs::outputImu) {
289 getTrackerPath(n + extra_outputs::outputImuIndex);
291 if (extra_outputs::haveHMDCameraSpaceExtraOutputs) {
292 auto &cameraSpace = body0[
"cameraSpace"];
293 cameraSpace = Json::Value(Json::objectValue);
295 if (extra_outputs::outputImuCam) {
297 getTrackerPath(n + extra_outputs::outputImuCamIndex);
299 if (extra_outputs::outputHMDCam) {
300 cameraSpace[
"body"] =
301 getTrackerPath(n + extra_outputs::outputHMDCamIndex);
307 Json::FastWriter writer;
308 return writer.write(root);
311 OSVR_ReturnCode update();
313 void startTrackerThread() {
314 if (m_trackerThreadManager) {
315 throw std::logic_error(
"Trying to start the tracker thread when "
316 "it's already started!");
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));
327 std::thread([&] { m_trackerThreadManager->threadAction(); });
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();
336 m_trackerThreadManager.reset();
337 m_trackerThread = std::thread();
342 inline OSVR_ReturnCode UnifiedVideoInertialTracker::update() {
343 if (!m_threadLoopStarted) {
345 m_threadLoopStarted =
true;
346 m_trackerThreadManager->permitStart();
350 std::size_t numSensors = m_bodyReportingVector.size();
353 for (std::size_t i = 0; i < numSensors; ++i) {
356 m_bodyReportingVector[i]->getReport(m_additionalPrediction, report);
367 m_dev, m_tracker, &report.vel, i, &report.timestamp);
371 osvr::vbtracker::DebugArray arr;
373 while (m_trackerThreadManager->checkForDebugData(arr)) {
380 class ConfiguredDeviceConstructor {
390 if (!r.parse(params, root)) {
391 std::cerr <<
"Could not parse parameters!" << std::endl;
399 auto config = osvr::vbtracker::parseConfigParams(root);
402 auto cam = osvr::vbtracker::openHDKCameraDirectShow(config.highGain);
409 auto cam = osvr::vbtracker::openOpenCVCamera(0);
412 if (!cam || !cam->ok()) {
413 std::cerr <<
"Could not access the tracking camera, skipping "
414 "video-based tracking!"
419 auto trackingSystem = osvr::vbtracker::makeHDKTrackingSystem(config);
423 ctx,
new UnifiedVideoInertialTracker(ctx, std::move(cam), config,
424 std::move(trackingSystem)));
437 ctx, DRIVER_NAME,
new ConfiguredDeviceConstructor);
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.
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 ...
C++ wrapper class for the opaque plugin context.
#define OSVR_TRUE
Canonical "true" value for OSVR_CBool.
Structure used internally to construct the desired type of device.
#define OSVR_RETURN_FAILURE
The "failure" value for an OSVR_ReturnCode.
IMUInputParams imu
IMU input-related parameters.
T * registerObjectForDeletion(OSVR_PluginRegContext ctx, T *obj)
Registers an object to be destroyed with delete when the plugin is unloaded.
General configuration parameters.
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.
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 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...
void osvrTimeValueSum(OSVR_TimeValue *tvA, const OSVR_TimeValue *tvB)
Sums two time values, replacing the first with the result.
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.