25 #ifndef INCLUDED_AssignMeasurementsToLeds_h_GUID_F7146BCA_13BF_4C91_1EE6_27E9FF039AD7 26 #define INCLUDED_AssignMeasurementsToLeds_h_GUID_F7146BCA_13BF_4C91_1EE6_27E9FF039AD7 34 #include <boost/assert.hpp> 35 #include <opencv2/core/core.hpp> 58 std::cerr <<
"Got a beacon claiming to be " 60 << numBeacons <<
" beacons" << std::endl;
70 inline float sqDist(cv::Point2f
const &lhs, cv::Point2f
const &rhs) {
71 auto diff = lhs - rhs;
72 return diff.dot(diff);
76 static const char *getPrefix() {
return "[AssignMeasurements] "; }
80 LedMeasurementVec
const &measurements,
81 const std::size_t numBeacons,
82 float blobMoveThresh,
bool verbose =
false)
83 : leds_(leds), measurements_(measurements), ledsEnd_(end(leds_)),
84 numBeacons_(numBeacons), blobMoveThreshFactor_(blobMoveThresh),
85 maxMatches_(std::min(leds_.size(), measurements_.size())),
88 using LedAndMeasurement = std::pair<Led &, LedMeasurement const &>;
90 using LedMeasDistance = std::tuple<std::size_t, std::size_t, float>;
91 using HeapValueType = LedMeasDistance;
92 using HeapType = std::vector<HeapValueType>;
93 using size_type = HeapType::size_type;
97 BOOST_ASSERT_MSG(!populated_,
98 "Can only call populateStructures() once.");
102 auto led = begin(leds_);
103 while (led != end(leds_)) {
105 ledRefs_.push_back(led);
110 for (
auto &meas : measurements_) {
112 measRefs_.push_back(&meas);
117 auto nMeas = measRefs_.size();
118 auto nLed = ledRefs_.size();
119 for (size_type measIdx = 0; measIdx < nMeas; ++measIdx) {
120 auto distThreshSquared =
121 getDistanceThresholdSquared(*measRefs_[measIdx]);
122 for (size_type ledIdx = 0; ledIdx < nLed; ++ledIdx) {
125 possiblyPushLedMeasurement(ledIdx, measIdx,
146 checkAndThrowNotPopulated(
"discardInvalidEntries()");
147 size_type discarded = 0;
153 if (verbose || verbose_) {
154 auto top = distanceHeap_.front();
155 std::cout << getPrefix() <<
"top: led index " 156 << ledIndex(top) <<
"\tmeas index " 157 << measIndex(top) <<
"\tsq dist " 158 << squaredDistance(top);
161 if (verbose || verbose_) {
162 std::cout <<
" isTopValid() says keep!\n";
167 if (verbose || verbose_) {
168 std::cout <<
" isTopValid() says discard!\n";
179 if (numMatches_ == 0) {
185 auto it = std::find(begin(measurements_), end(measurements_), meas);
186 if (it == end(measurements_)) {
191 auto idx = std::distance(begin(measurements_), it);
192 if (isMeasValid(idx)) {
193 std::cerr <<
"Trying to resubmit, but the measurement wasn't " 194 "marked as consumed!" 201 measRefs_[idx] = &(*it);
209 checkAndThrowNotPopulated(
"hasMoreMatches()");
213 if (verbose || verbose_) {
214 std::cout << getPrefix() <<
"hasMoreMatches: Already " 215 "reached our limit for " 221 if (verbose || verbose_) {
222 std::cout << getPrefix() <<
"hasMoreMatches: Discarded " 223 << discarded <<
" invalid entries.\n";
233 checkAndThrowNotPopulated(
"getMatch()");
238 throw std::logic_error(
"Can't call getMatch() without first " 239 "getting success from hasMoreMatches()");
242 if (verbose || verbose_) {
243 std::cout << getPrefix() <<
"Led Index " 244 << ledIndex(distanceHeap_.front()) <<
"\tMeas Index " 245 << measIndex(distanceHeap_.front()) << std::endl;
247 BOOST_ASSERT_MSG(isTopValid(),
"Shouldn't be able to get here " 248 "without a valid top element on the " 251 auto &topLed = *getTopLed();
252 auto &topMeas = *getTopMeasurement();
259 BOOST_ASSERT(!isTopValid());
268 return LedAndMeasurement(topLed, topMeas);
276 BOOST_ASSERT_MSG(populated_,
"Should have called " 277 "populateStructures() before calling " 278 "haveMadeMaxMatches().");
279 return numMatches_ == maxMatches_;
291 BOOST_ASSERT_MSG(populated_,
"Should have called " 292 "populateStructures() before calling " 294 return distanceHeap_.empty();
301 BOOST_ASSERT_MSG(populated_,
"Should have called " 302 "populateStructures() before " 304 return distanceHeap_.size();
310 return leds_.size() * measurements_.size();
317 BOOST_ASSERT_MSG(populated_,
"Should have called " 318 "populateStructures() before calling " 319 "heapSizeFraction().");
320 return static_cast<double>(
size()) /
324 size_type numUnclaimedLedObjects()
const {
325 return std::count_if(
326 begin(ledRefs_), end(ledRefs_),
327 [&](LedIter
const &it) {
return it != ledsEnd_; });
331 for (
auto &ledIter : ledRefs_) {
332 if (ledIter == ledsEnd_) {
337 if (ledIter->identified()) {
338 std::cout <<
"Erasing identified LED " 339 << ledIter->getOneBasedID().value()
340 <<
" because of a lack of updated data.\n";
342 std::cout <<
"Erasing unidentified LED at " 343 << ledIter->getLocation()
344 <<
" because of a lack of updated data.\n";
347 leds_.erase(ledIter);
351 size_type numUnclaimedMeasurements()
const {
352 return std::count_if(
353 begin(measRefs_), end(measRefs_),
354 [&](MeasPtr
const &ptr) {
return ptr !=
nullptr; });
358 for (
auto &measRef : measRefs_) {
364 std::forward<F>(op)(*measRef);
368 size_type numCompletedMatches()
const {
return numMatches_; }
371 using LedIter = LedGroup::iterator;
374 void checkAndThrowNotPopulated(
const char *functionName)
const {
376 throw std::logic_error(
"Must have called " 377 "populateStructures() before " 379 std::string(functionName));
385 static std::size_t ledIndex(LedMeasDistance
const &val) {
386 return std::get<0>(val);
388 static std::size_t &ledIndex(LedMeasDistance &val) {
389 return std::get<0>(val);
391 static std::size_t measIndex(LedMeasDistance
const &val) {
392 return std::get<1>(val);
394 static std::size_t &measIndex(LedMeasDistance &val) {
395 return std::get<1>(val);
397 static float squaredDistance(LedMeasDistance
const &val) {
398 return std::get<2>(val);
402 void possiblyPushLedMeasurement(std::size_t ledIdx, std::size_t measIdx,
403 float distThreshSquared) {
404 auto meas = measRefs_[measIdx];
405 auto led = ledRefs_[ledIdx];
406 auto squaredDist =
sqDist(led->getLocation(), meas->loc);
407 if (squaredDist < distThreshSquared) {
410 distanceHeap_.emplace_back(ledIdx, measIdx, squaredDist);
413 LedIter getTopLed()
const {
414 return ledRefs_[ledIndex(distanceHeap_.front())];
417 MeasPtr getTopMeasurement()
const {
418 return measRefs_[measIndex(distanceHeap_.front())];
421 bool isLedValid(size_type idx)
const {
422 return (ledRefs_[idx] != ledsEnd_);
425 bool isLedValid(LedMeasDistance
const &elt)
const {
426 return (ledRefs_[ledIndex(elt)] != ledsEnd_);
429 bool isMeasValid(size_type idx)
const {
430 return (measRefs_[idx] !=
nullptr);
433 bool isMeasValid(LedMeasDistance
const &elt)
const {
434 return (measRefs_[measIndex(elt)] !=
nullptr);
437 bool isTopValid()
const {
438 LedMeasDistance elt = distanceHeap_.front();
439 return isLedValid(elt) && isMeasValid(elt);
442 void markTopConsumed() {
443 LedMeasDistance elt = distanceHeap_.front();
444 ledRefs_[ledIndex(elt)] = ledsEnd_;
445 measRefs_[measIndex(elt)] =
nullptr;
447 BOOST_ASSERT(!isLedValid(elt));
448 BOOST_ASSERT(!isMeasValid(elt));
456 float getDistanceThresholdSquared(
LedMeasurement const &meas)
const {
457 auto thresh = blobMoveThreshFactor_ * meas.
diameter;
458 return thresh * thresh;
465 bool operator()(HeapValueType
const &lhs,
466 HeapValueType
const &rhs)
const {
467 return squaredDistance(lhs) > squaredDistance(rhs);
474 std::make_heap(begin(distanceHeap_), end(distanceHeap_),
480 BOOST_ASSERT_MSG(!distanceHeap_.empty(),
481 "Cannot pop from an empty heap");
484 std::pop_heap(begin(distanceHeap_), end(distanceHeap_),
487 distanceHeap_.pop_back();
490 bool populated_ =
false;
491 std::vector<LedIter> ledRefs_;
492 std::vector<MeasPtr> measRefs_;
493 HeapType distanceHeap_;
494 size_type numMatches_ = 0;
496 LedMeasurementVec
const &measurements_;
497 const LedIter ledsEnd_;
498 const std::size_t numBeacons_;
499 const float blobMoveThreshFactor_;
500 const size_type maxMatches_;
507 #endif // INCLUDED_AssignMeasurementsToLeds_h_GUID_F7146BCA_13BF_4C91_1EE6_27E9FF039AD7 Helper class to keep track of the state of a blob over time.
Definition: LED.h:47
ZeroBasedBeaconId makeZeroBased(OneBasedBeaconId id)
Overloaded conversion function to turn any beacon ID into zero-based, respecting the convention that ...
Definition: BeaconIdTypes.h:99
void eraseUnclaimedLedObjects(bool verbose=false)
Definition: AssignMeasurementsToLeds.h:330
The main namespace for all C++ elements of the framework, internal and external.
Definition: namespace_osvr.dox:3
bool empty() const
Is our heap of possibilities empty?
Definition: AssignMeasurementsToLeds.h:288
bool haveMadeMaxMatches() const
Have we made as many matches as we possibly can? (that is, the minimum of the number of LED objects a...
Definition: AssignMeasurementsToLeds.h:273
size_type maxMatches() const
The maximum number of matches theoretically achievable with this input: the minimum of the number of ...
Definition: AssignMeasurementsToLeds.h:285
size_type theoreticalMaxSize() const
This is the size it could have potentially been, had all LEDs been within the distance threshold...
Definition: AssignMeasurementsToLeds.h:309
ZeroBasedBeaconId getID() const
Tells which LED I am.
Definition: LED.h:89
Header file for class that tracks and identifies LEDs.
void markMisidentified()
Called from within pose estimation or elsewhere with model-based knowledge that can refute the identi...
Definition: LED.cpp:76
void populateStructures()
Must call first, and only once.
Definition: AssignMeasurementsToLeds.h:96
Definition: AssignMeasurementsToLeds.h:75
bool resumbitMeasurement(LedMeasurement const &meas)
In case a measurement update goes bad, we can try to "un-mark" a measurement as consumed.
Definition: AssignMeasurementsToLeds.h:178
float sqDist(cv::Point2f const &lhs, cv::Point2f const &rhs)
Get the squared distance between two OpenCV points.
Definition: AssignMeasurementsToLeds.h:70
OneBasedBeaconId getOneBasedID() const
Gets either the raw negative sentinel ID or a 1-based ID (for display purposes)
Definition: LED.h:93
bool identified() const
Do we have a positive identification as a known LED?
Definition: LED.h:96
float diameter
Blob diameter in pixels.
Definition: LedMeasurement.h:86
void forEachUnclaimedMeasurement(F &&op)
Definition: AssignMeasurementsToLeds.h:357
Definition: LedMeasurement.h:41
double heapSizeFraction() const
The fraction of the theoretical max that the size is.
Definition: AssignMeasurementsToLeds.h:314
bool hasMoreMatches(bool verbose=false)
Searches the heap, discarding now-invalid entries, until it finds an entry where both the LED and the...
Definition: AssignMeasurementsToLeds.h:208
bool handleOutOfRangeIds(Led &led, const std::size_t numBeacons)
In theory this shouldn't happen, but there are checks scattered all over the code.
Definition: AssignMeasurementsToLeds.h:54
LedAndMeasurement getMatch(bool verbose=false)
Requires that hasMoreMatches() has been run and returns true.
Definition: AssignMeasurementsToLeds.h:232
int UnderlyingBeaconIdType
All beacon IDs, whether 0 or 1 based, are ints on the inside.
Definition: BeaconIdTypes.h:49
size_type discardInvalidEntries(bool verbose=false)
Discards invalid entries (those where either the LED or the measurement, or both, have already been a...
Definition: AssignMeasurementsToLeds.h:145
size_type size() const
Entries in the heap of possibilities.
Definition: AssignMeasurementsToLeds.h:298