1 | // This file is part of OpenCV project. |
---|---|
2 | // It is subject to the license terms in the LICENSE file found in the top-level directory |
3 | // of this distribution and at http://opencv.org/license.html. |
4 | // |
5 | // Copyright (c) 2006-2010, Rob Hess <hess@eecs.oregonstate.edu> |
6 | // Copyright (C) 2009, Willow Garage Inc., all rights reserved. |
7 | // Copyright (C) 2020, Intel Corporation, all rights reserved. |
8 | |
9 | /**********************************************************************************************\ |
10 | Implementation of SIFT is based on the code from http://blogs.oregonstate.edu/hess/code/sift/ |
11 | Below is the original copyright. |
12 | Patent US6711293 expired in March 2020. |
13 | |
14 | // Copyright (c) 2006-2010, Rob Hess <hess@eecs.oregonstate.edu> |
15 | // All rights reserved. |
16 | |
17 | // The following patent has been issued for methods embodied in this |
18 | // software: "Method and apparatus for identifying scale invariant features |
19 | // in an image and use of same for locating an object in an image," David |
20 | // G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application |
21 | // filed March 8, 1999. Asignee: The University of British Columbia. For |
22 | // further details, contact David Lowe (lowe@cs.ubc.ca) or the |
23 | // University-Industry Liaison Office of the University of British |
24 | // Columbia. |
25 | |
26 | // Note that restrictions imposed by this patent (and possibly others) |
27 | // exist independently of and may be in conflict with the freedoms granted |
28 | // in this license, which refers to copyright of the program, not patents |
29 | // for any methods that it implements. Both copyright and patent law must |
30 | // be obeyed to legally use and redistribute this program and it is not the |
31 | // purpose of this license to induce you to infringe any patents or other |
32 | // property right claims or to contest validity of any such claims. If you |
33 | // redistribute or use the program, then this license merely protects you |
34 | // from committing copyright infringement. It does not protect you from |
35 | // committing patent infringement. So, before you do anything with this |
36 | // program, make sure that you have permission to do so not merely in terms |
37 | // of copyright, but also in terms of patent law. |
38 | |
39 | // Please note that this license is not to be understood as a guarantee |
40 | // either. If you use the program according to this license, but in |
41 | // conflict with patent law, it does not mean that the licensor will refund |
42 | // you for any losses that you incur if you are sued for your patent |
43 | // infringement. |
44 | |
45 | // Redistribution and use in source and binary forms, with or without |
46 | // modification, are permitted provided that the following conditions are |
47 | // met: |
48 | // * Redistributions of source code must retain the above copyright and |
49 | // patent notices, this list of conditions and the following |
50 | // disclaimer. |
51 | // * Redistributions in binary form must reproduce the above copyright |
52 | // notice, this list of conditions and the following disclaimer in |
53 | // the documentation and/or other materials provided with the |
54 | // distribution. |
55 | // * Neither the name of Oregon State University nor the names of its |
56 | // contributors may be used to endorse or promote products derived |
57 | // from this software without specific prior written permission. |
58 | |
59 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
60 | // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
61 | // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
62 | // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
63 | // HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
64 | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
65 | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
66 | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
67 | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
68 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
69 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
70 | \**********************************************************************************************/ |
71 | |
72 | #include "precomp.hpp" |
73 | #include <opencv2/core/hal/hal.hpp> |
74 | #include <opencv2/core/utils/tls.hpp> |
75 | #include <opencv2/core/utils/logger.hpp> |
76 | |
77 | #include "sift.simd.hpp" |
78 | #include "sift.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content |
79 | |
80 | namespace cv { |
81 | |
82 | /*! |
83 | SIFT implementation. |
84 | |
85 | The class implements SIFT algorithm by D. Lowe. |
86 | */ |
87 | class SIFT_Impl : public SIFT |
88 | { |
89 | public: |
90 | explicit SIFT_Impl( int nfeatures = 0, int nOctaveLayers = 3, |
91 | double contrastThreshold = 0.04, double edgeThreshold = 10, |
92 | double sigma = 1.6, int descriptorType = CV_32F, |
93 | bool enable_precise_upscale = true ); |
94 | |
95 | //! returns the descriptor size in floats (128) |
96 | int descriptorSize() const CV_OVERRIDE; |
97 | |
98 | //! returns the descriptor type |
99 | int descriptorType() const CV_OVERRIDE; |
100 | |
101 | //! returns the default norm type |
102 | int defaultNorm() const CV_OVERRIDE; |
103 | |
104 | //! finds the keypoints and computes descriptors for them using SIFT algorithm. |
105 | //! Optionally it can compute descriptors for the user-provided keypoints |
106 | void detectAndCompute(InputArray img, InputArray mask, |
107 | std::vector<KeyPoint>& keypoints, |
108 | OutputArray descriptors, |
109 | bool useProvidedKeypoints = false) CV_OVERRIDE; |
110 | |
111 | void buildGaussianPyramid( const Mat& base, std::vector<Mat>& pyr, int nOctaves ) const; |
112 | void buildDoGPyramid( const std::vector<Mat>& pyr, std::vector<Mat>& dogpyr ) const; |
113 | void findScaleSpaceExtrema( const std::vector<Mat>& gauss_pyr, const std::vector<Mat>& dog_pyr, |
114 | std::vector<KeyPoint>& keypoints ) const; |
115 | |
116 | void read( const FileNode& fn) CV_OVERRIDE; |
117 | void write( FileStorage& fs) const CV_OVERRIDE; |
118 | |
119 | void setNFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; } |
120 | int getNFeatures() const CV_OVERRIDE { return nfeatures; } |
121 | |
122 | void setNOctaveLayers(int nOctaveLayers_) CV_OVERRIDE { nOctaveLayers = nOctaveLayers_; } |
123 | int getNOctaveLayers() const CV_OVERRIDE { return nOctaveLayers; } |
124 | |
125 | void setContrastThreshold(double contrastThreshold_) CV_OVERRIDE { contrastThreshold = contrastThreshold_; } |
126 | double getContrastThreshold() const CV_OVERRIDE { return contrastThreshold; } |
127 | |
128 | void setEdgeThreshold(double edgeThreshold_) CV_OVERRIDE { edgeThreshold = edgeThreshold_; } |
129 | double getEdgeThreshold() const CV_OVERRIDE { return edgeThreshold; } |
130 | |
131 | void setSigma(double sigma_) CV_OVERRIDE { sigma = sigma_; } |
132 | double getSigma() const CV_OVERRIDE { return sigma; } |
133 | |
134 | protected: |
135 | CV_PROP_RW int nfeatures; |
136 | CV_PROP_RW int nOctaveLayers; |
137 | CV_PROP_RW double contrastThreshold; |
138 | CV_PROP_RW double edgeThreshold; |
139 | CV_PROP_RW double sigma; |
140 | CV_PROP_RW int descriptor_type; |
141 | CV_PROP_RW bool enable_precise_upscale; |
142 | }; |
143 | |
144 | Ptr<SIFT> SIFT::create( int _nfeatures, int _nOctaveLayers, |
145 | double _contrastThreshold, double _edgeThreshold, double _sigma, bool enable_precise_upscale ) |
146 | { |
147 | CV_TRACE_FUNCTION(); |
148 | |
149 | return makePtr<SIFT_Impl>(a1: _nfeatures, a1: _nOctaveLayers, a1: _contrastThreshold, a1: _edgeThreshold, a1: _sigma, CV_32F, a1: enable_precise_upscale); |
150 | } |
151 | |
152 | Ptr<SIFT> SIFT::create( int _nfeatures, int _nOctaveLayers, |
153 | double _contrastThreshold, double _edgeThreshold, double _sigma, int _descriptorType, bool enable_precise_upscale ) |
154 | { |
155 | CV_TRACE_FUNCTION(); |
156 | |
157 | // SIFT descriptor supports 32bit floating point and 8bit unsigned int. |
158 | CV_Assert(_descriptorType == CV_32F || _descriptorType == CV_8U); |
159 | return makePtr<SIFT_Impl>(a1: _nfeatures, a1: _nOctaveLayers, a1: _contrastThreshold, a1: _edgeThreshold, a1: _sigma, a1: _descriptorType, a1: enable_precise_upscale); |
160 | } |
161 | |
162 | String SIFT::getDefaultName() const |
163 | { |
164 | return (Feature2D::getDefaultName() + ".SIFT"); |
165 | } |
166 | |
167 | static inline void |
168 | unpackOctave(const KeyPoint& kpt, int& octave, int& layer, float& scale) |
169 | { |
170 | octave = kpt.octave & 255; |
171 | layer = (kpt.octave >> 8) & 255; |
172 | octave = octave < 128 ? octave : (-128 | octave); |
173 | scale = octave >= 0 ? 1.f/(1 << octave) : (float)(1 << -octave); |
174 | } |
175 | |
176 | static Mat createInitialImage( const Mat& img, bool doubleImageSize, float sigma, bool enable_precise_upscale ) |
177 | { |
178 | CV_TRACE_FUNCTION(); |
179 | |
180 | Mat gray, gray_fpt; |
181 | if( img.channels() == 3 || img.channels() == 4 ) |
182 | { |
183 | cvtColor(src: img, dst: gray, code: COLOR_BGR2GRAY); |
184 | gray.convertTo(m: gray_fpt, rtype: DataType<sift_wt>::type, alpha: SIFT_FIXPT_SCALE, beta: 0); |
185 | } |
186 | else |
187 | img.convertTo(m: gray_fpt, rtype: DataType<sift_wt>::type, alpha: SIFT_FIXPT_SCALE, beta: 0); |
188 | |
189 | float sig_diff; |
190 | |
191 | if( doubleImageSize ) |
192 | { |
193 | sig_diff = sqrtf( x: std::max(a: sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4, b: 0.01f) ); |
194 | |
195 | Mat dbl; |
196 | if (enable_precise_upscale) { |
197 | dbl.create(size: Size(gray_fpt.cols*2, gray_fpt.rows*2), type: gray_fpt.type()); |
198 | Mat H = Mat::zeros(rows: 2, cols: 3, CV_32F); |
199 | H.at<float>(i0: 0, i1: 0) = 0.5f; |
200 | H.at<float>(i0: 1, i1: 1) = 0.5f; |
201 | |
202 | cv::warpAffine(src: gray_fpt, dst: dbl, M: H, dsize: dbl.size(), flags: INTER_LINEAR | WARP_INVERSE_MAP, borderMode: BORDER_REFLECT); |
203 | } else { |
204 | #if DoG_TYPE_SHORT |
205 | resize(gray_fpt, dbl, Size(gray_fpt.cols*2, gray_fpt.rows*2), 0, 0, INTER_LINEAR_EXACT); |
206 | #else |
207 | resize(src: gray_fpt, dst: dbl, dsize: Size(gray_fpt.cols*2, gray_fpt.rows*2), fx: 0, fy: 0, interpolation: INTER_LINEAR); |
208 | #endif |
209 | } |
210 | Mat result; |
211 | GaussianBlur(src: dbl, dst: result, ksize: Size(), sigmaX: sig_diff, sigmaY: sig_diff); |
212 | return result; |
213 | } |
214 | else |
215 | { |
216 | sig_diff = sqrtf( x: std::max(a: sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA, b: 0.01f) ); |
217 | Mat result; |
218 | GaussianBlur(src: gray_fpt, dst: result, ksize: Size(), sigmaX: sig_diff, sigmaY: sig_diff); |
219 | return result; |
220 | } |
221 | } |
222 | |
223 | |
224 | void SIFT_Impl::buildGaussianPyramid( const Mat& base, std::vector<Mat>& pyr, int nOctaves ) const |
225 | { |
226 | CV_TRACE_FUNCTION(); |
227 | |
228 | std::vector<double> sig(nOctaveLayers + 3); |
229 | pyr.resize(new_size: nOctaves*(nOctaveLayers + 3)); |
230 | |
231 | // precompute Gaussian sigmas using the following formula: |
232 | // \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2 |
233 | sig[0] = sigma; |
234 | double k = std::pow( x: 2., y: 1. / nOctaveLayers ); |
235 | for( int i = 1; i < nOctaveLayers + 3; i++ ) |
236 | { |
237 | double sig_prev = std::pow(x: k, y: (double)(i-1))*sigma; |
238 | double sig_total = sig_prev*k; |
239 | sig[i] = std::sqrt(x: sig_total*sig_total - sig_prev*sig_prev); |
240 | } |
241 | |
242 | for( int o = 0; o < nOctaves; o++ ) |
243 | { |
244 | for( int i = 0; i < nOctaveLayers + 3; i++ ) |
245 | { |
246 | Mat& dst = pyr[o*(nOctaveLayers + 3) + i]; |
247 | if( o == 0 && i == 0 ) |
248 | dst = base; |
249 | // base of new octave is halved image from end of previous octave |
250 | else if( i == 0 ) |
251 | { |
252 | const Mat& src = pyr[(o-1)*(nOctaveLayers + 3) + nOctaveLayers]; |
253 | resize(src, dst, dsize: Size(src.cols/2, src.rows/2), |
254 | fx: 0, fy: 0, interpolation: INTER_NEAREST); |
255 | } |
256 | else |
257 | { |
258 | const Mat& src = pyr[o*(nOctaveLayers + 3) + i-1]; |
259 | GaussianBlur(src, dst, ksize: Size(), sigmaX: sig[i], sigmaY: sig[i]); |
260 | } |
261 | } |
262 | } |
263 | } |
264 | |
265 | |
266 | class buildDoGPyramidComputer : public ParallelLoopBody |
267 | { |
268 | public: |
269 | buildDoGPyramidComputer( |
270 | int _nOctaveLayers, |
271 | const std::vector<Mat>& _gpyr, |
272 | std::vector<Mat>& _dogpyr) |
273 | : nOctaveLayers(_nOctaveLayers), |
274 | gpyr(_gpyr), |
275 | dogpyr(_dogpyr) { } |
276 | |
277 | void operator()( const cv::Range& range ) const CV_OVERRIDE |
278 | { |
279 | CV_TRACE_FUNCTION(); |
280 | |
281 | const int begin = range.start; |
282 | const int end = range.end; |
283 | |
284 | for( int a = begin; a < end; a++ ) |
285 | { |
286 | const int o = a / (nOctaveLayers + 2); |
287 | const int i = a % (nOctaveLayers + 2); |
288 | |
289 | const Mat& src1 = gpyr[o*(nOctaveLayers + 3) + i]; |
290 | const Mat& src2 = gpyr[o*(nOctaveLayers + 3) + i + 1]; |
291 | Mat& dst = dogpyr[o*(nOctaveLayers + 2) + i]; |
292 | subtract(src1: src2, src2: src1, dst, mask: noArray(), dtype: DataType<sift_wt>::type); |
293 | } |
294 | } |
295 | |
296 | private: |
297 | int nOctaveLayers; |
298 | const std::vector<Mat>& gpyr; |
299 | std::vector<Mat>& dogpyr; |
300 | }; |
301 | |
302 | void SIFT_Impl::buildDoGPyramid( const std::vector<Mat>& gpyr, std::vector<Mat>& dogpyr ) const |
303 | { |
304 | CV_TRACE_FUNCTION(); |
305 | |
306 | int nOctaves = (int)gpyr.size()/(nOctaveLayers + 3); |
307 | dogpyr.resize( new_size: nOctaves*(nOctaveLayers + 2) ); |
308 | |
309 | parallel_for_(range: Range(0, nOctaves * (nOctaveLayers + 2)), body: buildDoGPyramidComputer(nOctaveLayers, gpyr, dogpyr)); |
310 | } |
311 | |
312 | class findScaleSpaceExtremaComputer : public ParallelLoopBody |
313 | { |
314 | public: |
315 | findScaleSpaceExtremaComputer( |
316 | int _o, |
317 | int _i, |
318 | int _threshold, |
319 | int _idx, |
320 | int _step, |
321 | int _cols, |
322 | int _nOctaveLayers, |
323 | double _contrastThreshold, |
324 | double _edgeThreshold, |
325 | double _sigma, |
326 | const std::vector<Mat>& _gauss_pyr, |
327 | const std::vector<Mat>& _dog_pyr, |
328 | TLSData<std::vector<KeyPoint> > &_tls_kpts_struct) |
329 | |
330 | : o(_o), |
331 | i(_i), |
332 | threshold(_threshold), |
333 | idx(_idx), |
334 | step(_step), |
335 | cols(_cols), |
336 | nOctaveLayers(_nOctaveLayers), |
337 | contrastThreshold(_contrastThreshold), |
338 | edgeThreshold(_edgeThreshold), |
339 | sigma(_sigma), |
340 | gauss_pyr(_gauss_pyr), |
341 | dog_pyr(_dog_pyr), |
342 | tls_kpts_struct(_tls_kpts_struct) { } |
343 | void operator()( const cv::Range& range ) const CV_OVERRIDE |
344 | { |
345 | CV_TRACE_FUNCTION(); |
346 | |
347 | std::vector<KeyPoint>& kpts = tls_kpts_struct.getRef(); |
348 | |
349 | CV_CPU_DISPATCH(findScaleSpaceExtrema, (o, i, threshold, idx, step, cols, nOctaveLayers, contrastThreshold, edgeThreshold, sigma, gauss_pyr, dog_pyr, kpts, range), |
350 | CV_CPU_DISPATCH_MODES_ALL); |
351 | } |
352 | private: |
353 | int o, i; |
354 | int threshold; |
355 | int idx, step, cols; |
356 | int nOctaveLayers; |
357 | double contrastThreshold; |
358 | double edgeThreshold; |
359 | double sigma; |
360 | const std::vector<Mat>& gauss_pyr; |
361 | const std::vector<Mat>& dog_pyr; |
362 | TLSData<std::vector<KeyPoint> > &tls_kpts_struct; |
363 | }; |
364 | |
365 | // |
366 | // Detects features at extrema in DoG scale space. Bad features are discarded |
367 | // based on contrast and ratio of principal curvatures. |
368 | void SIFT_Impl::findScaleSpaceExtrema( const std::vector<Mat>& gauss_pyr, const std::vector<Mat>& dog_pyr, |
369 | std::vector<KeyPoint>& keypoints ) const |
370 | { |
371 | CV_TRACE_FUNCTION(); |
372 | |
373 | const int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + 3); |
374 | const int threshold = cvFloor(value: 0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE); |
375 | |
376 | keypoints.clear(); |
377 | TLSDataAccumulator<std::vector<KeyPoint> > tls_kpts_struct; |
378 | |
379 | for( int o = 0; o < nOctaves; o++ ) |
380 | for( int i = 1; i <= nOctaveLayers; i++ ) |
381 | { |
382 | const int idx = o*(nOctaveLayers+2)+i; |
383 | const Mat& img = dog_pyr[idx]; |
384 | const int step = (int)img.step1(); |
385 | const int rows = img.rows, cols = img.cols; |
386 | |
387 | parallel_for_(range: Range(SIFT_IMG_BORDER, rows-SIFT_IMG_BORDER), |
388 | body: findScaleSpaceExtremaComputer( |
389 | o, i, threshold, idx, step, cols, |
390 | nOctaveLayers, |
391 | contrastThreshold, |
392 | edgeThreshold, |
393 | sigma, |
394 | gauss_pyr, dog_pyr, tls_kpts_struct)); |
395 | } |
396 | |
397 | std::vector<std::vector<KeyPoint>*> kpt_vecs; |
398 | tls_kpts_struct.gather(data&: kpt_vecs); |
399 | for (size_t i = 0; i < kpt_vecs.size(); ++i) { |
400 | keypoints.insert(position: keypoints.end(), first: kpt_vecs[i]->begin(), last: kpt_vecs[i]->end()); |
401 | } |
402 | } |
403 | |
404 | |
405 | static |
406 | void calcSIFTDescriptor( |
407 | const Mat& img, Point2f ptf, float ori, float scl, |
408 | int d, int n, Mat& dst, int row |
409 | ) |
410 | { |
411 | CV_TRACE_FUNCTION(); |
412 | |
413 | CV_CPU_DISPATCH(calcSIFTDescriptor, (img, ptf, ori, scl, d, n, dst, row), |
414 | CV_CPU_DISPATCH_MODES_ALL); |
415 | } |
416 | |
417 | class calcDescriptorsComputer : public ParallelLoopBody |
418 | { |
419 | public: |
420 | calcDescriptorsComputer(const std::vector<Mat>& _gpyr, |
421 | const std::vector<KeyPoint>& _keypoints, |
422 | Mat& _descriptors, |
423 | int _nOctaveLayers, |
424 | int _firstOctave) |
425 | : gpyr(_gpyr), |
426 | keypoints(_keypoints), |
427 | descriptors(_descriptors), |
428 | nOctaveLayers(_nOctaveLayers), |
429 | firstOctave(_firstOctave) { } |
430 | |
431 | void operator()( const cv::Range& range ) const CV_OVERRIDE |
432 | { |
433 | CV_TRACE_FUNCTION(); |
434 | |
435 | const int begin = range.start; |
436 | const int end = range.end; |
437 | |
438 | static const int d = SIFT_DESCR_WIDTH, n = SIFT_DESCR_HIST_BINS; |
439 | |
440 | for ( int i = begin; i<end; i++ ) |
441 | { |
442 | KeyPoint kpt = keypoints[i]; |
443 | int octave, layer; |
444 | float scale; |
445 | unpackOctave(kpt, octave, layer, scale); |
446 | CV_Assert(octave >= firstOctave && layer <= nOctaveLayers+2); |
447 | float size=kpt.size*scale; |
448 | Point2f ptf(kpt.pt.x*scale, kpt.pt.y*scale); |
449 | const Mat& img = gpyr[(octave - firstOctave)*(nOctaveLayers + 3) + layer]; |
450 | |
451 | float angle = 360.f - kpt.angle; |
452 | if(std::abs(x: angle - 360.f) < FLT_EPSILON) |
453 | angle = 0.f; |
454 | calcSIFTDescriptor(img, ptf, ori: angle, scl: size*0.5f, d, n, dst&: descriptors, row: i); |
455 | } |
456 | } |
457 | private: |
458 | const std::vector<Mat>& gpyr; |
459 | const std::vector<KeyPoint>& keypoints; |
460 | Mat& descriptors; |
461 | int nOctaveLayers; |
462 | int firstOctave; |
463 | }; |
464 | |
465 | static void calcDescriptors(const std::vector<Mat>& gpyr, const std::vector<KeyPoint>& keypoints, |
466 | Mat& descriptors, int nOctaveLayers, int firstOctave ) |
467 | { |
468 | CV_TRACE_FUNCTION(); |
469 | parallel_for_(range: Range(0, static_cast<int>(keypoints.size())), body: calcDescriptorsComputer(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave)); |
470 | } |
471 | |
472 | ////////////////////////////////////////////////////////////////////////////////////////// |
473 | |
474 | SIFT_Impl::SIFT_Impl( int _nfeatures, int _nOctaveLayers, |
475 | double _contrastThreshold, double _edgeThreshold, double _sigma, int _descriptorType, bool _enable_precise_upscale) |
476 | : nfeatures(_nfeatures), nOctaveLayers(_nOctaveLayers), |
477 | contrastThreshold(_contrastThreshold), edgeThreshold(_edgeThreshold), sigma(_sigma), descriptor_type(_descriptorType), |
478 | enable_precise_upscale(_enable_precise_upscale) |
479 | { |
480 | if (!enable_precise_upscale) { |
481 | CV_LOG_ONCE_INFO(NULL, "precise upscale disabled, this is now deprecated as it was found to induce a location bias"); |
482 | } |
483 | } |
484 | |
485 | int SIFT_Impl::descriptorSize() const |
486 | { |
487 | return SIFT_DESCR_WIDTH*SIFT_DESCR_WIDTH*SIFT_DESCR_HIST_BINS; |
488 | } |
489 | |
490 | int SIFT_Impl::descriptorType() const |
491 | { |
492 | return descriptor_type; |
493 | } |
494 | |
495 | int SIFT_Impl::defaultNorm() const |
496 | { |
497 | return NORM_L2; |
498 | } |
499 | |
500 | |
501 | void SIFT_Impl::detectAndCompute(InputArray _image, InputArray _mask, |
502 | std::vector<KeyPoint>& keypoints, |
503 | OutputArray _descriptors, |
504 | bool useProvidedKeypoints) |
505 | { |
506 | CV_TRACE_FUNCTION(); |
507 | |
508 | int firstOctave = -1, actualNOctaves = 0, actualNLayers = 0; |
509 | Mat image = _image.getMat(), mask = _mask.getMat(); |
510 | |
511 | if( image.empty() || image.depth() != CV_8U ) |
512 | CV_Error( Error::StsBadArg, "image is empty or has incorrect depth (!=CV_8U)"); |
513 | |
514 | if( !mask.empty() && mask.type() != CV_8UC1 ) |
515 | CV_Error( Error::StsBadArg, "mask has incorrect type (!=CV_8UC1)"); |
516 | |
517 | if( useProvidedKeypoints ) |
518 | { |
519 | firstOctave = 0; |
520 | int maxOctave = INT_MIN; |
521 | for( size_t i = 0; i < keypoints.size(); i++ ) |
522 | { |
523 | int octave, layer; |
524 | float scale; |
525 | unpackOctave(kpt: keypoints[i], octave, layer, scale); |
526 | firstOctave = std::min(a: firstOctave, b: octave); |
527 | maxOctave = std::max(a: maxOctave, b: octave); |
528 | actualNLayers = std::max(a: actualNLayers, b: layer-2); |
529 | } |
530 | |
531 | firstOctave = std::min(a: firstOctave, b: 0); |
532 | CV_Assert( firstOctave >= -1 && actualNLayers <= nOctaveLayers ); |
533 | actualNOctaves = maxOctave - firstOctave + 1; |
534 | } |
535 | |
536 | Mat base = createInitialImage(img: image, doubleImageSize: firstOctave < 0, sigma: (float)sigma, enable_precise_upscale); |
537 | std::vector<Mat> gpyr; |
538 | int nOctaves = actualNOctaves > 0 ? actualNOctaves : cvRound(value: std::log( x: (double)std::min( a: base.cols, b: base.rows ) ) / std::log(x: 2.) - 2) - firstOctave; |
539 | |
540 | //double t, tf = getTickFrequency(); |
541 | //t = (double)getTickCount(); |
542 | buildGaussianPyramid(base, pyr&: gpyr, nOctaves); |
543 | |
544 | //t = (double)getTickCount() - t; |
545 | //printf("pyramid construction time: %g\n", t*1000./tf); |
546 | |
547 | if( !useProvidedKeypoints ) |
548 | { |
549 | std::vector<Mat> dogpyr; |
550 | buildDoGPyramid(gpyr, dogpyr); |
551 | //t = (double)getTickCount(); |
552 | findScaleSpaceExtrema(gauss_pyr: gpyr, dog_pyr: dogpyr, keypoints); |
553 | KeyPointsFilter::removeDuplicatedSorted( keypoints ); |
554 | |
555 | if( nfeatures > 0 ) |
556 | KeyPointsFilter::retainBest(keypoints, npoints: nfeatures); |
557 | //t = (double)getTickCount() - t; |
558 | //printf("keypoint detection time: %g\n", t*1000./tf); |
559 | |
560 | if( firstOctave < 0 ) |
561 | for( size_t i = 0; i < keypoints.size(); i++ ) |
562 | { |
563 | KeyPoint& kpt = keypoints[i]; |
564 | float scale = 1.f/(float)(1 << -firstOctave); |
565 | kpt.octave = (kpt.octave & ~255) | ((kpt.octave + firstOctave) & 255); |
566 | kpt.pt *= scale; |
567 | kpt.size *= scale; |
568 | } |
569 | |
570 | if( !mask.empty() ) |
571 | KeyPointsFilter::runByPixelsMask( keypoints, mask ); |
572 | } |
573 | else |
574 | { |
575 | // filter keypoints by mask |
576 | //KeyPointsFilter::runByPixelsMask( keypoints, mask ); |
577 | } |
578 | |
579 | if( _descriptors.needed() ) |
580 | { |
581 | //t = (double)getTickCount(); |
582 | int dsize = descriptorSize(); |
583 | _descriptors.create(rows: (int)keypoints.size(), cols: dsize, type: descriptor_type); |
584 | |
585 | Mat descriptors = _descriptors.getMat(); |
586 | calcDescriptors(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave); |
587 | //t = (double)getTickCount() - t; |
588 | //printf("descriptor extraction time: %g\n", t*1000./tf); |
589 | } |
590 | } |
591 | |
592 | void SIFT_Impl::read( const FileNode& fn) |
593 | { |
594 | // if node is empty, keep previous value |
595 | if (!fn["nfeatures"].empty()) |
596 | fn["nfeatures"] >> nfeatures; |
597 | if (!fn["nOctaveLayers"].empty()) |
598 | fn["nOctaveLayers"] >> nOctaveLayers; |
599 | if (!fn["contrastThreshold"].empty()) |
600 | fn["contrastThreshold"] >> contrastThreshold; |
601 | if (!fn["edgeThreshold"].empty()) |
602 | fn["edgeThreshold"] >> edgeThreshold; |
603 | if (!fn["sigma"].empty()) |
604 | fn["sigma"] >> sigma; |
605 | if (!fn["descriptorType"].empty()) |
606 | fn["descriptorType"] >> descriptor_type; |
607 | } |
608 | void SIFT_Impl::write( FileStorage& fs) const |
609 | { |
610 | if(fs.isOpened()) |
611 | { |
612 | fs << "name"<< getDefaultName(); |
613 | fs << "nfeatures"<< nfeatures; |
614 | fs << "nOctaveLayers"<< nOctaveLayers; |
615 | fs << "contrastThreshold"<< contrastThreshold; |
616 | fs << "edgeThreshold"<< edgeThreshold; |
617 | fs << "sigma"<< sigma; |
618 | fs << "descriptorType"<< descriptor_type; |
619 | } |
620 | } |
621 | |
622 | } |
623 |
Definitions
- SIFT_Impl
- setNFeatures
- getNFeatures
- setNOctaveLayers
- getNOctaveLayers
- setContrastThreshold
- getContrastThreshold
- setEdgeThreshold
- getEdgeThreshold
- setSigma
- getSigma
- create
- create
- getDefaultName
- unpackOctave
- createInitialImage
- buildGaussianPyramid
- buildDoGPyramidComputer
- buildDoGPyramidComputer
- operator()
- buildDoGPyramid
- findScaleSpaceExtremaComputer
- findScaleSpaceExtremaComputer
- operator()
- findScaleSpaceExtrema
- calcSIFTDescriptor
- calcDescriptorsComputer
- calcDescriptorsComputer
- operator()
- calcDescriptors
- SIFT_Impl
- descriptorSize
- descriptorType
- defaultNorm
- detectAndCompute
- read
Improve your Profiling and Debugging skills
Find out more