7 #include <unordered_map>
17 const unsigned char *,
const Stars &stars,
const Catalog &catalog,
const Camera &)
const {
21 unsigned int randomSeed = 123456;
22 for (
int i = 0; i < (int)stars.size(); i++) {
23 result.push_back(
StarIdentifier(i, rand_r(&randomSeed) % catalog.size()));
30 const unsigned char *database,
const Stars &stars,
const Catalog &catalog,
const Camera &camera)
const {
35 if (databaseBuffer == NULL) {
41 for (
int i = 0; i < (int)stars.size(); i++) {
42 std::vector<int16_t> votes(catalog.size(), 0);
44 for (
int j = 0; j < (int)stars.size(); j++) {
47 std::vector<bool> votedInPair(catalog.size(),
false);
51 decimal lowerBoundRange = greatCircleDistance - tolerance;
52 decimal upperBoundRange = greatCircleDistance + tolerance;
53 const int16_t *upperBoundSearch;
55 lowerBoundRange, upperBoundRange, &upperBoundSearch);
57 for (
const int16_t *k = lowerBoundSearch; k != upperBoundSearch; k++) {
58 if ((k - lowerBoundSearch) % 2 == 0) {
59 decimal actualAngle =
AngleUnit(catalog[*k].spatial, catalog[*(k+1)].spatial);
60 assert(actualAngle <= greatCircleDistance + tolerance * 2);
61 assert(actualAngle >= greatCircleDistance - tolerance * 2);
63 if (!votedInPair[*k] ||
true) {
69 votedInPair[*k] =
true;
76 int16_t maxVotes = votes[0];
78 for (
int v = 1; v < (int)votes.size(); v++) {
79 if (votes[v] > maxVotes) {
101 identified.push_back(newStar);
108 std::vector<int16_t> verificationVotes(identified.size(), 0);
109 for (
int i = 0; i < (int)identified.size(); i++) {
111 for (
int j = i + 1; j < (int)identified.size(); j++) {
113 CatalogStar first = catalog[identified[i].catalogIndex];
114 CatalogStar second = catalog[identified[j].catalogIndex];
117 Star firstIdentified = stars[identified[i].starIndex];
118 Star secondIdentified = stars[identified[j].starIndex];
126 verificationVotes[i]++;
127 verificationVotes[j]++;
132 int maxVotes = verificationVotes.size() > 0 ? verificationVotes[0] : 0;
133 for (
int v = 1; v < (int)verificationVotes.size(); v++) {
134 if (verificationVotes[v] > maxVotes) {
135 maxVotes = verificationVotes[v];
143 int thresholdVotes = maxVotes * 3 / 4;
144 printf(
"Verification threshold: %d\n", thresholdVotes);
145 for (
int i = 0; i < (int)verificationVotes.size(); i++) {
146 if (verificationVotes[i] > thresholdVotes) {
147 verified.push_back(identified[i]);
176 : pairs(NULL), end(NULL) { };
185 : pairs(pairs), end(end), involving(involving) {
187 assert((end-pairs)%2 == 0);
188 ForwardUntilInvolving();
201 ForwardUntilInvolving();
223 const int16_t *pairs;
229 void ForwardUntilInvolving() {
230 while (pairs != end) {
231 if (pairs[0] == involving) {
235 if (pairs[1] == involving) {
245 std::vector<int16_t> result;
247 result.push_back(*it);
260 std::unordered_multimap<int16_t, int16_t> result;
261 for (
const int16_t *p = pairs; p != end; p += 2) {
262 result.emplace(p[0], p[1]);
263 result.emplace(p[1], p[0]);
281 for (
const auto &otherPair : identifiedStarsInRange) {
282 decimal curAngleFrom90 = VerticalAnglesToAngleFrom90(otherPair.first, angleFromVertical);
290 identifiedStarsInRange.emplace_back(angleFromVertical, starId);
300 std::vector<IRUnidentifiedCentroid *> *centroids,
const Star &star,
const Camera &camera,
308 std::vector<std::vector<IRUnidentifiedCentroid *>::iterator> result;
309 for (
auto it = centroids->begin(); it != centroids->end(); ++it) {
311 decimal angleCos = ourSpatial * theirSpatial;
312 if (angleCos >= minCos && angleCos <= maxCos) {
313 result.push_back(it);
351 std::vector<IRUnidentifiedCentroid *> *aboveThresholdCentroids,
352 std::vector<IRUnidentifiedCentroid *> *belowThresholdCentroids,
357 std::vector<int16_t> nowBelowThreshold;
360 (*centroidIt)->AddIdentifiedStar(starId, stars);
361 if ((*centroidIt)->bestAngleFrom90 <= angleFrom90Threshold) {
362 belowThresholdCentroids->push_back(*centroidIt);
363 nowBelowThreshold.push_back((*centroidIt)->index);
367 aboveThresholdCentroids->erase(std::remove_if(aboveThresholdCentroids->begin(), aboveThresholdCentroids->end(),
369 return std::find(nowBelowThreshold.begin(), nowBelowThreshold.end(), centroid->index) != nowBelowThreshold.end();
370 }), aboveThresholdCentroids->end());
386 int16_t catalogIndex1, int16_t catalogIndex2,
390 const int16_t *query1End;
391 const int16_t *query1 = db.
FindPairsExact(catalog, distance1-tolerance, distance1+tolerance, &query1End);
393 const Vec3 &spatial1 = catalog[catalogIndex1].spatial;
394 const Vec3 &spatial2 = catalog[catalogIndex2].spatial;
399 std::vector<int16_t> result;
405 Vec3 candidateSpatial = catalog[*candidateIt].spatial;
410 if (!(angle2 >= distance2-tolerance && angle2 <= distance2+tolerance)) {
415 decimal spectralTorch = cross * candidateSpatial;
418 if (spectralTorch <= 0) {
423 result.push_back(*candidateIt);
430 std::vector<IRUnidentifiedCentroid *> *belowThresholdCentroids) {
431 if (!belowThresholdCentroids->empty()) {
432 auto result = belowThresholdCentroids->back();
433 belowThresholdCentroids->pop_back();
438 auto bestAboveThreshold = std::min_element(aboveThresholdCentroids->begin(), aboveThresholdCentroids->end(),
440 return a->bestAngleFrom90 < b->bestAngleFrom90;
444 if (bestAboveThreshold != aboveThresholdCentroids->end() && (*bestAboveThreshold)->bestAngleFrom90 < 10) {
445 auto result = *bestAboveThreshold;
446 aboveThresholdCentroids->erase(bestAboveThreshold);
467 #ifdef LOST_DEBUG_PERFORMANCE
468 auto startTimestamp = std::chrono::steady_clock::now();
471 std::vector<IRUnidentifiedCentroid> allUnidentifiedCentroids;
472 std::vector<IRUnidentifiedCentroid *> aboveThresholdUnidentifiedCentroids;
473 std::vector<IRUnidentifiedCentroid *> belowThresholdUnidentifiedCentroids;
474 allUnidentifiedCentroids.reserve(stars.size());
475 for (
size_t i = 0; i < stars.size(); i++) {
479 aboveThresholdUnidentifiedCentroids.reserve(allUnidentifiedCentroids.size());
480 for (
size_t i = 0; i < allUnidentifiedCentroids.size(); i++) {
482 if (std::find_if(identifiers->begin(), identifiers->end(),
483 [&allUnidentifiedCentroids, i](
const StarIdentifier &identifier) {
484 return identifier.starIndex == allUnidentifiedCentroids[i].index;
485 }) == identifiers->end()) {
487 aboveThresholdUnidentifiedCentroids.push_back(&allUnidentifiedCentroids[i]);
499 for (
const auto &starId : *identifiers) {
501 &aboveThresholdUnidentifiedCentroids, &belowThresholdUnidentifiedCentroids,
507 int numExtraIdentifiedStars = 0;
510 while (!belowThresholdUnidentifiedCentroids.empty() || !aboveThresholdUnidentifiedCentroids.empty()) {
513 if (nextUnidentifiedCentroid == NULL) {
527 std::vector<int16_t> candidates =
540 if (candidates.size() != 1) {
541 if (candidates.size() > 1) {
542 std::cerr <<
"WARNING: Multiple catalog stars matched during identify remaining stars. This should be rare." << std::endl;
546 identifiers->emplace_back(nextUnidentifiedCentroid->
index, candidates[0]);
550 &aboveThresholdUnidentifiedCentroids, &belowThresholdUnidentifiedCentroids,
556 ++numExtraIdentifiedStars;
561 assert(belowThresholdUnidentifiedCentroids.empty());
563 #ifdef LOST_DEBUG_PERFORMANCE
564 auto endTimestamp = std::chrono::steady_clock::now();
565 std::cout <<
"IdentifyRemainingStarsPairDistance took " << std::chrono::duration_cast<std::chrono::microseconds>(endTimestamp - startTimestamp).count() <<
"us" << std::endl;
568 return numExtraIdentifiedStars;
572 const unsigned char *database,
const Stars &stars,
const Catalog &catalog,
const Camera &camera)
const {
577 if (databaseBuffer == NULL || stars.size() < 4) {
578 std::cerr <<
"Not enough stars, or database missing." << std::endl;
592 int numStars = (int)stars.size();
594 int across = floor(sqrt(numStars))*2;
595 int halfwayAcross = floor(sqrt(numStars)/2);
596 long totalIterations = 0;
598 int jMax = numStars - 3;
599 for (
int jIter = 0; jIter < jMax; jIter++) {
600 int dj = 1+(jIter+halfwayAcross)%jMax;
602 int kMax = numStars-dj-2;
603 for (
int kIter = 0; kIter < kMax; kIter++) {
604 int dk = 1+(kIter+across)%kMax;
606 int rMax = numStars-dj-dk-1;
607 for (
int rIter = 0; rIter < rMax; rIter++) {
608 int dr = 1+(rIter+halfwayAcross)%rMax;
610 int iMax = numStars-dj-dk-dr-1;
611 for (
int iIter = 0; iIter <= iMax; iIter++) {
612 int i = (iIter + iMax/2)%(iMax+1);
615 if (++totalIterations > cutoff) {
616 std::cerr <<
"Cutoff reached." << std::endl;
624 assert(i != j && j != k && k != r && i != k && i != r && j != r);
640 decimal expectedMismatches = expectedMismatchesConstant
643 / std::max(std::max(iSinInner, jSinInner), kSinInner);
645 if (expectedMismatches > maxMismatchProbability) {
646 std::cout <<
"skip: mismatch prob." << std::endl;
653 bool spectralTorch = iSpatial.
CrossProduct(jSpatial)*kSpatial > 0;
667 #define _CHECK_DISTANCE(_dist) if (_dist < vectorDatabase.MinDistance() + tolerance || _dist > vectorDatabase.MaxDistance() - tolerance) { continue; }
673 #undef _CHECK_DISTANCE
675 const int16_t *ijEnd, *ikEnd, *irEnd;
676 const int16_t *
const ijQuery = vectorDatabase.
FindPairsLiberal(ijDist - tolerance, ijDist + tolerance, &ijEnd);
677 const int16_t *
const ikQuery = vectorDatabase.
FindPairsLiberal(ikDist - tolerance, ikDist + tolerance, &ikEnd);
678 const int16_t *
const irQuery = vectorDatabase.
FindPairsLiberal(irDist - tolerance, irDist + tolerance, &irEnd);
683 int iMatch = -1, jMatch = -1, kMatch = -1, rMatch = -1;
684 for (
const int16_t *iCandidateQuery = ijQuery; iCandidateQuery != ijEnd; iCandidateQuery++) {
685 int iCandidate = *iCandidateQuery;
687 int jCandidate = (iCandidateQuery - ijQuery) % 2 == 0
689 : iCandidateQuery[-1];
691 const Vec3 &iCandidateSpatial = catalog[iCandidate].spatial;
692 const Vec3 &jCandidateSpatial = catalog[jCandidate].spatial;
696 for (
auto kCandidateIt = ikMap.equal_range(iCandidate); kCandidateIt.first != kCandidateIt.second; kCandidateIt.first++) {
698 int kCandidate = kCandidateIt.first->second;
699 Vec3 kCandidateSpatial = catalog[kCandidate].spatial;
700 bool candidateSpectralTorch = ijCandidateCross*kCandidateSpatial > 0;
702 if (candidateSpectralTorch != spectralTorch) {
708 if (jkCandidateDist < jkDist - tolerance || jkCandidateDist > jkDist + tolerance) {
715 for (
auto rCandidateIt = irMap.equal_range(iCandidate); rCandidateIt.first != rCandidateIt.second; rCandidateIt.first++) {
716 int rCandidate = rCandidateIt.first->second;
717 const Vec3 &rCandidateSpatial = catalog[rCandidate].spatial;
720 if (jrCandidateDist < jrDist - tolerance || jrCandidateDist > jrDist + tolerance) {
723 krCandidateDist =
AngleUnit(kCandidateSpatial, rCandidateSpatial);
724 if (krCandidateDist < krDist - tolerance || krCandidateDist > krDist + tolerance) {
738 std::cerr <<
"Pyramid not unique, skipping..." << std::endl;
748 std::cout.precision(6);
749 std::cout <<
"Matched unique pyramid!" << std::endl <<
"Expected mismatches: " << std::scientific << expectedMismatches << std::endl << std::fixed;
756 printf(
"Identified an additional %d stars.\n", numAdditionallyIdentified);
757 assert(numAdditionallyIdentified == (
int)identified.size()-4);
768 std::cerr <<
"Tried all pyramids; none matched." << std::endl;
A full description of a camera. Enough information to reconstruct the camera matrix and then some.
Vec3 CameraToSpatial(const Vec2 &) const
Gives a point in 3d space that could correspond to the given vector, using the same coordinate system...
A star from the Bright Star Catalog.
Vec3 spatial
The point on the unit sphere where the star lies.
StarIdentifiers Go(const unsigned char *database, const Stars &, const Catalog &, const Camera &) const
Actualy perform the star idenification. This is the "main" function for StarIdAlgorithm.
StarIdentifiers Go(const unsigned char *database, const Stars &, const Catalog &, const Camera &) const
Actualy perform the star idenification. This is the "main" function for StarIdAlgorithm.
unidentified centroid used in IdentifyRemainingStarsPairDistance The "angles" through here are "trian...
const Star * star
Index into list of all centroids.
StarIdentifier bestStar1
For the pair of other centroids forming the triangular angle closest to 90 degrees,...
void AddIdentifiedStar(const StarIdentifier &starId, const Stars &stars)
When a centroid within range of this centroid is identified, call this function.
StarIdentifier bestStar2
One star corresponding to bestAngleFrom90.
int16_t index
The other star corresponding to bestAngleFrom90.
const unsigned char * SubDatabasePointer(int32_t magicValue) const
MultiDatabase memory layout:
Given a list of star pairs, finds all those pairs which involve a certain star.
PairDistanceInvolvingIterator(const int16_t *pairs, const int16_t *end, int16_t involving)
The main constructor.
int16_t operator*() const
Access the curent pair.
PairDistanceInvolvingIterator & operator++()
Move to the next matching pair.
bool HasValue()
Whether the iterator is currently on a value. (false if iteration is complete)
PairDistanceInvolvingIterator()
Create a "past-the-end" iterator.
A database storing distances between pairs of stars.
decimal MinDistance() const
Lower bound on stored star pair distances.
static const int32_t kMagicValue
Magic value to use when storing inside a MultiDatabase.
const int16_t * FindPairsExact(const Catalog &, decimal min, decimal max, const int16_t **end) const
const int16_t * FindPairsLiberal(decimal min, decimal max, const int16_t **end) const
Return at least all the star pairs whose inter-star distance is between min and max.
decimal MaxDistance() const
Upper bound on stored star pair distances.
StarIdentifiers Go(const unsigned char *database, const Stars &, const Catalog &, const Camera &) const
Actualy perform the star idenification. This is the "main" function for StarIdAlgorithm.
A "centroid" detected in an image.
Vec2 position
The (x,y) pixel coordinates in the image (top left is 0,0)
Records that a certain Star (detected in the image) corresponds to a certain CatalogStar.
int catalogIndex
An index into an array of CatalogStar objects.
int starIndex
An index into an array of Star objects.
Three dimensional vector with decimaling point components.
Vec3 Normalize() const
Create a vector pointing in the same direction with magnitude 1.
Vec3 CrossProduct(const Vec3 &) const
Usual vector cross product.
#define DECIMAL_POW(base, power)
#define DECIMAL_ATAN2(x, y)
const decimal kAngleFrom90SoftThreshold
std::vector< StarIdentifier > StarIdentifiers
std::vector< Star > Stars
decimal AngleUnit(const Vec3 &vec1, const Vec3 &vec2)
Calculate the inner angle, in radians, between two /unit/ vectors.
std::vector< int16_t > ConsumeInvolvingIterator(PairDistanceInvolvingIterator it)
decimal Angle(const Vec3 &vec1, const Vec3 &vec2)
Calculate the inner angle, in radians, between two vectors.
std::vector< std::vector< IRUnidentifiedCentroid * >::iterator > FindUnidentifiedCentroidsInRange(std::vector< IRUnidentifiedCentroid * > *centroids, const Star &star, const Camera &camera, decimal minDistance, decimal maxDistance)
Return all the unidentified centroids within the requested distance bounds from star
int IdentifyRemainingStarsPairDistance(StarIdentifiers *, const Stars &, const PairDistanceKVectorDatabase &, const Catalog &, const Camera &, decimal tolerance)
Given some identified stars, attempt to identify the rest.
decimal DecimalModulo(decimal x, decimal mod)
Given a decimal, find it "modulo" another decimal, in the true mathematical sense (not remainder).
std::vector< int16_t > IdentifyThirdStar(const PairDistanceKVectorDatabase &db, const Catalog &catalog, int16_t catalogIndex1, int16_t catalogIndex2, decimal distance1, decimal distance2, decimal tolerance)
Given two already-identified centroids, and the distance from each to an as-yet unidentified third ce...
std::unordered_multimap< int16_t, int16_t > PairDistanceQueryToMap(const int16_t *pairs, const int16_t *end)
Given the result of a pair-distance kvector query, build a hashmultimap of stars to other stars that ...
std::vector< CatalogStar > Catalog
IRUnidentifiedCentroid * SelectNextUnidentifiedCentroid(std::vector< IRUnidentifiedCentroid * > *aboveThresholdCentroids, std::vector< IRUnidentifiedCentroid * > *belowThresholdCentroids)
void AddToAllUnidentifiedCentroids(const StarIdentifier &starId, const Stars &stars, std::vector< IRUnidentifiedCentroid * > *aboveThresholdCentroids, std::vector< IRUnidentifiedCentroid * > *belowThresholdCentroids, decimal minDistance, decimal maxDistance, decimal angleFrom90Threshold, const Camera &camera)
Given a list of unidentified centroids not yet at the soft threshold, and a list of unidentified cent...
#define _CHECK_DISTANCE(_dist)
A two dimensional vector with decimaling point components.