-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmatchergpu.h
More file actions
375 lines (301 loc) · 14.5 KB
/
matchergpu.h
File metadata and controls
375 lines (301 loc) · 14.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
#ifndef MATCHERGPU_H
#define MATCHERGPU_H
/*------------------------------------------------------------------------------------------*\
This file is a GPU version of Laganiere's matcher.
\*------------------------------------------------------------------------------------------*/
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
// NEW IMPORTS from OpenCV 2.4.3
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/nonfree/features2d.hpp"
#include "opencv2/photo/photo.hpp"
#include <opencv2/legacy/legacy.hpp>
#include "opencv2/nonfree/gpu.hpp"
//#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/gpu/gpu.hpp"
#include <opencv2/nonfree/gpu.hpp> // for SURF
using namespace std;
class RobustGpuMatcher
{
private:
// pointer to the feature point detector object
cv::Ptr<cv::FeatureDetector> detector;
// pointer to the feature descriptor extractor object
cv::Ptr<cv::DescriptorExtractor> extractor;
cv::gpu::SURF_GPU surf;
float ratio; // max ratio between 1st and 2nd NN
bool refineF; // if true will refine the F matrix
double confidence; // confidence level (probability)
double distance; // min distance to epipolar
int _image_features_1;
int _image_features_2;
public:
RobustGpuMatcher() : ratio(0.65f), refineF(true), confidence(0.99), distance(3.0)
{
// SURF is the default feature
detector= new cv::SurfFeatureDetector();
extractor= new cv::SurfDescriptorExtractor();
}
// Set the feature detector
void setFeatureDetector (cv::Ptr<cv::FeatureDetector>& detect)
{
detector= detect;
}
// Set descriptor extractor
void setDescriptorExtractor (cv::Ptr<cv::DescriptorExtractor>& desc)
{
extractor= desc;
}
// Set the minimum distance to epipolar in RANSAC
void setMinDistanceToEpipolar (double d)
{
distance= d;
}
// Set confidence level in RANSAC
void setConfidenceLevel (double c)
{
confidence= c;
}
// Set the NN ratio
void setRatio (float r)
{
ratio= r;
}
// if you want the F matrix to be recalculated
void refineFundamental (bool flag)
{
refineF= flag;
}
// Clear matches for which NN ratio is > than threshold
// return the number of removed points
// (corresponding entries being cleared, i.e. size will be 0)
int ratioTest (vector <vector<cv::DMatch> > & matches)
{
int removed=0;
// for all matches
for (vector <vector<cv::DMatch> > ::iterator matchIterator= matches.begin();
matchIterator!= matches.end(); ++matchIterator)
{
// if 2 NN has been identified
if (matchIterator->size() > 1)
{
// check distance ratio
if ((*matchIterator)[0].distance/(*matchIterator)[1].distance > ratio)
{
matchIterator->clear(); // remove match
removed++;
}
} else { // does not have 2 neighbours
matchIterator->clear(); // remove match
removed++;
}
}
return removed;
}
// Insert symmetrical matches in symMatches vector
void symmetryTest (const vector<vector<cv::DMatch> > & matches1,
const vector<vector<cv::DMatch> > & matches2,
vector<cv::DMatch>& symMatches)
{
// for all matches image 1 -> image 2
for (vector<vector<cv::DMatch> > ::const_iterator matchIterator1= matches1.begin(); matchIterator1!= matches1.end(); ++matchIterator1)
{
if (matchIterator1->size() < 2) // ignore deleted matches
continue;
// for all matches image 2 -> image 1
for (vector<vector<cv::DMatch> > ::const_iterator matchIterator2= matches2.begin(); matchIterator2!= matches2.end(); ++matchIterator2)
{
if (matchIterator2->size() < 2) continue; // ignore deleted matches
// Match symmetry test
if ((*matchIterator1)[0].queryIdx == (*matchIterator2)[0].trainIdx && (*matchIterator2)[0].queryIdx == (*matchIterator1)[0].trainIdx)
{
// add symmetrical match
symMatches.push_back (cv::DMatch((*matchIterator1)[0].queryIdx, (*matchIterator1)[0].trainIdx, (*matchIterator1)[0].distance));
break; // next match in image 1 -> image 2
}
}
}
}
// Identify good matches using RANSAC
// Return f matrix
cv::Mat ransacTest (const vector<cv::DMatch>& matches,
const vector<cv::KeyPoint>& keypoints1,
const vector<cv::KeyPoint>& keypoints2,
vector<cv::DMatch>& outMatches)
{
// Convert keypoints into Point2f
vector<cv::Point2f> points1, points2;
for (vector<cv::DMatch>::const_iterator it = matches.begin(); it != matches.end(); ++it)
{
// Get the position of left keypoints
float x= keypoints1[it->queryIdx].pt.x;
float y= keypoints1[it->queryIdx].pt.y;
points1.push_back(cv::Point2f(x,y));
// Get the position of right keypoints
x= keypoints2[it->trainIdx].pt.x;
y= keypoints2[it->trainIdx].pt.y;
points2.push_back(cv::Point2f(x,y));
}
// Compute F matrix using RANSAC
vector<uchar> inliers (points1.size(), 0);
cv::Mat f= cv::findFundamentalMat (cv::Mat (points1),cv::Mat (points2), // matching points
inliers, // match status (inlier ou outlier)
CV_FM_RANSAC, // RANSAC method
distance, // distance to epipolar line
confidence); // confidence probability
// extract the surviving (inliers) matches
vector<uchar>::const_iterator itIn= inliers.begin();
vector<cv::DMatch>::const_iterator itM= matches.begin();
// for all matches
for ( ;itIn!= inliers.end(); ++itIn, ++itM)
{
if (*itIn) // it is a valid match
{
outMatches.push_back(*itM);
}
}
cout << "Number of matched points (after cleaning): " << outMatches.size() << endl;
if (refineF)
{
// The F matrix will be recomputed with all accepted matches
// Convert keypoints into Point2f for final F computation
points1.clear();
points2.clear();
for (vector<cv::DMatch>::const_iterator it= outMatches.begin(); it!= outMatches.end(); ++it)
{
// Get the position of left keypoints
float x= keypoints1[it->queryIdx].pt.x;
float y= keypoints1[it->queryIdx].pt.y;
points1.push_back(cv::Point2f (x,y));
// Get the position of right keypoints
x= keypoints2[it->trainIdx].pt.x;
y= keypoints2[it->trainIdx].pt.y;
points2.push_back(cv::Point2f (x,y));
}
// Compute 8-point F from all accepted matches
f= cv::findFundamentalMat (cv::Mat (points1),cv::Mat (points2), // matching points
CV_FM_8POINT); // 8-point method
}
return f;
}
void detectFeatures (cv::Mat& image1, cv::Mat& image2, // input images
vector<cv::KeyPoint>& keypoints1, vector<cv::KeyPoint>& keypoints2)
{
// 1a. Detection of the SURF features
detector->detect (image1, keypoints1);
detector->detect (image2, keypoints2);
_image_features_1 = keypoints1.size ();
_image_features_2 = keypoints2.size ();
}
// Match feature points using symmetry test and RANSAC
// returns fundamental matrix
cv::Mat match (cv::Mat& image1, cv::Mat& image2, // input images
vector<cv::DMatch>& matches, // output matches and keypoints
vector<cv::KeyPoint>& keypoints1, vector<cv::KeyPoint>& keypoints2)
{
// 1. Extraction of SURF descriptors
cv::Mat descriptors1, descriptors2;
extractor->compute (image1, keypoints1, descriptors1);
extractor->compute (image2, keypoints2, descriptors2);
cv::gpu::GpuMat keypoints1_GPU, descriptors1_GPU;
cv::gpu::GpuMat keypoints2_GPU, descriptors2_GPU;
descriptors1_GPU.upload (descriptors1);
descriptors2_GPU.upload (descriptors2);
/*
//cv::GpuMat image(Size(1000, 500), CV_8UC3); // GPU memory allocations are very expensive !
cv::gpu::GpuMat img1_gpu, img2_gpu;
cv::gpu::GpuMat keypoints1_GPU, descriptors1_GPU;
cv::gpu::GpuMat keypoints2_GPU, descriptors2_GPU;
//vector<float> descriptors1_CPU, descriptors2_CPU;
surf.keypointsRatio = 0.1f;
surf.extended = false;
surf.hessianThreshold = 100;
//surf.nOctaveLayers = 2;
//surf.nOctaves = 4;
surf.releaseMemory();
surf.uploadKeypoints (keypoints1, keypoints1_GPU);
img1_gpu.upload(image1);
surf (img1_gpu, cv::gpu::GpuMat(), keypoints1_GPU, descriptors1_GPU, false);
surf.downloadKeypoints(keypoints1_GPU, keypoints1);
//surf.downloadDescriptors(descriptors1_GPU, descriptors1_CPU);
cout << "GPU descriptor matrix size: " << descriptors1_GPU.rows << " by " << descriptors1_GPU.cols << endl;
img1_gpu.release();
surf.releaseMemory();
surf.uploadKeypoints (keypoints2, keypoints2_GPU);
img2_gpu.upload(image2);
surf (img2_gpu, cv::gpu::GpuMat(), keypoints2_GPU, descriptors2_GPU, false);
surf.downloadKeypoints(keypoints1_GPU, keypoints2);
//surf.downloadDescriptors(descriptors2_GPU, descriptors2_CPU);
cout << "GPU descriptor matrix size: " << descriptors2_GPU.rows << " by " << descriptors2_GPU.cols << endl;
surf.releaseMemory();
img2_gpu.release();
*/
// 2. Match the two image descriptors
cv::gpu::GpuMat trainIdx,distance,allDist;
// Construction of the matcher
//cv::gpu::BruteForceMatcher_GPU_base <cv::L2<float> > matcher;
cv::gpu::BruteForceMatcher_GPU<cv::L2<float> > matcher;
matcher.clear();
// from image 1 to image 2
// based on k nearest neighbours (with k=2)
vector<vector<cv::DMatch> > matches1;
// matcher.knnMatch (descriptors1_GPU, descriptors2_GPU, matches1, 2);
matcher.knnMatchSingle (descriptors1_GPU, descriptors2_GPU,trainIdx,distance,allDist,2);
matcher.knnMatchDownload (trainIdx,distance,matches1);
matcher.clear();
// from image 2 to image 1
// based on k nearest neighbours (with k=2)
vector<vector<cv::DMatch> > matches2;
// matcher.knnMatch (descriptors2_GPU, descriptors1_GPU, matches2, 2);
matcher.knnMatchSingle (descriptors2_GPU, descriptors1_GPU,trainIdx,distance,allDist,2);
matcher.knnMatchDownload (trainIdx,distance,matches2);
matcher.clear();
// garbage collection
descriptors1_GPU.release();
descriptors2_GPU.release();
keypoints1_GPU.release();
keypoints2_GPU.release();
/*
cv::Mat descriptors1, descriptors2;
descriptors1_GPU.download (descriptors1);
descriptors2_GPU.download (descriptors2);
cv::BruteForceMatcher<cv::L2<float> > matcher;
// from image 1 to image 2
// based on k nearest neighbours (with k=2)
vector<vector<cv::DMatch> > matches1;
matcher.knnMatch (descriptors1, descriptors2,
matches1, // vector of matches (up to 2 per entry)
2); // return 2 nearest neighbours
// from image 2 to image 1
// based on k nearest neighbours (with k=2)
vector<vector<cv::DMatch> > matches2;
matcher.knnMatch (descriptors2, descriptors1,
matches2, // vector of matches (up to 2 per entry)
2); // return 2 nearest neighbours
*/
cout << "Number of matched points 1->2: " << matches1.size() << endl;
cout << "Number of matched points 2->1: " << matches2.size() << endl;
// 3. Remove matches for which NN ratio is > than threshold
// clean image 1 -> image 2 matches
int removed= ratioTest (matches1);
cout << "Number of matched points 1->2 (ratio test) : " << matches1.size()-removed << endl;
// clean image 2 -> image 1 matches
removed= ratioTest (matches2);
cout << "Number of matched points 1->2 (ratio test) : " << matches2.size()-removed << endl;
// 4. Remove non-symmetrical matches
vector<cv::DMatch> symMatches;
symmetryTest(matches1,matches2,symMatches);
cout << "Number of matched points (symmetry test): " << symMatches.size() << endl;
// 5. Validate matches using RANSAC
cv::Mat f = ransacTest(symMatches, keypoints1, keypoints2, matches);
// return the found fundamental matrix
return f;
}
// getters
int getNumberFeaturesImage1 () {return _image_features_1;}
int getNumberFeaturesImage2 () {return _image_features_2;}
};
#endif // MATCHERGPU_H