38 #include "FLANN/flann.hpp"
40 #include "SiftGPU/SiftGPU.h"
41 #include "VLFeat/covdet.h"
42 #include "VLFeat/sift.h"
53 size_t FindBestMatchesOneWayBruteForce(
const Eigen::MatrixXi& dists,
54 const float max_ratio,
55 const float max_distance,
56 std::vector<int>* matches) {
58 const float kDistNorm = 1.0f / (512.0f * 512.0f);
60 size_t num_matches = 0;
61 matches->resize(dists.rows(), -1);
66 int second_best_dist = 0;
68 const int dist = dists(i1, i2);
69 if (dist > best_dist) {
71 second_best_dist = best_dist;
73 }
else if (dist > second_best_dist) {
74 second_best_dist = dist;
83 const float best_dist_normed =
84 std::acos(std::min(kDistNorm * best_dist, 1.0f));
87 if (best_dist_normed > max_distance) {
91 const float second_best_dist_normed =
92 std::acos(std::min(kDistNorm * second_best_dist, 1.0f));
96 if (best_dist_normed >= max_ratio * second_best_dist_normed) {
101 (*matches)[i1] = best_i2;
107 void FindBestMatchesBruteForce(
const Eigen::MatrixXi& dists,
108 const float max_ratio,
109 const float max_distance,
110 const bool cross_check,
114 std::vector<int> matches12;
115 const size_t num_matches12 = FindBestMatchesOneWayBruteForce(
116 dists, max_ratio, max_distance, &matches12);
119 std::vector<int> matches21;
120 const size_t num_matches21 = FindBestMatchesOneWayBruteForce(
121 dists.transpose(), max_ratio, max_distance, &matches21);
122 matches->reserve(std::min(num_matches12, num_matches21));
123 for (
size_t i1 = 0; i1 < matches12.size(); ++i1) {
124 if (matches12[i1] != -1 && matches21[matches12[i1]] != -1 &&
125 matches21[matches12[i1]] ==
static_cast<int>(i1)) {
127 match.point2D_idx1 = i1;
128 match.point2D_idx2 = matches12[i1];
129 matches->push_back(match);
133 matches->reserve(num_matches12);
134 for (
size_t i1 = 0; i1 < matches12.size(); ++i1) {
135 if (matches12[i1] != -1) {
137 match.point2D_idx1 = i1;
138 match.point2D_idx2 = matches12[i1];
139 matches->push_back(match);
147 static std::map<int, std::unique_ptr<std::mutex>> sift_extraction_mutexes;
148 static std::map<int, std::unique_ptr<std::mutex>> sift_matching_mutexes;
155 vlfeat_descriptors.cols());
156 const std::array<int, 8> q{{0, 7, 6, 5, 4, 3, 2, 1}};
158 for (
int i = 0; i < 4; ++i) {
159 for (
int j = 0; j < 4; ++j) {
160 for (
int k = 0; k < 8; ++k) {
161 ubc_descriptors(n, 8 * (j + 4 * i) + q[k]) =
162 vlfeat_descriptors(n, 8 * (j + 4 * i) + k);
167 return ubc_descriptors;
170 Eigen::MatrixXi ComputeSiftDistanceMatrix(
175 const std::function<
bool(
float,
float,
float,
float)>& guided_filter) {
176 if (guided_filter !=
nullptr) {
177 CHECK_NOTNULL(keypoints1);
178 CHECK_NOTNULL(keypoints2);
179 CHECK_EQ(keypoints1->size(), descriptors1.rows());
180 CHECK_EQ(keypoints2->size(), descriptors2.rows());
183 const Eigen::Matrix<int, Eigen::Dynamic, 128> descriptors1_int =
184 descriptors1.cast<
int>();
185 const Eigen::Matrix<int, Eigen::Dynamic, 128> descriptors2_int =
186 descriptors2.cast<
int>();
188 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> dists(
189 descriptors1.rows(), descriptors2.rows());
193 if (guided_filter !=
nullptr &&
194 guided_filter((*keypoints1)[i1].
x, (*keypoints1)[i1].
y,
195 (*keypoints2)[i2].
x, (*keypoints2)[i2].
y)) {
199 descriptors1_int.row(i1).dot(descriptors2_int.row(i2));
207 void FindNearestNeighborsFLANN(
210 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>*
212 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>*
214 if (query.rows() == 0 || database.rows() == 0) {
218 const size_t kNumNearestNeighbors = 2;
219 const size_t kNumTreesInForest = 4;
221 const size_t num_nearest_neighbors = std::min(
222 kNumNearestNeighbors,
static_cast<size_t>(database.rows()));
224 indices->resize(query.rows(), num_nearest_neighbors);
225 distances->resize(query.rows(), num_nearest_neighbors);
226 const flann::Matrix<uint8_t> query_matrix(
227 const_cast<uint8_t*
>(query.data()), query.rows(), 128);
228 const flann::Matrix<uint8_t> database_matrix(
229 const_cast<uint8_t*
>(database.data()), database.rows(), 128);
231 flann::Matrix<int> indices_matrix(indices->data(), query.rows(),
232 num_nearest_neighbors);
233 std::vector<float> distances_vector(query.rows() * num_nearest_neighbors);
234 flann::Matrix<float> distances_matrix(distances_vector.data(), query.rows(),
235 num_nearest_neighbors);
236 flann::Index<flann::L2<uint8_t>> index(
237 database_matrix, flann::KDTreeIndexParams(kNumTreesInForest));
239 index.knnSearch(query_matrix, indices_matrix, distances_matrix,
240 num_nearest_neighbors, flann::SearchParams(128));
242 for (
Eigen::Index query_index = 0; query_index < indices->rows();
245 const Eigen::Index database_index = indices->coeff(query_index, k);
246 distances->coeffRef(query_index, k) =
247 query.row(query_index)
249 .dot(database.row(database_index).cast<
int>());
254 size_t FindBestMatchesOneWayFLANN(
256 Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
259 Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
261 const float max_ratio,
262 const float max_distance,
263 std::vector<int>* matches) {
265 const float kDistNorm = 1.0f / (512.0f * 512.0f);
267 size_t num_matches = 0;
268 matches->resize(indices.rows(), -1);
270 for (
int d1_idx = 0; d1_idx < indices.rows(); ++d1_idx) {
273 int second_best_dist = 0;
274 for (
int n_idx = 0; n_idx < indices.cols(); ++n_idx) {
275 const int d2_idx = indices(d1_idx, n_idx);
276 const int dist = distances(d1_idx, n_idx);
277 if (dist > best_dist) {
279 second_best_dist = best_dist;
281 }
else if (dist > second_best_dist) {
282 second_best_dist = dist;
291 const float best_dist_normed =
292 std::acos(std::min(kDistNorm * best_dist, 1.0f));
295 if (best_dist_normed > max_distance) {
299 const float second_best_dist_normed =
300 std::acos(std::min(kDistNorm * second_best_dist, 1.0f));
304 if (best_dist_normed >= max_ratio * second_best_dist_normed) {
309 (*matches)[d1_idx] = best_i2;
315 void FindBestMatchesFLANN(
317 Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
320 Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
323 Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
326 Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>&
328 const float max_ratio,
329 const float max_distance,
330 const bool cross_check,
334 std::vector<int> matches12;
335 const size_t num_matches12 = FindBestMatchesOneWayFLANN(
336 indices_1to2, distances_1to2, max_ratio, max_distance, &matches12);
338 if (cross_check && indices_2to1.rows()) {
339 std::vector<int> matches21;
340 const size_t num_matches21 =
341 FindBestMatchesOneWayFLANN(indices_2to1, distances_2to1,
342 max_ratio, max_distance, &matches21);
343 matches->reserve(std::min(num_matches12, num_matches21));
344 for (
size_t i1 = 0; i1 < matches12.size(); ++i1) {
345 if (matches12[i1] != -1 && matches21[matches12[i1]] != -1 &&
346 matches21[matches12[i1]] ==
static_cast<int>(i1)) {
348 match.point2D_idx1 = i1;
349 match.point2D_idx2 = matches12[i1];
350 matches->push_back(match);
354 matches->reserve(num_matches12);
355 for (
size_t i1 = 0; i1 < matches12.size(); ++i1) {
356 if (matches12[i1] != -1) {
358 match.point2D_idx1 = i1;
359 match.point2D_idx2 = matches12[i1];
360 matches->push_back(match);
366 void WarnIfMaxNumMatchesReachedGPU(
const SiftMatchGPU& sift_match_gpu,
368 if (sift_match_gpu.GetMaxSift() <
descriptors.rows()) {
370 "WARNING: Clamping features from %d to %d - "
372 "increasing the maximum number of matches.",
378 void WarnDarknessAdaptivityNotAvailable() {
379 std::cout <<
"WARNING: Darkness adaptivity only available for GLSL SiftGPU."
423 CHECK(options.
Check());
425 CHECK_NOTNULL(keypoints);
431 WarnDarknessAdaptivityNotAvailable();
435 std::unique_ptr<VlSiftFilt, void (*)(VlSiftFilt*)> sift(
447 std::vector<size_t> level_num_features;
448 std::vector<FeatureKeypoints> level_keypoints;
449 std::vector<FeatureDescriptors> level_descriptors;
450 bool first_octave =
true;
453 const std::vector<uint8_t> data_uint8 =
455 std::vector<float> data_float(data_uint8.size());
456 for (
size_t i = 0; i < data_uint8.size(); ++i) {
457 data_float[i] =
static_cast<float>(data_uint8[i]) / 255.0f;
459 if (vl_sift_process_first_octave(sift.get(), data_float.data())) {
462 first_octave =
false;
464 if (vl_sift_process_next_octave(sift.get())) {
470 vl_sift_detect(sift.get());
473 const VlSiftKeypoint* vl_keypoints = vl_sift_get_keypoints(sift.get());
474 const int num_keypoints = vl_sift_get_nkeypoints(sift.get());
475 if (num_keypoints == 0) {
480 size_t level_idx = 0;
482 for (
int i = 0; i < num_keypoints; ++i) {
483 if (vl_keypoints[i].is != prev_level) {
486 level_keypoints.back().resize(level_idx);
488 level_descriptors.back().conservativeResize(level_idx,
495 level_num_features.push_back(0);
499 level_descriptors.emplace_back(
504 level_num_features.back() += 1;
505 prev_level = vl_keypoints[i].is;
509 int num_orientations;
511 num_orientations = 1;
514 num_orientations = vl_sift_calc_keypoint_orientations(
515 sift.get(), angles, &vl_keypoints[i]);
521 const int num_used_orientations =
524 for (
int o = 0; o < num_used_orientations; ++o) {
526 vl_keypoints[i].
x + 0.5f, vl_keypoints[i].
y + 0.5f,
527 vl_keypoints[i].sigma, angles[o]);
529 Eigen::MatrixXf desc(1, 128);
530 vl_sift_calc_keypoint_descriptor(sift.get(), desc.data(),
540 LOG(FATAL) <<
"Normalization type not supported";
543 level_descriptors.back().row(level_idx) =
552 level_keypoints.back().resize(level_idx);
554 level_descriptors.back().conservativeResize(level_idx, 128);
559 int first_level_to_keep = 0;
560 int num_features = 0;
561 int num_features_with_orientations = 0;
562 for (
int i = level_keypoints.size() - 1; i >= 0; --i) {
563 num_features += level_num_features[i];
564 num_features_with_orientations += level_keypoints[i].size();
566 first_level_to_keep = i;
574 keypoints->resize(num_features_with_orientations);
575 for (
size_t i = first_level_to_keep; i < level_keypoints.size(); ++i) {
576 for (
size_t j = 0; j < level_keypoints[i].size(); ++j) {
577 (*keypoints)[k] = level_keypoints[i][j];
586 descriptors->resize(num_features_with_orientations, 128);
587 for (
size_t i = first_level_to_keep; i < level_keypoints.size(); ++i) {
588 for (
size_t j = 0; j < level_keypoints[i].size(); ++j) {
603 CHECK(options.
Check());
605 CHECK_NOTNULL(keypoints);
608 WarnDarknessAdaptivityNotAvailable();
612 std::unique_ptr<VlCovDet, void (*)(VlCovDet*)> covdet(
613 vl_covdet_new(VL_COVDET_METHOD_DOG), &vl_covdet_delete);
618 const int kMaxOctaveResolution = 1000;
621 vl_covdet_set_first_octave(covdet.get(), options.
first_octave);
623 vl_covdet_set_peak_threshold(covdet.get(), options.
peak_threshold);
624 vl_covdet_set_edge_threshold(covdet.get(), options.
edge_threshold);
628 std::vector<float> data_float(data_uint8.size());
629 for (
size_t i = 0; i < data_uint8.size(); ++i) {
630 data_float[i] =
static_cast<float>(data_uint8[i]) / 255.0f;
632 vl_covdet_put_image(covdet.get(), data_float.data(), bitmap.
Width(),
640 vl_covdet_extract_affine_shape(covdet.get());
642 vl_covdet_extract_orientations(covdet.get());
646 const int num_features = vl_covdet_get_num_features(covdet.get());
647 VlCovDetFeature* features = vl_covdet_get_features(covdet.get());
650 std::sort(features, features + num_features,
651 [](
const VlCovDetFeature& feature1,
652 const VlCovDetFeature& feature2) {
653 if (feature1.o == feature2.o) {
654 return feature1.s > feature2.s;
656 return feature1.o > feature2.o;
660 const size_t max_num_features =
665 int prev_octave_scale_idx = std::numeric_limits<int>::max();
666 for (
int i = 0; i < num_features; ++i) {
668 keypoint.
x = features[i].frame.x + 0.5;
669 keypoint.
y = features[i].frame.y + 0.5;
670 keypoint.
a11 = features[i].frame.a11;
671 keypoint.
a12 = features[i].frame.a12;
672 keypoint.
a21 = features[i].frame.a21;
673 keypoint.
a22 = features[i].frame.a22;
674 keypoints->push_back(keypoint);
676 const int octave_scale_idx =
677 features[i].o * kMaxOctaveResolution + features[i].s;
678 CHECK_LE(octave_scale_idx, prev_octave_scale_idx);
680 if (octave_scale_idx != prev_octave_scale_idx &&
681 keypoints->size() >= max_num_features) {
685 prev_octave_scale_idx = octave_scale_idx;
692 const size_t kPatchResolution = 15;
693 const size_t kPatchSide = 2 * kPatchResolution + 1;
694 const double kPatchRelativeExtent = 7.5;
695 const double kPatchRelativeSmoothing = 1;
696 const double kPatchStep = kPatchRelativeExtent / kPatchResolution;
697 const double kSigma =
698 kPatchRelativeExtent / (3.0 * (4 + 1) / 2) / kPatchStep;
700 std::vector<float>
patch(kPatchSide * kPatchSide);
701 std::vector<float> patchXY(2 * kPatchSide * kPatchSide);
703 float dsp_min_scale = 1;
704 float dsp_scale_step = 0;
705 int dsp_num_scales = 1;
713 Eigen::Matrix<float, Eigen::Dynamic, 128, Eigen::RowMajor>
714 scaled_descriptors(dsp_num_scales, 128);
716 std::unique_ptr<VlSiftFilt, void (*)(VlSiftFilt*)> sift(
717 vl_sift_new(16, 16, 1, 3, 0), &vl_sift_delete);
722 vl_sift_set_magnif(sift.get(), 3.0);
724 for (
size_t i = 0; i < keypoints->size(); ++i) {
725 for (
int s = 0; s < dsp_num_scales; ++s) {
726 const double dsp_scale = dsp_min_scale + s * dsp_scale_step;
728 VlFrameOrientedEllipse scaled_frame = features[i].frame;
729 scaled_frame.a11 *= dsp_scale;
730 scaled_frame.a12 *= dsp_scale;
731 scaled_frame.a21 *= dsp_scale;
732 scaled_frame.a22 *= dsp_scale;
734 vl_covdet_extract_patch_for_frame(
735 covdet.get(),
patch.data(), kPatchResolution,
736 kPatchRelativeExtent, kPatchRelativeSmoothing,
739 vl_imgradient_polar_f(patchXY.data(), patchXY.data() + 1, 2,
740 2 * kPatchSide,
patch.data(), kPatchSide,
741 kPatchSide, kPatchSide);
743 vl_sift_calc_raw_descriptor(sift.get(), patchXY.data(),
744 scaled_descriptors.row(s).data(),
745 kPatchSide, kPatchSide,
746 kPatchResolution, kPatchResolution,
750 Eigen::Matrix<float, 1, 128> descriptor;
752 descriptor = scaled_descriptors.colwise().mean();
754 descriptor = scaled_descriptors;
764 LOG(FATAL) <<
"Normalization type not supported";
778 CHECK(options.
Check());
779 CHECK_NOTNULL(sift_gpu);
783 static std::mutex mutex;
784 std::unique_lock<std::mutex> lock(mutex);
786 std::vector<int> gpu_indices = CSVToVector<int>(options.
gpu_index);
787 CHECK_EQ(gpu_indices.size(), 1) <<
"SiftGPU can only run on one GPU";
789 std::vector<std::string> sift_gpu_args;
791 sift_gpu_args.push_back(
"./sift_gpu");
799 if (gpu_indices[0] >= 0) {
800 sift_gpu_args.push_back(
"-cuda");
808 if (gpu_indices[0] >= 0) {
809 WarnDarknessAdaptivityNotAvailable();
811 sift_gpu_args.push_back(
"-da");
815 sift_gpu_args.push_back(
"-v");
816 sift_gpu_args.push_back(
"0");
821 const int compensation_factor = 1 << -std::min(0, options.
first_octave);
822 sift_gpu_args.push_back(
"-maxd");
823 sift_gpu_args.push_back(
827 sift_gpu_args.push_back(
"-tc2");
831 sift_gpu_args.push_back(
"-fo");
835 sift_gpu_args.push_back(
"-d");
839 sift_gpu_args.push_back(
"-t");
843 sift_gpu_args.push_back(
"-e");
848 sift_gpu_args.push_back(
"-ofix");
850 sift_gpu_args.push_back(
"-mo");
851 sift_gpu_args.push_back(
"1");
854 sift_gpu_args.push_back(
"-mo");
858 std::vector<const char*> sift_gpu_args_cstr;
859 sift_gpu_args_cstr.reserve(sift_gpu_args.size());
860 for (
const auto& arg : sift_gpu_args) {
861 sift_gpu_args_cstr.push_back(arg.c_str());
864 sift_gpu->ParseParam(sift_gpu_args_cstr.size(), sift_gpu_args_cstr.data());
866 sift_gpu->gpu_index = gpu_indices[0];
867 if (sift_extraction_mutexes.count(gpu_indices[0]) == 0) {
868 sift_extraction_mutexes.emplace(
869 gpu_indices[0], std::unique_ptr<std::mutex>(
new std::mutex()));
872 return sift_gpu->VerifyContextGL() == SiftGPU::SIFTGPU_FULL_SUPPORTED;
880 CHECK(options.
Check());
882 CHECK_NOTNULL(keypoints);
887 const int compensation_factor = 1 << -std::min(0, options.
first_octave);
889 sift_gpu->GetMaxDimension());
894 std::unique_lock<std::mutex> lock(
895 *sift_extraction_mutexes[sift_gpu->gpu_index]);
900 const int code = sift_gpu->RunSIFT(bitmap.
ScanWidth(), bitmap.
Height(),
901 bitmap_raw_bits.data(), GL_LUMINANCE,
904 const int kSuccessCode = 1;
905 if (code != kSuccessCode) {
909 const size_t num_features =
static_cast<size_t>(sift_gpu->GetFeatureNum());
911 std::vector<SiftKeypoint> keypoints_data(num_features);
914 Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
915 descriptors_float(num_features, 128);
918 sift_gpu->GetFeatureVector(keypoints_data.data(), descriptors_float.data());
920 keypoints->resize(num_features);
921 for (
size_t i = 0; i < num_features; ++i) {
924 keypoints_data[i].s, keypoints_data[i].o);
935 LOG(FATAL) <<
"Normalization type not supported";
946 CHECK_NOTNULL(keypoints);
949 std::ifstream file(
path.c_str());
950 CHECK(file.is_open()) <<
path;
955 std::getline(file, line);
956 std::stringstream header_line_stream(line);
958 std::getline(header_line_stream >> std::ws, item,
' ');
959 const point2D_t num_features = std::stoul(item);
961 std::getline(header_line_stream >> std::ws, item,
' ');
962 const size_t dim = std::stoul(item);
964 CHECK_EQ(dim, 128) <<
"SIFT features must have 128 dimensions";
966 keypoints->resize(num_features);
969 for (
size_t i = 0; i < num_features; ++i) {
970 std::getline(file, line);
971 std::stringstream feature_line_stream(line);
973 std::getline(feature_line_stream >> std::ws, item,
' ');
974 const float x = std::stold(item);
976 std::getline(feature_line_stream >> std::ws, item,
' ');
977 const float y = std::stold(item);
979 std::getline(feature_line_stream >> std::ws, item,
' ');
980 const float scale = std::stold(item);
982 std::getline(feature_line_stream >> std::ws, item,
' ');
983 const float orientation = std::stold(item);
988 for (
size_t j = 0; j < dim; ++j) {
989 std::getline(feature_line_stream >> std::ws, item,
' ');
990 const float value = std::stod(item);
992 CHECK_LE(value, 255);
993 (*descriptors)(i, j) = TruncateCast<float, uint8_t>(value);
1002 CHECK(match_options.
Check());
1003 CHECK_NOTNULL(matches);
1005 const Eigen::MatrixXi distances = ComputeSiftDistanceMatrix(
1006 nullptr,
nullptr, descriptors1, descriptors2,
nullptr);
1008 FindBestMatchesBruteForce(distances, match_options.
max_ratio,
1017 CHECK(match_options.
Check());
1018 CHECK_NOTNULL(matches);
1020 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1022 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1024 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1026 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1029 FindNearestNeighborsFLANN(descriptors1, descriptors2, &indices_1to2,
1032 FindNearestNeighborsFLANN(descriptors2, descriptors1, &indices_2to1,
1036 FindBestMatchesFLANN(indices_1to2, distances_1to2, indices_2to1,
1037 distances_2to1, match_options.
max_ratio,
1056 CHECK(match_options.
Check());
1057 CHECK_NOTNULL(two_view_geometry);
1059 const float max_residual =
1062 const Eigen::Matrix3f F = two_view_geometry->
F.cast<
float>();
1063 const Eigen::Matrix3f H = two_view_geometry->
H.cast<
float>();
1065 std::function<bool(
float,
float,
float,
float)> guided_filter;
1068 guided_filter = [&](
const float x1,
const float y1,
const float x2,
1070 const Eigen::Vector3f p1(x1, y1, 1.0f);
1071 const Eigen::Vector3f p2(x2, y2, 1.0f);
1072 const Eigen::Vector3f Fx1 = F * p1;
1073 const Eigen::Vector3f Ftx2 = F.transpose() * p2;
1074 const float x2tFx1 = p2.transpose() * Fx1;
1075 return x2tFx1 * x2tFx1 /
1076 (Fx1(0) * Fx1(0) + Fx1(1) * Fx1(1) +
1077 Ftx2(0) * Ftx2(0) + Ftx2(1) * Ftx2(1)) >
1082 two_view_geometry->
config ==
1084 guided_filter = [&](
const float x1,
const float y1,
const float x2,
1086 const Eigen::Vector3f p1(x1, y1, 1.0f);
1087 const Eigen::Vector2f p2(x2, y2);
1088 return ((H * p1).hnormalized() - p2).squaredNorm() > max_residual;
1094 CHECK(guided_filter);
1096 const Eigen::MatrixXi dists =
1097 ComputeSiftDistanceMatrix(&keypoints1, &keypoints2, descriptors1,
1098 descriptors2, guided_filter);
1100 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1101 indices_1to2(dists.rows(), dists.cols());
1102 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1103 indices_2to1(dists.cols(), dists.rows());
1104 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1105 distances_1to2 = dists;
1106 Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
1107 distances_2to1 = dists.transpose();
1109 for (
int i = 0; i < indices_1to2.rows(); ++i) {
1110 indices_1to2.row(i) = Eigen::VectorXi::LinSpaced(
1111 indices_1to2.cols(), 0, indices_1to2.cols() - 1);
1113 for (
int i = 0; i < indices_2to1.rows(); ++i) {
1114 indices_2to1.row(i) = Eigen::VectorXi::LinSpaced(
1115 indices_2to1.cols(), 0, indices_2to1.cols() - 1);
1118 FindBestMatchesFLANN(indices_1to2, distances_1to2, indices_2to1,
1119 distances_2to1, match_options.
max_ratio,
1125 SiftMatchGPU* sift_match_gpu) {
1126 CHECK(match_options.
Check());
1127 CHECK_NOTNULL(sift_match_gpu);
1131 static std::mutex mutex;
1132 std::unique_lock<std::mutex> lock(mutex);
1134 const std::vector<int> gpu_indices =
1135 CSVToVector<int>(match_options.
gpu_index);
1136 CHECK_EQ(gpu_indices.size(), 1) <<
"SiftGPU can only run on one GPU";
1139 sift_gpu.SetVerbose(0);
1148 if (gpu_indices[0] >= 0) {
1149 sift_match_gpu->SetLanguage(SiftMatchGPU::SIFTMATCH_CUDA_DEVICE0 +
1152 sift_match_gpu->SetLanguage(SiftMatchGPU::SIFTMATCH_CUDA);
1155 sift_match_gpu->SetLanguage(SiftMatchGPU::SIFTMATCH_GLSL);
1158 if (sift_match_gpu->VerifyContextGL() == 0) {
1166 "ERROR: Not enough GPU memory to match %d features. "
1167 "Reduce the maximum number of matches.",
1173 #ifndef CUDA_ENABLED
1177 "WARNING: OpenGL version of SiftGPU only supports a "
1178 "maximum of %d matches - consider changing to "
1180 "feature matching to avoid this limitation.",
1181 sift_match_gpu->GetMaxSift())
1186 sift_match_gpu->gpu_index = gpu_indices[0];
1187 if (sift_matching_mutexes.count(gpu_indices[0]) == 0) {
1188 sift_matching_mutexes.emplace(
1189 gpu_indices[0], std::unique_ptr<std::mutex>(
new std::mutex()));
1198 SiftMatchGPU* sift_match_gpu,
1200 CHECK(match_options.
Check());
1201 CHECK_NOTNULL(sift_match_gpu);
1202 CHECK_NOTNULL(matches);
1205 if (sift_matching_mutexes.find(sift_match_gpu->gpu_index) ==
1206 sift_matching_mutexes.end()) {
1207 std::cerr <<
"ERROR: GPU index " << sift_match_gpu->gpu_index
1208 <<
" not found in SIFT matching mutexes" <<
std::endl;
1215 std::unique_lock<std::mutex> lock(
1216 *sift_matching_mutexes[sift_match_gpu->gpu_index]);
1218 if (descriptors1 !=
nullptr) {
1219 if (descriptors1->cols() != 128) {
1221 <<
"ERROR: Invalid descriptor dimensions for descriptors1: "
1222 << descriptors1->cols() <<
" (expected 128)" <<
std::endl;
1226 WarnIfMaxNumMatchesReachedGPU(*sift_match_gpu, *descriptors1);
1227 sift_match_gpu->SetDescriptors(0, descriptors1->rows(),
1228 descriptors1->data());
1231 if (descriptors2 !=
nullptr) {
1232 if (descriptors2->cols() != 128) {
1234 <<
"ERROR: Invalid descriptor dimensions for descriptors2: "
1235 << descriptors2->cols() <<
" (expected 128)" <<
std::endl;
1239 WarnIfMaxNumMatchesReachedGPU(*sift_match_gpu, *descriptors2);
1240 sift_match_gpu->SetDescriptors(1, descriptors2->rows(),
1241 descriptors2->data());
1246 const int num_matches = sift_match_gpu->GetSiftMatch(
1248 reinterpret_cast<uint32_t(*)[2]
>(matches->data()),
1250 static_cast<float>(match_options.
max_ratio),
1253 if (num_matches < 0) {
1255 <<
"ERROR: Feature matching failed. This is probably caused by "
1256 "insufficient GPU memory. Consider reducing the maximum "
1257 "number of features and/or matches."
1262 CHECK_LE(num_matches, matches->size());
1263 matches->resize(num_matches);
1272 SiftMatchGPU* sift_match_gpu,
1275 "Invalid keypoint format");
1277 "Invalid keypoint format");
1279 "Invalid keypoint format");
1281 CHECK(match_options.
Check());
1282 CHECK_NOTNULL(sift_match_gpu);
1283 CHECK_NOTNULL(two_view_geometry);
1285 std::unique_lock<std::mutex> lock(
1286 *sift_matching_mutexes[sift_match_gpu->gpu_index]);
1288 const size_t kFeatureShapeNumElems = 4;
1290 if (descriptors1 !=
nullptr) {
1291 CHECK_NOTNULL(keypoints1);
1292 CHECK_EQ(descriptors1->rows(), keypoints1->size());
1293 CHECK_EQ(descriptors1->cols(), 128);
1294 WarnIfMaxNumMatchesReachedGPU(*sift_match_gpu, *descriptors1);
1295 const size_t kIndex = 0;
1296 sift_match_gpu->SetDescriptors(kIndex, descriptors1->rows(),
1297 descriptors1->data());
1298 sift_match_gpu->SetFeautreLocation(
1299 kIndex,
reinterpret_cast<const float*
>(keypoints1->data()),
1300 kFeatureShapeNumElems);
1303 if (descriptors2 !=
nullptr) {
1304 CHECK_NOTNULL(keypoints2);
1305 CHECK_EQ(descriptors2->rows(), keypoints2->size());
1306 CHECK_EQ(descriptors2->cols(), 128);
1307 WarnIfMaxNumMatchesReachedGPU(*sift_match_gpu, *descriptors2);
1308 const size_t kIndex = 1;
1309 sift_match_gpu->SetDescriptors(kIndex, descriptors2->rows(),
1310 descriptors2->data());
1311 sift_match_gpu->SetFeautreLocation(
1312 kIndex,
reinterpret_cast<const float*
>(keypoints2->data()),
1313 kFeatureShapeNumElems);
1316 Eigen::Matrix<float, 3, 3, Eigen::RowMajor> F;
1317 Eigen::Matrix<float, 3, 3, Eigen::RowMajor> H;
1318 float* F_ptr =
nullptr;
1319 float* H_ptr =
nullptr;
1322 F = two_view_geometry->
F.cast<
float>();
1326 two_view_geometry->
config ==
1328 H = two_view_geometry->
H.cast<
float>();
1334 CHECK(F_ptr !=
nullptr || H_ptr !=
nullptr);
1339 const int num_matches = sift_match_gpu->GetGuidedSiftMatch(
1341 reinterpret_cast<uint32_t(*)[2]
>(
1343 H_ptr, F_ptr,
static_cast<float>(match_options.
max_distance),
1344 static_cast<float>(match_options.
max_ratio),
1345 static_cast<float>(match_options.
max_error *
1347 static_cast<float>(match_options.
max_error *
1351 if (num_matches < 0) {
1353 <<
"ERROR: Feature matching failed. This is probably caused by "
1354 "insufficient GPU memory. Consider reducing the maximum "
1355 "number of features."
std::vector< uint8_t > ConvertToRowMajorArray() const
std::vector< uint8_t > ConvertToRawBits() const
unsigned int ScanWidth() const
#define CHECK_OPTION_LE(val1, val2)
#define CHECK_OPTION_GT(val1, val2)
#define CHECK_OPTION_GE(val1, val2)
QTextStream & endl(QTextStream &stream)
static const std::string path
Eigen::MatrixXf L1RootNormalizeFeatureDescriptors(const Eigen::MatrixXf &descriptors)
bool ExtractCovariantSiftFeaturesCPU(const SiftExtractionOptions &options, const Bitmap &bitmap, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
void MatchGuidedSiftFeaturesGPU(const SiftMatchingOptions &match_options, const FeatureKeypoints *keypoints1, const FeatureKeypoints *keypoints2, const FeatureDescriptors *descriptors1, const FeatureDescriptors *descriptors2, SiftMatchGPU *sift_match_gpu, TwoViewGeometry *two_view_geometry)
bool CreateSiftGPUExtractor(const SiftExtractionOptions &options, SiftGPU *sift_gpu)
void MatchSiftFeaturesGPU(const SiftMatchingOptions &match_options, const FeatureDescriptors *descriptors1, const FeatureDescriptors *descriptors2, SiftMatchGPU *sift_match_gpu, FeatureMatches *matches)
bool CreateSiftGPUMatcher(const SiftMatchingOptions &match_options, SiftMatchGPU *sift_match_gpu)
Eigen::Matrix< uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > FeatureDescriptors
FeatureDescriptors FeatureDescriptorsToUnsignedByte(const Eigen::MatrixXf &descriptors)
Eigen::MatrixXf L2NormalizeFeatureDescriptors(const Eigen::MatrixXf &descriptors)
void MatchSiftFeaturesCPUBruteForce(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
void MatchSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
bool ExtractSiftFeaturesCPU(const SiftExtractionOptions &options, const Bitmap &bitmap, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
void MatchGuidedSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureKeypoints &keypoints1, const FeatureKeypoints &keypoints2, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, TwoViewGeometry *two_view_geometry)
std::vector< FeatureKeypoint > FeatureKeypoints
std::string StringPrintf(const char *format,...)
std::vector< FeatureMatch > FeatureMatches
bool ExtractSiftFeaturesGPU(const SiftExtractionOptions &options, const Bitmap &bitmap, SiftGPU *sift_gpu, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
void LoadSiftFeaturesFromTextFile(const std::string &path, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
void MatchSiftFeaturesCPUFLANN(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
Eigen::MatrixXd::Index Index
std::string to_string(const T &n)
FeatureMatches inlier_matches