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>
54 inline bool handleOutOfRangeIds(Led &led,
const std::size_t numBeacons) {
55 if (led.identified() &&
56 makeZeroBased(led.getID()).value() >
57 static_cast<UnderlyingBeaconIdType
>(numBeacons)) {
58 std::cerr <<
"Got a beacon claiming to be "
59 << led.getOneBasedID().value() <<
" when we only have "
60 << numBeacons <<
" beacons" << std::endl;
63 led.markMisidentified();
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;
372 using LedPtr = Led *;
373 using MeasPtr = LedMeasurement
const *;
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
void eraseUnclaimedLedObjects(bool verbose=false)
size_type theoreticalMaxSize() const
size_type maxMatches() const
Header file for class that tracks and identifies LEDs.
void populateStructures()
Must call first, and only once.
bool empty() const
Is our heap of possibilities empty?
bool haveMadeMaxMatches() const
bool resumbitMeasurement(LedMeasurement const &meas)
double heapSizeFraction() const
The fraction of the theoretical max that the size is.
size_type size() const
Entries in the heap of possibilities.
void forEachUnclaimedMeasurement(F &&op)
bool hasMoreMatches(bool verbose=false)
LedAndMeasurement getMatch(bool verbose=false)
Requires that hasMoreMatches() has been run and returns true.
size_type discardInvalidEntries(bool verbose=false)