Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 121 additions & 49 deletions src/phg/matching/descriptor_matcher.cpp
Original file line number Diff line number Diff line change
@@ -1,16 +1,46 @@
#include "descriptor_matcher.h"

#include <opencv2/flann/miniflann.hpp>
#include "flann_factory.h"

#include <opencv2/flann/miniflann.hpp>

#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <vector>

void phg::DescriptorMatcher::filterMatchesRatioTest(const std::vector<std::vector<cv::DMatch>> &matches,
std::vector<cv::DMatch> &filtered_matches)
{
const float ratio_thresh = 0.75f;

filtered_matches.clear();
filtered_matches.reserve(matches.size());

throw std::runtime_error("not implemented yet");
}
for (const auto &m : matches) {
if (m.size() < 2) {
continue;
}

const cv::DMatch &m0 = m[0];
const cv::DMatch &m1 = m[1];

if (m0.queryIdx < 0 || m0.trainIdx < 0) {
continue;
}
if (!std::isfinite(m0.distance) || !std::isfinite(m1.distance)) {
continue;
}
if (m1.distance <= 0.0f) {
continue;
}

if (m0.distance < ratio_thresh * m1.distance) {
filtered_matches.push_back(m0);
}
}
}

void phg::DescriptorMatcher::filterMatchesClusters(const std::vector<cv::DMatch> &matches,
const std::vector<cv::KeyPoint> keypoints_query,
Expand All @@ -19,58 +49,100 @@ void phg::DescriptorMatcher::filterMatchesClusters(const std::vector<cv::DMatch>
{
filtered_matches.clear();

const size_t total_neighbours = 5; // total number of neighbours to test (including candidate)
const size_t consistent_matches = 3; // minimum number of consistent matches (including candidate)
const float radius_limit_scale = 2.f; // limit search radius by scaled median

const int n_matches = matches.size();
const int total_neighbours = 5;
const int consistent_matches = 3;
const float radius_limit_scale = 2.f;

const int n_matches = (int)matches.size();
if (n_matches < total_neighbours) {
throw std::runtime_error("DescriptorMatcher::filterMatchesClusters : too few matches");
filtered_matches = matches;
return;
}

cv::Mat points_query(n_matches, 2, CV_32FC1);
cv::Mat points_train(n_matches, 2, CV_32FC1);
for (int i = 0; i < n_matches; ++i) {
points_query.at<cv::Point2f>(i) = keypoints_query[matches[i].queryIdx].pt;
points_train.at<cv::Point2f>(i) = keypoints_train[matches[i].trainIdx].pt;
const int qi = matches[i].queryIdx;
const int ti = matches[i].trainIdx;

if (qi < 0 || qi >= (int)keypoints_query.size() || ti < 0 || ti >= (int)keypoints_train.size()) {
points_query.at<float>(i, 0) = 0.0f;
points_query.at<float>(i, 1) = 0.0f;
points_train.at<float>(i, 0) = 0.0f;
points_train.at<float>(i, 1) = 0.0f;
continue;
}

points_query.at<float>(i, 0) = keypoints_query[qi].pt.x;
points_query.at<float>(i, 1) = keypoints_query[qi].pt.y;
points_train.at<float>(i, 0) = keypoints_train[ti].pt.x;
points_train.at<float>(i, 1) = keypoints_train[ti].pt.y;
}

const int ntrees = 1;
const int nchecks = 32;
std::shared_ptr<cv::flann::IndexParams> index_params = flannKdTreeIndexParams(ntrees);
std::shared_ptr<cv::flann::SearchParams> search_params = flannKsTreeSearchParams(nchecks);

std::shared_ptr<cv::flann::Index> index_query = flannKdTreeIndex(points_query, index_params);
std::shared_ptr<cv::flann::Index> index_train = flannKdTreeIndex(points_train, index_params);

cv::Mat indices_query(n_matches, total_neighbours, CV_32SC1);
cv::Mat distances2_query(n_matches, total_neighbours, CV_32FC1);
cv::Mat indices_train(n_matches, total_neighbours, CV_32SC1);
cv::Mat distances2_train(n_matches, total_neighbours, CV_32FC1);

index_query->knnSearch(points_query, indices_query, distances2_query, total_neighbours, *search_params);
index_train->knnSearch(points_train, indices_train, distances2_train, total_neighbours, *search_params);

float radius2_query = 0.0f;
float radius2_train = 0.0f;
{
std::vector<float> max_d2_q(n_matches);
std::vector<float> max_d2_t(n_matches);
for (int i = 0; i < n_matches; ++i) {
max_d2_q[i] = distances2_query.at<float>(i, total_neighbours - 1);
max_d2_t[i] = distances2_train.at<float>(i, total_neighbours - 1);
}

const int median_pos = n_matches / 2;
std::nth_element(max_d2_q.begin(), max_d2_q.begin() + median_pos, max_d2_q.end());
std::nth_element(max_d2_t.begin(), max_d2_t.begin() + median_pos, max_d2_t.end());

radius2_query = max_d2_q[median_pos] * radius_limit_scale * radius_limit_scale;
radius2_train = max_d2_t[median_pos] * radius_limit_scale * radius_limit_scale;
if (!(radius2_query > 0.0f)) radius2_query = std::numeric_limits<float>::infinity();
if (!(radius2_train > 0.0f)) radius2_train = std::numeric_limits<float>::infinity();
}

filtered_matches.reserve(n_matches);

for (int i = 0; i < n_matches; ++i) {
std::array<int, (size_t)total_neighbours> neigh_q{};
int n_q = 0;
for (int k = 0; k < total_neighbours; ++k) {
const float d2 = distances2_query.at<float>(i, k);
if (d2 <= radius2_query) {
neigh_q[(size_t)n_q++] = indices_query.at<int>(i, k);
}
}

int inter = 0;
for (int k = 0; k < total_neighbours; ++k) {
const float d2 = distances2_train.at<float>(i, k);
if (d2 <= radius2_train) {
const int id = indices_train.at<int>(i, k);
for (int j = 0; j < n_q; ++j) {
if (neigh_q[(size_t)j] == id) {
++inter;
break;
}
}
}
}

if (inter >= consistent_matches) {
filtered_matches.push_back(matches[i]);
}
}
//
// // размерность всего 2, так что точное KD-дерево
// std::shared_ptr<cv::flann::IndexParams> index_params = flannKdTreeIndexParams(TODO);
// std::shared_ptr<cv::flann::SearchParams> search_params = flannKsTreeSearchParams(TODO);
//
// std::shared_ptr<cv::flann::Index> index_query = flannKdTreeIndex(points_query, index_params);
// std::shared_ptr<cv::flann::Index> index_train = flannKdTreeIndex(points_train, index_params);
//
// // для каждой точки найти total neighbors ближайших соседей
// cv::Mat indices_query(n_matches, total_neighbours, CV_32SC1);
// cv::Mat distances2_query(n_matches, total_neighbours, CV_32FC1);
// cv::Mat indices_train(n_matches, total_neighbours, CV_32SC1);
// cv::Mat distances2_train(n_matches, total_neighbours, CV_32FC1);
//
// index_query->knnSearch(points_query, indices_query, distances2_query, total_neighbours, *search_params);
// index_train->knnSearch(points_train, indices_train, distances2_train, total_neighbours, *search_params);
//
// // оценить радиус поиска для каждой картинки
// // NB: radius2_query, radius2_train: квадраты радиуса!
// float radius2_query, radius2_train;
// {
// std::vector<double> max_dists2_query(n_matches);
// std::vector<double> max_dists2_train(n_matches);
// for (int i = 0; i < n_matches; ++i) {
// max_dists2_query[i] = distances2_query.at<float>(i, total_neighbours - 1);
// max_dists2_train[i] = distances2_train.at<float>(i, total_neighbours - 1);
// }
//
// int median_pos = n_matches / 2;
// std::nth_element(max_dists2_query.begin(), max_dists2_query.begin() + median_pos, max_dists2_query.end());
// std::nth_element(max_dists2_train.begin(), max_dists2_train.begin() + median_pos, max_dists2_train.end());
//
// radius2_query = max_dists2_query[median_pos] * radius_limit_scale * radius_limit_scale;
// radius2_train = max_dists2_train[median_pos] * radius_limit_scale * radius_limit_scale;
// }
//
// метч остается, если левое и правое множества первых total_neighbors соседей в радиусах поиска(radius2_query, radius2_train) имеют как минимум consistent_matches общих элементов
// // TODO заполнить filtered_matches
}
5 changes: 4 additions & 1 deletion src/phg/matching/descriptor_matcher.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#pragma once

#include <opencv2/core.hpp>
#include <opencv2/features2d.hpp>

#include <vector>

namespace phg {

Expand All @@ -17,4 +20,4 @@ namespace phg {
std::vector<cv::DMatch> &filtered_matches);
};

}
}
74 changes: 67 additions & 7 deletions src/phg/matching/flann_matcher.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,81 @@
#include <iostream>
#include <algorithm>
#include <cmath>
#include <stdexcept>

#include "flann_matcher.h"
#include "flann_factory.h"


phg::FlannMatcher::FlannMatcher()
{
// параметры для приближенного поиска
// index_params = flannKdTreeIndexParams(TODO);
// search_params = flannKsTreeSearchParams(TODO);
const int ntrees = 4;
const int nchecks = 32;

index_params = flannKdTreeIndexParams(ntrees);
search_params = flannKsTreeSearchParams(nchecks);
}

void phg::FlannMatcher::train(const cv::Mat &train_desc)
{
flann_index = flannKdTreeIndex(train_desc, index_params);
if (train_desc.rows < 2) {
throw std::runtime_error("FlannMatcher::train : needed at least 2 train descriptors");
}

if (train_desc.type() != CV_32F) {
train_desc.convertTo(train_desc_, CV_32F);
} else {
train_desc_ = train_desc;
}

flann_index = flannKdTreeIndex(train_desc_, index_params);
}

void phg::FlannMatcher::knnMatch(const cv::Mat &query_desc, std::vector<std::vector<cv::DMatch>> &matches, int k) const
void phg::FlannMatcher::knnMatch(const cv::Mat &query_desc,
std::vector<std::vector<cv::DMatch>> &matches,
int k) const
{
throw std::runtime_error("not implemented yet");
if (!flann_index) {
throw std::runtime_error("FlannMatcher::knnMatch : matcher is not trained");
}
if (!search_params) {
throw std::runtime_error("FlannMatcher::knnMatch : invalid search_params");
}
if (k <= 0) {
throw std::runtime_error("FlannMatcher::knnMatch : invalid k");
}

cv::Mat query32f;
if (query_desc.type() != CV_32F) {
query_desc.convertTo(query32f, CV_32F);
} else {
query32f = query_desc;
}

const int n_query = query32f.rows;
matches.assign(n_query, {});

cv::Mat indices(n_query, k, CV_32SC1);
cv::Mat dists2(n_query, k, CV_32FC1);

flann_index->knnSearch(query32f, indices, dists2, k, *search_params);

for (int qi = 0; qi < n_query; ++qi) {
std::vector<cv::DMatch> &dst = matches[qi];
dst.resize(k);

for (int j = 0; j < k; ++j) {
cv::DMatch m;
m.queryIdx = qi;
m.trainIdx = indices.at<int>(qi, j);
m.imgIdx = 0;

float d2 = dists2.at<float>(qi, j);
m.distance = (d2 > 0.0f) ? std::sqrt(d2) : 0.0f;

dst[j] = m;
}

std::sort(dst.begin(), dst.end(),
[](const cv::DMatch &a, const cv::DMatch &b) { return a.distance < b.distance; });
}
}
4 changes: 4 additions & 0 deletions src/phg/matching/flann_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "descriptor_matcher.h"
#include <opencv2/flann/miniflann.hpp>

#include <memory>

namespace phg {

struct FlannMatcher : DescriptorMatcher {
Expand All @@ -15,6 +17,8 @@ namespace phg {

private:

cv::Mat train_desc_;

std::shared_ptr<cv::flann::IndexParams> index_params;
std::shared_ptr<cv::flann::SearchParams> search_params;
std::shared_ptr<cv::flann::Index> flann_index;
Expand Down
Loading
Loading