//! Find the nearest neighbors in the descriptor space using FLANN. void append_nearest_neighbors( size_t i1, const Set<OERegion, RealDescriptor>& keys1, const Set<OERegion, RealDescriptor>& keys2, vector<Match>& matches, const flann::Matrix<float>& /*data2*/, flann::Index<flann::L2<float>>& tree2, float squared_ratio_thres, Match::Direction dir, bool self_matching, const KeyProximity& is_redundant, // self-matching vector<int>& vec_indices, vector<float>& vec_dists, size_t num_max_neighbors) // internal storage parameters { // Prepare the query matrix flann::Matrix<float> query{ const_cast<float *>(&(keys1.descriptors.matrix()(0, i1))), 1, keys1.descriptors.dimension() }; // Prepare the indices and distances. flann::Matrix<int> indices{ &vec_indices[0], 1, num_max_neighbors }; flann::Matrix<float> dists{ &vec_dists[0], 1, num_max_neighbors }; // Create search parameters. flann::SearchParams search_params; // N.B.: We should not be in the boundary case in practice, in which case the // ambiguity score does not really make sense. // // Boundary case 1. if (keys2.size() == 0) return; // Boundary case 2. if (keys2.size() == 1 && !self_matching) { auto m = Match{ &keys1.features[i1], &keys2.features[0], 1.f, dir, int(i1), 0 }; m.rank() = 1; if(dir == Match::Direction::TargetToSource) { swap(m.x_pointer(), m.y_pointer()); swap(m.x_index(), m.y_index()); } if (m.score() < squared_ratio_thres) matches.push_back(m); return; } // Boundary case 3. if (keys2.size() == 2 && self_matching) { tree2.knnSearch(query, indices, dists, 2, search_params); const auto i2 = indices[0][1]; // The first index can't be indices[0][0], which is i1. auto m = Match{ &keys1.features[i1], &keys2.features[i2], 1.f, dir, int(i1), i2 }; m.rank() = 1; if(dir == Match::Direction::TargetToSource) { swap(m.x_pointer(), m.y_pointer()); swap(m.x_index(), m.y_index()); } if (m.score() < squared_ratio_thres) matches.push_back(m); return; } // Now treat the generic case. // // Search the nearest neighbor. tree2.knnSearch(query, indices, dists, 3, search_params); // This is to avoid the source key matches with himself in case of intra // image matching. const auto top1_index = self_matching ? 1 : 0; auto top1_score = dists[0][top1_index + 1] > 0.f ? dists[0][top1_index] / dists[0][top1_index + 1] : 0.f; auto K = 1; // Determine the number of nearest neighbors. if (squared_ratio_thres > 1.f) { // Performs an adaptive radius search. const auto radius = dists[0][top1_index] * squared_ratio_thres; K = tree2.radiusSearch(query, indices, dists, radius, search_params); } // Retrieve the right key points. for (int rank = top1_index; rank < K; ++rank) { auto score = 0.f; if (rank == top1_index) score = top1_score; else if (dists[0][top1_index]) score = dists[0][rank] / dists[0][top1_index]; // We still need this check as FLANN can still return wrong neighbors. if (score > squared_ratio_thres) break; auto i2 = indices[0][rank]; // Ignore the match if keys1 == keys2. if (self_matching && is_redundant(keys1.features[i1], keys2.features[i2])) continue; Match m(&keys1.features[i1], &keys2.features[i2], score, dir, i1, i2); m.rank() = top1_index == 0 ? rank+1 : rank; if(dir == Match::Direction::TargetToSource) { swap(m.x_pointer(), m.y_pointer()); swap(m.x_index(), m.y_index()); } matches.push_back(m); } }
// x座標列を返す const std::vector<double>& FieldBase::x_list() const{ return *x_pointer(); }