31 #include <opencv2/calib3d/calib3d.hpp>
32 #include <opencv2/core/affine.hpp>
43 static const double LINEAR_SCALE_FACTOR = 1000.;
45 static const auto DEFAULT_MEASUREMENT_VARIANCE = 3.0;
49 static const std::size_t MAX_PROBATION_FRAMES = 10;
51 static const std::size_t MAX_FRAMES_WITHOUT_MEASUREMENTS = 50;
53 static const std::size_t MAX_FRAMES_WITHOUT_ID_BLOBS = 10;
58 : m_params(params), m_camParams(camParams) {
60 m_requiredInliers = requiredInliers;
61 m_permittedOutliers = permittedOutliers;
65 const Point3Vector &beacons, Vec3Vector
const &emissionDirection,
66 std::vector<double>
const &variance,
67 BeaconIDPredicate
const &autocalibrationFixedPredicate,
68 double beaconAutocalibErrorScale) {
72 m_updateBeaconCentroid(beacons);
73 Eigen::Matrix3d beaconError =
76 beaconAutocalibErrorScale;
77 auto bNum =
size_t{0};
78 for (
auto &beacon : beacons) {
79 auto isFixed = autocalibrationFixedPredicate(bNum + 1);
80 m_beaconFixed.push_back(isFixed);
82 cvToVector(beacon).cast<
double>() - m_centroid,
83 isFixed ? Eigen::Matrix3d::Zero() : beaconError});
86 if (1 == variance.size()) {
89 m_beaconMeasurementVariance.resize(m_beacons.size(), variance[0]);
91 m_beaconMeasurementVariance = variance;
94 m_beaconMeasurementVariance.resize(m_beacons.size(),
95 DEFAULT_MEASUREMENT_VARIANCE);
97 m_beaconEmissionDirection = emissionDirection;
98 if (m_beaconEmissionDirection.size() != m_beacons.size()) {
99 throw std::runtime_error(
"Beacon emission direction array size did "
100 "not match number of beacons!");
103 m_updateBeaconDebugInfoArray();
107 void BeaconBasedPoseEstimator::m_updateBeaconCentroid(
108 const Point3Vector &beacons) {
110 Eigen::Vector3d beaconSum = Eigen::Vector3d::Zero();
111 auto bNum =
size_t{0};
112 for (
auto &beacon : beacons) {
113 beaconSum += cvToVector(beacon).cast<
double>();
116 m_centroid = beaconSum / bNum;
117 if (m_params.
debug) {
118 std::cout <<
"Beacon centroid: " << m_centroid.transpose()
126 void BeaconBasedPoseEstimator::m_updateBeaconDebugInfoArray() {
127 m_beaconDebugData.resize(m_beacons.size());
130 bool BeaconBasedPoseEstimator::SetCameraParameters(
131 CameraParameters
const &camParams) {
135 m_camParams = camParams;
141 util::eigen_interop::map(ret).
rotation() = m_state.getQuaternion();
142 util::eigen_interop::map(ret).translation() =
143 m_convertInternalPositionRepToExternal(m_state.position());
147 Eigen::Vector3d BeaconBasedPoseEstimator::GetLinearVelocity()
const {
148 return m_state.velocity() / LINEAR_SCALE_FACTOR;
151 Eigen::Vector3d BeaconBasedPoseEstimator::GetAngularVelocity()
const {
152 return m_state.angularVelocity();
157 auto ret = m_estimatePoseFromLeds(leds, tv, outPose);
162 BeaconBasedPoseEstimator::m_convertInternalPositionRepToExternal(
163 Eigen::Vector3d
const &pos)
const {
164 return (pos + m_centroid) / LINEAR_SCALE_FACTOR;
166 bool BeaconBasedPoseEstimator::m_estimatePoseFromLeds(
171 for (
auto &data : m_beaconDebugData) {
177 auto didReset = m_forceRansacIfKalmanNeedsReset(leds);
178 if (didReset && m_params.
debug) {
179 std::cout <<
"Video-based tracker: lost fix, in-flight reset"
183 bool usedKalman =
false;
185 if (!m_gotPose || !m_gotPrev || !m_permitKalman) {
187 result = m_pnpransacEstimator(leds);
191 result = m_kalmanAutocalibEstimator(leds, dt);
205 outPose = GetState();
214 bool BeaconBasedPoseEstimator::m_forceRansacIfKalmanNeedsReset(
215 LedGroup
const &leds) {
216 auto needsReset = (m_framesInProbation > MAX_PROBATION_FRAMES) ||
217 (m_framesWithoutUtilizedMeasurements >
218 MAX_FRAMES_WITHOUT_MEASUREMENTS);
221 m_framesWithoutIdentifiedBlobs > MAX_FRAMES_WITHOUT_ID_BLOBS) {
223 auto const beaconsSize = m_beacons.size();
224 for (
auto const &led : leds) {
225 if (!led.identified()) {
228 auto id = led.getID();
229 if (
id < beaconsSize) {
236 m_framesInProbation = 0;
237 m_framesWithoutUtilizedMeasurements = 0;
238 m_framesWithoutIdentifiedBlobs = 0;
245 using StateVec = kalman::types::DimVector<State>;
255 bool BeaconBasedPoseEstimator::m_pnpransacEstimator(LedGroup &leds) {
263 std::vector<cv::Point3f> objectPoints;
264 std::vector<cv::Point2f> imagePoints;
265 auto const beaconsSize = m_beacons.size();
266 for (
auto const &led : leds) {
267 if (!led.identified()) {
270 auto id = led.getID();
271 if (
id < beaconsSize) {
272 m_beaconDebugData[id].variance = -1;
273 m_beaconDebugData[id].measurement = led.getLocation();
274 imagePoints.push_back(led.getLocation());
275 objectPoints.push_back(
276 vec3dToCVPoint3f(m_beacons[
id]->stateVector()));
281 if (objectPoints.size() < m_permittedOutliers + m_requiredInliers) {
293 const float MaxReprojectionErrorForInlier = 87.0f;
294 bool usePreviousGuess =
false;
295 int iterationsCount = 5;
296 cv::Mat inlierIndices;
298 #if CV_MAJOR_VERSION == 2
300 objectPoints, imagePoints, m_camParams.cameraMatrix,
301 m_camParams.distortionParameters, m_rvec, m_tvec, usePreviousGuess,
302 iterationsCount, MaxReprojectionErrorForInlier,
303 static_cast<int>(objectPoints.size() - m_permittedOutliers),
305 #elif CV_MAJOR_VERSION == 3
310 double confidence = 0.99;
311 auto ransacResult = cv::solvePnPRansac(
312 objectPoints, imagePoints, m_camParams.cameraMatrix,
313 m_camParams.distortionParameters, m_rvec, m_tvec, usePreviousGuess,
314 iterationsCount, 8.0f, confidence, inlierIndices);
319 #error "Unrecognized OpenCV version!"
325 if (inlierIndices.rows < m_requiredInliers) {
332 const double pixelReprojectionErrorForSingleAxisMax = 4;
333 if (inlierIndices.rows > 0) {
334 std::vector<cv::Point3f> inlierObjectPoints;
335 std::vector<cv::Point2f> inlierImagePoints;
336 for (
int i = 0; i < inlierIndices.rows; i++) {
337 inlierObjectPoints.push_back(objectPoints[i]);
338 inlierImagePoints.push_back(imagePoints[i]);
340 std::vector<cv::Point2f> reprojectedPoints;
342 inlierObjectPoints, m_rvec, m_tvec, m_camParams.cameraMatrix,
343 m_camParams.distortionParameters, reprojectedPoints);
345 for (
size_t i = 0; i < reprojectedPoints.size(); i++) {
346 if (reprojectedPoints[i].x - inlierImagePoints[i].x >
347 pixelReprojectionErrorForSingleAxisMax) {
350 if (reprojectedPoints[i].y - inlierImagePoints[i].y >
351 pixelReprojectionErrorForSingleAxisMax) {
381 m_gotMeasurement =
true;
383 if (m_tvec.at<
double>(2) < 0) {
387 std::cout <<
"On the wrong side of the looking glass:" << m_tvec
395 auto rotMat = cv::Affine3d::Mat3::eye();
400 cv::Mat(cv::Affine3d(m_rvec) * (cv::Affine3d(rotMat)).rvec());
403 m_resetState(cvToVector3d(m_tvec), cvRotVecToQuat(m_rvec));
408 std::vector<cv::Point2f> &out) {
414 Point3Vector beacons;
415 for (
auto const &beacon : m_beacons) {
416 beacons.push_back(vec3dToCVPoint3f(beacon->stateVector()));
419 cv::projectPoints(beacons, m_rvec, m_tvec, m_camParams.cameraMatrix,
420 m_camParams.distortionParameters, out);
425 m_permitKalman = permitKalman;
429 BeaconBasedPoseEstimator::getBeaconAutocalibPosition(std::size_t i)
const {
430 return m_beacons.at(i)->stateVector();
434 BeaconBasedPoseEstimator::getBeaconAutocalibVariance(std::size_t i)
const {
435 return m_beacons.at(i)->errorCovariance().diagonal();
439 static const double InitialStateError[] = {.01, .01, .1, 1., 1., .1,
440 10., 10., 10., 10., 10., 10.};
442 static const double InitialStateError[] = {0, 0, 0, 0, 0, 0,
443 10., 10., 10., 10., 10., 10.};
445 BeaconBasedPoseEstimator::m_resetState(Eigen::Vector3d
const &xlate,
446 Eigen::Quaterniond
const &quat) {
449 using StateVec = kalman::types::DimVector<State>;
451 m_state.position() = xlate;
454 m_state.setErrorCovariance(StateVec(InitialStateError).asDiagonal());
458 m_model.setNoiseAutocorrelation(
461 if (m_params.
debug && m_permitKalman) {
462 std::cout <<
"Video-based tracker: Beacon entering run state: pos:"
463 << m_state.position().transpose() <<
"\n orientation: "
464 << m_state.getQuaternion().coeffs().transpose()
469 void BeaconBasedPoseEstimator::dumpBeaconLocationsToStream(
470 std::ostream &os)
const {
471 Eigen::IOFormat ourFormat(Eigen::StreamPrecision, 0,
",");
472 for (
auto const &beacon : m_beacons) {
473 os << (beacon->stateVector() + m_centroid)
bool streamBeaconDebugInfo
OSVR_EXTERN_C_END double osvrTimeValueDurationSeconds(const OSVR_TimeValue *tvA, const OSVR_TimeValue *tvB)
Compute the difference between the two time values, returning the duration as a double-precision floa...
double linearVelocityDecayCoefficient
double processNoiseAutocorrelation[6]
void setQuaternion(Eigen::Quaterniond const &quaternion)
Intended for startup use.
void getNow(TimeValue &tv)
Set the given TimeValue to the current time.
void setDamping(double posDamping, double oriDamping)
Set the damping - must be in (0, 1)
OSVR_Quaternion rotation
Orientation as a unit quaternion.
double manualBeaconOffset[3]
Header wrapping include of and for warning quieting.
double initialBeaconError
bool SetBeacons(const Point3Vector &beacons, Vec3Vector const &emissionDirection, std::vector< double > const &variance, BeaconIDPredicate const &autocalibrationFixedPredicate, double beaconAutocalibErrorScale=1)
Header file for class that tracks and identifies LEDs.
General configuration parameters.
BeaconBasedPoseEstimator(CameraParameters const &camParams, size_t requiredInliers=4, size_t permittedOutliers=2, ConfigParams const ¶ms=ConfigParams{})
Constructor that expects its beacons to be set later. It is told the camera matrix and distortion coe...
bool debug
Whether to show the debug windows and debug messages.
A structure defining a 3D (6DOF) rigid body pose: translation and rotation.
Header for interoperation between the Eigen math library, the internal mini math library, and VRPN's quatlib.
double additionalPrediction
Seconds beyond the current time to predict, using the Kalman state.
Standardized, portable parallel to struct timeval for representing both absolute times and time inter...
void setStateVector(StateVector const &state)
set xhat
bool ProjectBeaconsToImage(std::vector< cv::Point2f > &outPose)
Project the beacons into image space given the most-recent estimation of pose.
void permitKalmanMode(bool permitKalman)
EIGEN_MAKE_ALIGNED_OPERATOR_NEW bool EstimatePoseFromLeds(LedGroup &leds, OSVR_TimeValue const &tv, OSVR_PoseState &out)
Produce an estimate of the pose of the model-space origin in camera space, where the origin is at the...
double angularVelocityDecayCoefficient