ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
sift_test.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #define TEST_NAME "feature/sift_test"
33 #include "util/testing.h"
34 
35 #ifdef GUI_ENABLED
36 #include <QApplication>
37 #else
38 #include "exe/gui.h"
39 #endif
40 
41 #include "SiftGPU/SiftGPU.h"
42 #include "feature/sift.h"
43 #include "feature/utils.h"
44 #include "util/math.h"
45 #include "util/opengl_utils.h"
46 #include "util/random.h"
47 
48 using namespace colmap;
49 
50 void CreateImageWithSquare(const int size, Bitmap* bitmap) {
51  bitmap->Allocate(size, size, false);
52  bitmap->Fill(BitmapColor<uint8_t>(0, 0, 0));
53  for (int r = size / 2 - size / 8; r < size / 2 + size / 8; ++r) {
54  for (int c = size / 2 - size / 8; c < size / 2 + size / 8; ++c) {
55  bitmap->SetPixel(r, c, BitmapColor<uint8_t>(255));
56  }
57  }
58 }
59 
60 BOOST_AUTO_TEST_CASE(TestExtractSiftFeaturesCPU) {
61  Bitmap bitmap;
62  CreateImageWithSquare(256, &bitmap);
63 
64  FeatureKeypoints keypoints;
66  BOOST_CHECK(ExtractSiftFeaturesCPU(SiftExtractionOptions(), bitmap,
67  &keypoints, &descriptors));
68 
69  BOOST_CHECK_EQUAL(keypoints.size(), 22);
70  for (size_t i = 0; i < keypoints.size(); ++i) {
71  BOOST_CHECK_GE(keypoints[i].x, 0);
72  BOOST_CHECK_GE(keypoints[i].y, 0);
73  BOOST_CHECK_LE(keypoints[i].x, bitmap.Width());
74  BOOST_CHECK_LE(keypoints[i].y, bitmap.Height());
75  BOOST_CHECK_GT(keypoints[i].ComputeScale(), 0);
76  BOOST_CHECK_GT(keypoints[i].ComputeOrientation(), -M_PI);
77  BOOST_CHECK_LT(keypoints[i].ComputeOrientation(), M_PI);
78  }
79 
80  BOOST_CHECK_EQUAL(descriptors.rows(), 22);
81  for (FeatureDescriptors::Index i = 0; i < descriptors.rows(); ++i) {
82  BOOST_CHECK_LT(std::abs(descriptors.row(i).cast<float>().norm() - 512), 1);
83  }
84 }
85 
86 BOOST_AUTO_TEST_CASE(TestExtractCovariantSiftFeaturesCPU) {
87  Bitmap bitmap;
88  CreateImageWithSquare(256, &bitmap);
89 
90  FeatureKeypoints keypoints;
93  &keypoints, &descriptors));
94 
95  BOOST_CHECK_EQUAL(keypoints.size(), 22);
96  for (size_t i = 0; i < keypoints.size(); ++i) {
97  BOOST_CHECK_GE(keypoints[i].x, 0);
98  BOOST_CHECK_GE(keypoints[i].y, 0);
99  BOOST_CHECK_LE(keypoints[i].x, bitmap.Width());
100  BOOST_CHECK_LE(keypoints[i].y, bitmap.Height());
101  BOOST_CHECK_GT(keypoints[i].ComputeScale(), 0);
102  BOOST_CHECK_GT(keypoints[i].ComputeOrientation(), -M_PI);
103  BOOST_CHECK_LT(keypoints[i].ComputeOrientation(), M_PI);
104  }
105 
106  BOOST_CHECK_EQUAL(descriptors.rows(), 22);
107  for (FeatureDescriptors::Index i = 0; i < descriptors.rows(); ++i) {
108  BOOST_CHECK_LT(std::abs(descriptors.row(i).cast<float>().norm() - 512), 1);
109  }
110 }
111 
112 BOOST_AUTO_TEST_CASE(TestExtractCovariantAffineSiftFeaturesCPU) {
113  Bitmap bitmap;
114  CreateImageWithSquare(256, &bitmap);
115 
116  FeatureKeypoints keypoints;
118  SiftExtractionOptions options;
119  options.estimate_affine_shape = true;
120  BOOST_CHECK(ExtractCovariantSiftFeaturesCPU(options, bitmap, &keypoints,
121  &descriptors));
122 
123  BOOST_CHECK_EQUAL(keypoints.size(), 10);
124  for (size_t i = 0; i < keypoints.size(); ++i) {
125  BOOST_CHECK_GE(keypoints[i].x, 0);
126  BOOST_CHECK_GE(keypoints[i].y, 0);
127  BOOST_CHECK_LE(keypoints[i].x, bitmap.Width());
128  BOOST_CHECK_LE(keypoints[i].y, bitmap.Height());
129  BOOST_CHECK_GT(keypoints[i].ComputeScale(), 0);
130  BOOST_CHECK_GT(keypoints[i].ComputeOrientation(), -M_PI);
131  BOOST_CHECK_LT(keypoints[i].ComputeOrientation(), M_PI);
132  }
133 
134  BOOST_CHECK_EQUAL(descriptors.rows(), 10);
135  for (FeatureDescriptors::Index i = 0; i < descriptors.rows(); ++i) {
136  BOOST_CHECK_LT(std::abs(descriptors.row(i).cast<float>().norm() - 512), 1);
137  }
138 }
139 
140 BOOST_AUTO_TEST_CASE(TestExtractCovariantDSPSiftFeaturesCPU) {
141  Bitmap bitmap;
142  CreateImageWithSquare(256, &bitmap);
143 
144  FeatureKeypoints keypoints;
146  SiftExtractionOptions options;
147  options.domain_size_pooling = true;
148  BOOST_CHECK(ExtractCovariantSiftFeaturesCPU(options, bitmap, &keypoints,
149  &descriptors));
150 
151  BOOST_CHECK_EQUAL(keypoints.size(), 22);
152  for (size_t i = 0; i < keypoints.size(); ++i) {
153  BOOST_CHECK_GE(keypoints[i].x, 0);
154  BOOST_CHECK_GE(keypoints[i].y, 0);
155  BOOST_CHECK_LE(keypoints[i].x, bitmap.Width());
156  BOOST_CHECK_LE(keypoints[i].y, bitmap.Height());
157  BOOST_CHECK_GT(keypoints[i].ComputeScale(), 0);
158  BOOST_CHECK_GT(keypoints[i].ComputeOrientation(), -M_PI);
159  BOOST_CHECK_LT(keypoints[i].ComputeOrientation(), M_PI);
160  }
161 
162  BOOST_CHECK_EQUAL(descriptors.rows(), 22);
163  for (FeatureDescriptors::Index i = 0; i < descriptors.rows(); ++i) {
164  BOOST_CHECK_LT(std::abs(descriptors.row(i).cast<float>().norm() - 512), 1);
165  }
166 }
167 
168 BOOST_AUTO_TEST_CASE(TestExtractCovariantAffineDSPSiftFeaturesCPU) {
169  Bitmap bitmap;
170  CreateImageWithSquare(256, &bitmap);
171 
172  FeatureKeypoints keypoints;
174  SiftExtractionOptions options;
175  options.estimate_affine_shape = true;
176  options.domain_size_pooling = true;
177  BOOST_CHECK(ExtractCovariantSiftFeaturesCPU(options, bitmap, &keypoints,
178  &descriptors));
179 
180  BOOST_CHECK_EQUAL(keypoints.size(), 10);
181  for (size_t i = 0; i < keypoints.size(); ++i) {
182  BOOST_CHECK_GE(keypoints[i].x, 0);
183  BOOST_CHECK_GE(keypoints[i].y, 0);
184  BOOST_CHECK_LE(keypoints[i].x, bitmap.Width());
185  BOOST_CHECK_LE(keypoints[i].y, bitmap.Height());
186  BOOST_CHECK_GT(keypoints[i].ComputeScale(), 0);
187  BOOST_CHECK_GT(keypoints[i].ComputeOrientation(), -M_PI);
188  BOOST_CHECK_LT(keypoints[i].ComputeOrientation(), M_PI);
189  }
190 
191  BOOST_CHECK_EQUAL(descriptors.rows(), 10);
192  for (FeatureDescriptors::Index i = 0; i < descriptors.rows(); ++i) {
193  BOOST_CHECK_LT(std::abs(descriptors.row(i).cast<float>().norm() - 512), 1);
194  }
195 }
196 
197 BOOST_AUTO_TEST_CASE(TestExtractSiftFeaturesGPU) {
198  char app_name[] = "Test";
199  int argc = 1;
200  char* argv[] = {app_name};
201  QApplication app(argc, argv);
202 
204  return;
205  }
206 
207  class TestThread : public Thread {
208  private:
209  void Run() {
210  opengl_context_.MakeCurrent();
211 
212  Bitmap bitmap;
213  CreateImageWithSquare(256, &bitmap);
214 
215  SiftGPU sift_gpu;
216  BOOST_CHECK(CreateSiftGPUExtractor(SiftExtractionOptions(), &sift_gpu));
217 
218  FeatureKeypoints keypoints;
220  BOOST_CHECK(ExtractSiftFeaturesGPU(SiftExtractionOptions(), bitmap,
221  &sift_gpu, &keypoints, &descriptors));
222 
223  BOOST_CHECK_EQUAL(keypoints.size(), 24);
224  for (size_t i = 0; i < keypoints.size(); ++i) {
225  BOOST_CHECK_GE(keypoints[i].x, 0);
226  BOOST_CHECK_GE(keypoints[i].y, 0);
227  BOOST_CHECK_LE(keypoints[i].x, bitmap.Width());
228  BOOST_CHECK_LE(keypoints[i].y, bitmap.Height());
229  BOOST_CHECK_GT(keypoints[i].ComputeScale(), 0);
230  BOOST_CHECK_GT(keypoints[i].ComputeOrientation(), -M_PI);
231  BOOST_CHECK_LT(keypoints[i].ComputeOrientation(), M_PI);
232  }
233 
234  BOOST_CHECK_EQUAL(descriptors.rows(), 24);
235  for (FeatureDescriptors::Index i = 0; i < descriptors.rows(); ++i) {
236  BOOST_CHECK_LT(std::abs(descriptors.row(i).cast<float>().norm() - 512),
237  1);
238  }
239  }
240  OpenGLContextManager opengl_context_;
241  };
242 
243  TestThread thread;
245 }
246 
248  SetPRNGSeed(0);
249  Eigen::MatrixXf descriptors(num_features, 128);
250  for (size_t i = 0; i < num_features; ++i) {
251  for (size_t j = 0; j < 128; ++j) {
252  descriptors(i, j) = std::pow(RandomReal(0.0f, 1.0f), 2);
253  }
254  }
257 }
258 
259 void CheckEqualMatches(const FeatureMatches& matches1,
260  const FeatureMatches& matches2) {
261  BOOST_REQUIRE_EQUAL(matches1.size(), matches2.size());
262  for (size_t i = 0; i < matches1.size(); ++i) {
263  BOOST_CHECK_EQUAL(matches1[i].point2D_idx1, matches2[i].point2D_idx1);
264  BOOST_CHECK_EQUAL(matches1[i].point2D_idx2, matches2[i].point2D_idx2);
265  }
266 }
267 
268 BOOST_AUTO_TEST_CASE(TestCreateSiftGPUMatcherOpenGL) {
269  char app_name[] = "Test";
270  int argc = 1;
271  char* argv[] = {app_name};
272  QApplication app(argc, argv);
273 
275  return;
276  }
277 
278  class TestThread : public Thread {
279  private:
280  void Run() {
281  opengl_context_.MakeCurrent();
282  SiftMatchGPU sift_match_gpu;
283  SiftMatchingOptions match_options;
284  match_options.max_num_matches = 1000;
285  BOOST_CHECK(CreateSiftGPUMatcher(match_options, &sift_match_gpu));
286  }
287  OpenGLContextManager opengl_context_;
288  };
289 
290  TestThread thread;
292 }
293 
294 BOOST_AUTO_TEST_CASE(TestCreateSiftGPUMatcherCUDA) {
295 #ifdef CUDA_ENABLED
296  SiftMatchGPU sift_match_gpu;
297  SiftMatchingOptions match_options;
298  match_options.gpu_index = "0";
299  match_options.max_num_matches = 1000;
300  BOOST_CHECK(CreateSiftGPUMatcher(match_options, &sift_match_gpu));
301 #endif
302 }
303 
304 BOOST_AUTO_TEST_CASE(TestMatchSiftFeaturesCPU) {
305  const FeatureDescriptors empty_descriptors =
307  const FeatureDescriptors descriptors1 = CreateRandomFeatureDescriptors(2);
308  const FeatureDescriptors descriptors2 = descriptors1.colwise().reverse();
309 
310  FeatureMatches matches;
311 
312  MatchSiftFeaturesCPU(SiftMatchingOptions(), descriptors1, descriptors2,
313  &matches);
314  BOOST_CHECK_EQUAL(matches.size(), 2);
315  BOOST_CHECK_EQUAL(matches[0].point2D_idx1, 0);
316  BOOST_CHECK_EQUAL(matches[0].point2D_idx2, 1);
317  BOOST_CHECK_EQUAL(matches[1].point2D_idx1, 1);
318  BOOST_CHECK_EQUAL(matches[1].point2D_idx2, 0);
319 
320  MatchSiftFeaturesCPU(SiftMatchingOptions(), empty_descriptors, descriptors2,
321  &matches);
322  BOOST_CHECK_EQUAL(matches.size(), 0);
323  MatchSiftFeaturesCPU(SiftMatchingOptions(), descriptors1, empty_descriptors,
324  &matches);
325  BOOST_CHECK_EQUAL(matches.size(), 0);
326  MatchSiftFeaturesCPU(SiftMatchingOptions(), empty_descriptors,
327  empty_descriptors, &matches);
328  BOOST_CHECK_EQUAL(matches.size(), 0);
329 }
330 
331 BOOST_AUTO_TEST_CASE(TestMatchSiftFeaturesCPUFLANNvsBruteForce) {
332  SiftMatchingOptions match_options;
333  match_options.max_num_matches = 1000;
334 
335  auto TestFLANNvsBruteForce = [](const SiftMatchingOptions& options,
336  const FeatureDescriptors& descriptors1,
337  const FeatureDescriptors& descriptors2) {
338  FeatureMatches matches_bf;
339  FeatureMatches matches_flann;
340 
341  MatchSiftFeaturesCPUBruteForce(options, descriptors1, descriptors2,
342  &matches_bf);
343  MatchSiftFeaturesCPUFLANN(options, descriptors1, descriptors2,
344  &matches_flann);
345  CheckEqualMatches(matches_bf, matches_flann);
346 
347  const size_t num_matches = matches_bf.size();
348 
349  const FeatureDescriptors empty_descriptors =
351 
352  MatchSiftFeaturesCPUBruteForce(options, empty_descriptors, descriptors2,
353  &matches_bf);
354  MatchSiftFeaturesCPUFLANN(options, empty_descriptors, descriptors2,
355  &matches_flann);
356  CheckEqualMatches(matches_bf, matches_flann);
357 
358  MatchSiftFeaturesCPUBruteForce(options, descriptors1, empty_descriptors,
359  &matches_bf);
360  MatchSiftFeaturesCPUFLANN(options, descriptors1, empty_descriptors,
361  &matches_flann);
362  CheckEqualMatches(matches_bf, matches_flann);
363 
364  MatchSiftFeaturesCPUBruteForce(options, empty_descriptors,
365  empty_descriptors, &matches_bf);
366  MatchSiftFeaturesCPUFLANN(options, empty_descriptors, empty_descriptors,
367  &matches_flann);
368  CheckEqualMatches(matches_bf, matches_flann);
369 
370  return num_matches;
371  };
372 
373  {
374  const FeatureDescriptors descriptors1 = CreateRandomFeatureDescriptors(100);
375  const FeatureDescriptors descriptors2 = CreateRandomFeatureDescriptors(100);
376  SiftMatchingOptions match_options;
377  TestFLANNvsBruteForce(match_options, descriptors1, descriptors2);
378  }
379 
380  {
381  const FeatureDescriptors descriptors1 = CreateRandomFeatureDescriptors(100);
382  const FeatureDescriptors descriptors2 = descriptors1.colwise().reverse();
383  SiftMatchingOptions match_options;
384  const size_t num_matches =
385  TestFLANNvsBruteForce(match_options, descriptors1, descriptors2);
386  BOOST_CHECK_EQUAL(num_matches, 100);
387  }
388 
389  // Check the ratio test.
390  {
392  FeatureDescriptors descriptors2 = descriptors1;
393 
394  SiftMatchingOptions match_options;
395  const size_t num_matches1 =
396  TestFLANNvsBruteForce(match_options, descriptors1, descriptors2);
397  BOOST_CHECK_EQUAL(num_matches1, 100);
398 
399  descriptors2.row(99) = descriptors2.row(0);
400  descriptors2(0, 0) += 50.0f;
401  descriptors2.row(0) = FeatureDescriptorsToUnsignedByte(
402  L2NormalizeFeatureDescriptors(descriptors2.row(0).cast<float>()));
403  descriptors2(99, 0) += 100.0f;
404  descriptors2.row(99) = FeatureDescriptorsToUnsignedByte(
405  L2NormalizeFeatureDescriptors(descriptors2.row(99).cast<float>()));
406 
407  match_options.max_ratio = 0.4;
408  const size_t num_matches2 = TestFLANNvsBruteForce(
409  match_options, descriptors1.topRows(99), descriptors2);
410  BOOST_CHECK_EQUAL(num_matches2, 98);
411 
412  match_options.max_ratio = 0.5;
413  const size_t num_matches3 =
414  TestFLANNvsBruteForce(match_options, descriptors1, descriptors2);
415  BOOST_CHECK_EQUAL(num_matches3, 99);
416  }
417 
418  // Check the cross check.
419  {
421  FeatureDescriptors descriptors2 = descriptors1;
422  descriptors1.row(0) = descriptors1.row(1);
423 
424  SiftMatchingOptions match_options;
425 
426  match_options.cross_check = false;
427  const size_t num_matches1 =
428  TestFLANNvsBruteForce(match_options, descriptors1, descriptors2);
429  BOOST_CHECK_EQUAL(num_matches1, 100);
430 
431  match_options.cross_check = true;
432  const size_t num_matches2 =
433  TestFLANNvsBruteForce(match_options, descriptors1, descriptors2);
434  BOOST_CHECK_EQUAL(num_matches2, 98);
435  }
436 }
437 
438 BOOST_AUTO_TEST_CASE(TestMatchGuidedSiftFeaturesCPU) {
439  FeatureKeypoints empty_keypoints(0);
440  FeatureKeypoints keypoints1(2);
441  keypoints1[0].x = 1;
442  keypoints1[1].x = 2;
443  FeatureKeypoints keypoints2(2);
444  keypoints2[0].x = 2;
445  keypoints2[1].x = 1;
446  const FeatureDescriptors empty_descriptors =
448  const FeatureDescriptors descriptors1 = CreateRandomFeatureDescriptors(2);
449  const FeatureDescriptors descriptors2 = descriptors1.colwise().reverse();
450 
451  TwoViewGeometry two_view_geometry;
452  two_view_geometry.config = TwoViewGeometry::PLANAR_OR_PANORAMIC;
453  two_view_geometry.H = Eigen::Matrix3d::Identity();
454 
455  MatchGuidedSiftFeaturesCPU(SiftMatchingOptions(), keypoints1, keypoints2,
456  descriptors1, descriptors2, &two_view_geometry);
457  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 2);
458  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 0);
459  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 1);
460  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx1, 1);
461  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx2, 0);
462 
463  keypoints1[0].x = 100;
464  MatchGuidedSiftFeaturesCPU(SiftMatchingOptions(), keypoints1, keypoints2,
465  descriptors1, descriptors2, &two_view_geometry);
466  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 1);
467  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 1);
468  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 0);
469 
470  MatchGuidedSiftFeaturesCPU(SiftMatchingOptions(), empty_keypoints, keypoints2,
471  empty_descriptors, descriptors2,
472  &two_view_geometry);
473  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 0);
474  MatchGuidedSiftFeaturesCPU(SiftMatchingOptions(), keypoints1, empty_keypoints,
475  descriptors1, empty_descriptors,
476  &two_view_geometry);
477  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 0);
479  empty_keypoints, empty_descriptors,
480  empty_descriptors, &two_view_geometry);
481  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 0);
482 }
483 
484 BOOST_AUTO_TEST_CASE(TestMatchSiftFeaturesGPU) {
485  char app_name[] = "Test";
486  int argc = 1;
487  char* argv[] = {app_name};
488  QApplication app(argc, argv);
489 
491  return;
492  }
493 
494  class TestThread : public Thread {
495  private:
496  void Run() {
497  opengl_context_.MakeCurrent();
498  SiftMatchGPU sift_match_gpu;
499  SiftMatchingOptions match_options;
500  match_options.max_num_matches = 1000;
501  BOOST_CHECK(CreateSiftGPUMatcher(match_options, &sift_match_gpu));
502 
503  const FeatureDescriptors empty_descriptors =
505  const FeatureDescriptors descriptors1 = CreateRandomFeatureDescriptors(2);
506  const FeatureDescriptors descriptors2 = descriptors1.colwise().reverse();
507 
508  FeatureMatches matches;
509 
510  MatchSiftFeaturesGPU(SiftMatchingOptions(), &descriptors1, &descriptors2,
511  &sift_match_gpu, &matches);
512  BOOST_CHECK_EQUAL(matches.size(), 2);
513  BOOST_CHECK_EQUAL(matches[0].point2D_idx1, 0);
514  BOOST_CHECK_EQUAL(matches[0].point2D_idx2, 1);
515  BOOST_CHECK_EQUAL(matches[1].point2D_idx1, 1);
516  BOOST_CHECK_EQUAL(matches[1].point2D_idx2, 0);
517 
518  MatchSiftFeaturesGPU(SiftMatchingOptions(), nullptr, nullptr,
519  &sift_match_gpu, &matches);
520  BOOST_CHECK_EQUAL(matches.size(), 2);
521  BOOST_CHECK_EQUAL(matches[0].point2D_idx1, 0);
522  BOOST_CHECK_EQUAL(matches[0].point2D_idx2, 1);
523  BOOST_CHECK_EQUAL(matches[1].point2D_idx1, 1);
524  BOOST_CHECK_EQUAL(matches[1].point2D_idx2, 0);
525 
526  MatchSiftFeaturesGPU(SiftMatchingOptions(), &descriptors1, nullptr,
527  &sift_match_gpu, &matches);
528  BOOST_CHECK_EQUAL(matches.size(), 2);
529  BOOST_CHECK_EQUAL(matches[0].point2D_idx1, 0);
530  BOOST_CHECK_EQUAL(matches[0].point2D_idx2, 1);
531  BOOST_CHECK_EQUAL(matches[1].point2D_idx1, 1);
532  BOOST_CHECK_EQUAL(matches[1].point2D_idx2, 0);
533 
534  MatchSiftFeaturesGPU(SiftMatchingOptions(), nullptr, &descriptors2,
535  &sift_match_gpu, &matches);
536  BOOST_CHECK_EQUAL(matches.size(), 2);
537  BOOST_CHECK_EQUAL(matches[0].point2D_idx1, 0);
538  BOOST_CHECK_EQUAL(matches[0].point2D_idx2, 1);
539  BOOST_CHECK_EQUAL(matches[1].point2D_idx1, 1);
540  BOOST_CHECK_EQUAL(matches[1].point2D_idx2, 0);
541 
542  MatchSiftFeaturesGPU(SiftMatchingOptions(), &empty_descriptors,
543  &descriptors2, &sift_match_gpu, &matches);
544  BOOST_CHECK_EQUAL(matches.size(), 0);
545  MatchSiftFeaturesGPU(SiftMatchingOptions(), &descriptors1,
546  &empty_descriptors, &sift_match_gpu, &matches);
547  BOOST_CHECK_EQUAL(matches.size(), 0);
548  MatchSiftFeaturesGPU(SiftMatchingOptions(), &empty_descriptors,
549  &empty_descriptors, &sift_match_gpu, &matches);
550  BOOST_CHECK_EQUAL(matches.size(), 0);
551  }
552  OpenGLContextManager opengl_context_;
553  };
554 
555  TestThread thread;
557 }
558 
559 BOOST_AUTO_TEST_CASE(TestMatchSiftFeaturesCPUvsGPU) {
560  char app_name[] = "Test";
561  int argc = 1;
562  char* argv[] = {app_name};
563  QApplication app(argc, argv);
564 
566  return;
567  }
568 
569  class TestThread : public Thread {
570  private:
571  void Run() {
572  opengl_context_.MakeCurrent();
573  SiftMatchGPU sift_match_gpu;
574  SiftMatchingOptions match_options;
575  match_options.max_num_matches = 1000;
576  BOOST_CHECK(CreateSiftGPUMatcher(match_options, &sift_match_gpu));
577 
578  auto TestCPUvsGPU = [&sift_match_gpu](
579  const SiftMatchingOptions& options,
580  const FeatureDescriptors& descriptors1,
581  const FeatureDescriptors& descriptors2) {
582  FeatureMatches matches_cpu;
583  FeatureMatches matches_gpu;
584 
585  MatchSiftFeaturesCPU(options, descriptors1, descriptors2, &matches_cpu);
586  MatchSiftFeaturesGPU(options, &descriptors1, &descriptors2,
587  &sift_match_gpu, &matches_gpu);
588  CheckEqualMatches(matches_cpu, matches_gpu);
589 
590  const size_t num_matches = matches_cpu.size();
591 
592  const FeatureDescriptors empty_descriptors =
594 
595  MatchSiftFeaturesCPU(options, empty_descriptors, descriptors2,
596  &matches_cpu);
597  MatchSiftFeaturesGPU(options, &empty_descriptors, &descriptors2,
598  &sift_match_gpu, &matches_gpu);
599  CheckEqualMatches(matches_cpu, matches_gpu);
600 
601  MatchSiftFeaturesCPU(options, descriptors1, empty_descriptors,
602  &matches_cpu);
603  MatchSiftFeaturesGPU(options, &descriptors1, &empty_descriptors,
604  &sift_match_gpu, &matches_gpu);
605  CheckEqualMatches(matches_cpu, matches_gpu);
606 
607  MatchSiftFeaturesCPU(options, empty_descriptors, empty_descriptors,
608  &matches_cpu);
609  MatchSiftFeaturesGPU(options, &empty_descriptors, &empty_descriptors,
610  &sift_match_gpu, &matches_gpu);
611  CheckEqualMatches(matches_cpu, matches_gpu);
612 
613  return num_matches;
614  };
615 
616  {
617  const FeatureDescriptors descriptors1 =
619  const FeatureDescriptors descriptors2 =
621  SiftMatchingOptions match_options;
622  TestCPUvsGPU(match_options, descriptors1, descriptors2);
623  }
624 
625  {
626  const FeatureDescriptors descriptors1 =
628  const FeatureDescriptors descriptors2 =
629  descriptors1.colwise().reverse();
630  SiftMatchingOptions match_options;
631  const size_t num_matches =
632  TestCPUvsGPU(match_options, descriptors1, descriptors2);
633  BOOST_CHECK_EQUAL(num_matches, 100);
634  }
635 
636  // Check the ratio test.
637  {
639  FeatureDescriptors descriptors2 = descriptors1;
640 
641  SiftMatchingOptions match_options;
642  const size_t num_matches1 =
643  TestCPUvsGPU(match_options, descriptors1, descriptors2);
644  BOOST_CHECK_EQUAL(num_matches1, 100);
645 
646  descriptors2.row(99) = descriptors2.row(0);
647  descriptors2(0, 0) += 50.0f;
648  descriptors2.row(0) = FeatureDescriptorsToUnsignedByte(
649  L2NormalizeFeatureDescriptors(descriptors2.row(0).cast<float>()));
650  descriptors2(99, 0) += 100.0f;
651  descriptors2.row(99) = FeatureDescriptorsToUnsignedByte(
652  L2NormalizeFeatureDescriptors(descriptors2.row(99).cast<float>()));
653 
654  match_options.max_ratio = 0.4;
655  const size_t num_matches2 =
656  TestCPUvsGPU(match_options, descriptors1.topRows(99), descriptors2);
657  BOOST_CHECK_EQUAL(num_matches2, 98);
658 
659  match_options.max_ratio = 0.5;
660  const size_t num_matches3 =
661  TestCPUvsGPU(match_options, descriptors1, descriptors2);
662  BOOST_CHECK_EQUAL(num_matches3, 99);
663  }
664 
665  // Check the cross check.
666  {
668  FeatureDescriptors descriptors2 = descriptors1;
669  descriptors1.row(0) = descriptors1.row(1);
670 
671  SiftMatchingOptions match_options;
672 
673  match_options.cross_check = false;
674  const size_t num_matches1 =
675  TestCPUvsGPU(match_options, descriptors1, descriptors2);
676  BOOST_CHECK_EQUAL(num_matches1, 100);
677 
678  match_options.cross_check = true;
679  const size_t num_matches2 =
680  TestCPUvsGPU(match_options, descriptors1, descriptors2);
681  BOOST_CHECK_EQUAL(num_matches2, 98);
682  }
683  }
684  OpenGLContextManager opengl_context_;
685  };
686 
687  TestThread thread;
689 }
690 
691 BOOST_AUTO_TEST_CASE(TestMatchGuidedSiftFeaturesGPU) {
692  char app_name[] = "Test";
693  int argc = 1;
694  char* argv[] = {app_name};
695  QApplication app(argc, argv);
696 
698  return;
699  }
700 
701  class TestThread : public Thread {
702  private:
703  void Run() {
704  opengl_context_.MakeCurrent();
705  SiftMatchGPU sift_match_gpu;
706  SiftMatchingOptions match_options;
707  match_options.max_num_matches = 1000;
708  BOOST_CHECK(CreateSiftGPUMatcher(match_options, &sift_match_gpu));
709 
710  FeatureKeypoints empty_keypoints(0);
711  FeatureKeypoints keypoints1(2);
712  keypoints1[0].x = 1;
713  keypoints1[1].x = 2;
714  FeatureKeypoints keypoints2(2);
715  keypoints2[0].x = 2;
716  keypoints2[1].x = 1;
717  const FeatureDescriptors empty_descriptors =
719  const FeatureDescriptors descriptors1 = CreateRandomFeatureDescriptors(2);
720  const FeatureDescriptors descriptors2 = descriptors1.colwise().reverse();
721 
722  TwoViewGeometry two_view_geometry;
723  two_view_geometry.config = TwoViewGeometry::PLANAR_OR_PANORAMIC;
724  two_view_geometry.H = Eigen::Matrix3d::Identity();
725 
727  &keypoints2, &descriptors1, &descriptors2,
728  &sift_match_gpu, &two_view_geometry);
729  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 2);
730  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 0);
731  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 1);
732  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx1, 1);
733  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx2, 0);
734 
736  nullptr, nullptr, &sift_match_gpu,
737  &two_view_geometry);
738  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 2);
739  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 0);
740  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 1);
741  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx1, 1);
742  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx2, 0);
743 
744  MatchGuidedSiftFeaturesGPU(SiftMatchingOptions(), &keypoints1, nullptr,
745  &descriptors1, nullptr, &sift_match_gpu,
746  &two_view_geometry);
747  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 2);
748  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 0);
749  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 1);
750  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx1, 1);
751  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx2, 0);
752 
753  MatchGuidedSiftFeaturesGPU(SiftMatchingOptions(), nullptr, &keypoints2,
754  nullptr, &descriptors2, &sift_match_gpu,
755  &two_view_geometry);
756  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 2);
757  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 0);
758  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 1);
759  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx1, 1);
760  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[1].point2D_idx2, 0);
761 
762  keypoints1[0].x = 100;
764  &keypoints2, &descriptors1, &descriptors2,
765  &sift_match_gpu, &two_view_geometry);
766  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 1);
767  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx1, 1);
768  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches[0].point2D_idx2, 0);
769 
771  &keypoints2, &empty_descriptors, &descriptors2,
772  &sift_match_gpu, &two_view_geometry);
773  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 0);
775  SiftMatchingOptions(), &keypoints1, &empty_keypoints, &descriptors1,
776  &empty_descriptors, &sift_match_gpu, &two_view_geometry);
777  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 0);
779  &empty_keypoints, &empty_descriptors,
780  &empty_descriptors, &sift_match_gpu,
781  &two_view_geometry);
782  BOOST_CHECK_EQUAL(two_view_geometry.inlier_matches.size(), 0);
783  }
784  OpenGLContextManager opengl_context_;
785  };
786 
787  TestThread thread;
789 }
constexpr double M_PI
Pi.
Definition: CVConst.h:19
int Run(int argc, const char *argv[])
int size
bool SetPixel(const int x, const int y, const BitmapColor< uint8_t > &color)
Definition: bitmap.cc:199
void Fill(const BitmapColor< uint8_t > &color)
Definition: bitmap.cc:226
bool Allocate(const int width, const int height, const bool as_rgb)
Definition: bitmap.cc:104
int Width() const
Definition: bitmap.h:249
int Height() const
Definition: bitmap.h:250
normal_z y
normal_z x
void SetPRNGSeed(unsigned seed)
Definition: random.cc:40
bool ExtractCovariantSiftFeaturesCPU(const SiftExtractionOptions &options, const Bitmap &bitmap, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
Definition: sift.cc:599
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)
Definition: sift.cc:1267
bool CreateSiftGPUExtractor(const SiftExtractionOptions &options, SiftGPU *sift_gpu)
Definition: sift.cc:776
void RunThreadWithOpenGLContext(Thread *thread)
Definition: opengl_utils.h:81
void MatchSiftFeaturesGPU(const SiftMatchingOptions &match_options, const FeatureDescriptors *descriptors1, const FeatureDescriptors *descriptors2, SiftMatchGPU *sift_match_gpu, FeatureMatches *matches)
Definition: sift.cc:1195
bool CreateSiftGPUMatcher(const SiftMatchingOptions &match_options, SiftMatchGPU *sift_match_gpu)
Definition: sift.cc:1124
Eigen::Matrix< uint8_t, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > FeatureDescriptors
Definition: types.h:79
FeatureDescriptors FeatureDescriptorsToUnsignedByte(const Eigen::MatrixXf &descriptors)
Definition: utils.cc:65
Eigen::MatrixXf L2NormalizeFeatureDescriptors(const Eigen::MatrixXf &descriptors)
Definition: utils.cc:47
void MatchSiftFeaturesCPUBruteForce(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
Definition: sift.cc:998
void MatchSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
Definition: sift.cc:1042
bool ExtractSiftFeaturesCPU(const SiftExtractionOptions &options, const Bitmap &bitmap, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
Definition: sift.cc:419
void MatchGuidedSiftFeaturesCPU(const SiftMatchingOptions &match_options, const FeatureKeypoints &keypoints1, const FeatureKeypoints &keypoints2, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, TwoViewGeometry *two_view_geometry)
Definition: sift.cc:1050
std::vector< FeatureKeypoint > FeatureKeypoints
Definition: types.h:77
T RandomReal(const T min, const T max)
Definition: random.h:75
std::vector< FeatureMatch > FeatureMatches
Definition: types.h:80
bool ExtractSiftFeaturesGPU(const SiftExtractionOptions &options, const Bitmap &bitmap, SiftGPU *sift_gpu, FeatureKeypoints *keypoints, FeatureDescriptors *descriptors)
Definition: sift.cc:875
void MatchSiftFeaturesCPUFLANN(const SiftMatchingOptions &match_options, const FeatureDescriptors &descriptors1, const FeatureDescriptors &descriptors2, FeatureMatches *matches)
Definition: sift.cc:1013
Eigen::MatrixXd::Index Index
Definition: knncpp.h:26
CorePointDescSet * descriptors
void CreateImageWithSquare(const int size, Bitmap *bitmap)
Definition: sift_test.cc:50
void CheckEqualMatches(const FeatureMatches &matches1, const FeatureMatches &matches2)
Definition: sift_test.cc:259
FeatureDescriptors CreateRandomFeatureDescriptors(const size_t num_features)
Definition: sift_test.cc:247
BOOST_AUTO_TEST_CASE(TestExtractSiftFeaturesCPU)
Definition: sift_test.cc:60
std::string gpu_index
Definition: sift.h:109