blob: afab9b4f73a08f09fffe03e9233e6eebf430eaca [file] [log] [blame]
Brian Silvermanf1196122020-01-16 00:41:54 -08001// clang-format off
2/*M///////////////////////////////////////////////////////////////////////////////////////
3//
4// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
5//
6// By downloading, copying, installing or using the software you agree to this license.
7// If you do not agree to this license, do not download, install,
8// copy or use the software.
9//
10//
11// License Agreement
12// For Open Source Computer Vision Library
13//
14// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
15// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
16// Third party copyrights are property of their respective owners.
17//
18// Redistribution and use in source and binary forms, with or without modification,
19// are permitted provided that the following conditions are met:
20//
21// * Redistribution's of source code must retain the above copyright notice,
22// this list of conditions and the following disclaimer.
23//
24// * Redistribution's in binary form must reproduce the above copyright notice,
25// this list of conditions and the following disclaimer in the documentation
26// and/or other materials provided with the distribution.
27//
28// * The name of the copyright holders may not be used to endorse or promote products
29// derived from this software without specific prior written permission.
30//
31// This software is provided by the copyright holders and contributors "as is" and
32// any express or implied warranties, including, but not limited to, the implied
33// warranties of merchantability and fitness for a particular purpose are disclaimed.
34// In no event shall the Intel Corporation or contributors be liable for any direct,
35// indirect, incidental, special, exemplary, or consequential damages
36// (including, but not limited to, procurement of substitute goods or services;
37// loss of use, data, or profits; or business interruption) however caused
38// and on any theory of liability, whether in contract, strict liability,
39// or tort (including negligence or otherwise) arising in any way out of
40// the use of this software, even if advised of the possibility of such damage.
41//
42//M*/
43
44/**********************************************************************************************\
45 Implementation of SIFT is based on the code from http://blogs.oregonstate.edu/hess/code/sift/
46 Below is the original copyright.
47
48// Copyright (c) 2006-2010, Rob Hess <hess@eecs.oregonstate.edu>
49// All rights reserved.
50
51// The following patent has been issued for methods embodied in this
52// software: "Method and apparatus for identifying scale invariant features
53// in an image and use of same for locating an object in an image," David
54// G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application
55// filed March 8, 1999. Asignee: The University of British Columbia. For
56// further details, contact David Lowe (lowe@cs.ubc.ca) or the
57// University-Industry Liaison Office of the University of British
58// Columbia.
59
60// Note that restrictions imposed by this patent (and possibly others)
61// exist independently of and may be in conflict with the freedoms granted
62// in this license, which refers to copyright of the program, not patents
63// for any methods that it implements. Both copyright and patent law must
64// be obeyed to legally use and redistribute this program and it is not the
65// purpose of this license to induce you to infringe any patents or other
66// property right claims or to contest validity of any such claims. If you
67// redistribute or use the program, then this license merely protects you
68// from committing copyright infringement. It does not protect you from
69// committing patent infringement. So, before you do anything with this
70// program, make sure that you have permission to do so not merely in terms
71// of copyright, but also in terms of patent law.
72
73// Please note that this license is not to be understood as a guarantee
74// either. If you use the program according to this license, but in
75// conflict with patent law, it does not mean that the licensor will refund
76// you for any losses that you incur if you are sued for your patent
77// infringement.
78
79// Redistribution and use in source and binary forms, with or without
80// modification, are permitted provided that the following conditions are
81// met:
82// * Redistributions of source code must retain the above copyright and
83// patent notices, this list of conditions and the following
84// disclaimer.
85// * Redistributions in binary form must reproduce the above copyright
86// notice, this list of conditions and the following disclaimer in
87// the documentation and/or other materials provided with the
88// distribution.
89// * Neither the name of Oregon State University nor the names of its
90// contributors may be used to endorse or promote products derived
91// from this software without specific prior written permission.
92
93// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
94// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
95// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
96// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
97// HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
98// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
99// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
100// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
101// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
102// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
103// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
104\**********************************************************************************************/
105// clang-format on
106
107#include "y2020/vision/sift/sift971.h"
108
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700109#include <cstdarg>
Brian Silvermanf1196122020-01-16 00:41:54 -0800110#include <iostream>
111#include <mutex>
Philipp Schrader790cb542023-07-05 21:06:52 -0700112
113#include "glog/logging.h"
Brian Silvermanf1196122020-01-16 00:41:54 -0800114#include <opencv2/core/hal/hal.hpp>
115#include <opencv2/imgproc.hpp>
Brian Silverman3fec6482020-01-19 17:56:20 -0800116
117#include "y2020/vision/sift/fast_gaussian.h"
Brian Silvermanf1196122020-01-16 00:41:54 -0800118
119using namespace cv;
120
121namespace frc971 {
122namespace vision {
123namespace {
124
125#define USE_AVX2 0
126
127/******************************* Defs and macros *****************************/
128
129// default width of descriptor histogram array
130static const int SIFT_DESCR_WIDTH = 4;
131
132// default number of bins per histogram in descriptor array
133static const int SIFT_DESCR_HIST_BINS = 8;
134
135// assumed gaussian blur for input image
136static const float SIFT_INIT_SIGMA = 0.5f;
137
138// width of border in which to ignore keypoints
139static const int SIFT_IMG_BORDER = 5;
140
141// maximum steps of keypoint interpolation before failure
142static const int SIFT_MAX_INTERP_STEPS = 5;
143
144// default number of bins in histogram for orientation assignment
145static const int SIFT_ORI_HIST_BINS = 36;
146
147// determines gaussian sigma for orientation assignment
148static const float SIFT_ORI_SIG_FCTR = 1.5f;
149
150// determines the radius of the region used in orientation assignment
151static const float SIFT_ORI_RADIUS = 3 * SIFT_ORI_SIG_FCTR;
152
153// orientation magnitude relative to max that results in new feature
154static const float SIFT_ORI_PEAK_RATIO = 0.8f;
155
156// determines the size of a single descriptor orientation histogram
157static const float SIFT_DESCR_SCL_FCTR = 3.f;
158
159// threshold on magnitude of elements of descriptor vector
160static const float SIFT_DESCR_MAG_THR = 0.2f;
161
162// factor used to convert floating-point descriptor to unsigned char
163static const float SIFT_INT_DESCR_FCTR = 512.f;
164
Brian Silverman3fec6482020-01-19 17:56:20 -0800165#define DoG_TYPE_SHORT 1
Brian Silvermanf1196122020-01-16 00:41:54 -0800166#if DoG_TYPE_SHORT
167// intermediate type used for DoG pyramids
168typedef short sift_wt;
169static const int SIFT_FIXPT_SCALE = 48;
170#else
171// intermediate type used for DoG pyramids
172typedef float sift_wt;
173static const int SIFT_FIXPT_SCALE = 1;
174#endif
175
176static inline void unpackOctave(const KeyPoint &kpt, int &octave, int &layer,
177 float &scale) {
178 octave = kpt.octave & 255;
179 layer = (kpt.octave >> 8) & 255;
180 octave = octave < 128 ? octave : (-128 | octave);
181 scale = octave >= 0 ? 1.f / (1 << octave) : (float)(1 << -octave);
182}
183
Brian Silverman3fec6482020-01-19 17:56:20 -0800184constexpr bool kLogTiming = false;
Brian Silvermanf1196122020-01-16 00:41:54 -0800185
186} // namespace
187
188void SIFT971_Impl::buildGaussianPyramid(const Mat &base, std::vector<Mat> &pyr,
189 int nOctaves) const {
190 std::vector<double> sig(nOctaveLayers + 3);
191 pyr.resize(nOctaves * (nOctaveLayers + 3));
192
193 // precompute Gaussian sigmas using the following formula:
194 // \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2
195 sig[0] = sigma;
196 double k = std::pow(2., 1. / nOctaveLayers);
197 for (int i = 1; i < nOctaveLayers + 3; i++) {
198 double sig_prev = std::pow(k, (double)(i - 1)) * sigma;
199 double sig_total = sig_prev * k;
200 sig[i] = std::sqrt(sig_total * sig_total - sig_prev * sig_prev);
201 }
202
203 for (int o = 0; o < nOctaves; o++) {
204 for (int i = 0; i < nOctaveLayers + 3; i++) {
205 Mat &dst = pyr[o * (nOctaveLayers + 3) + i];
Brian Silverman3fec6482020-01-19 17:56:20 -0800206 if (o == 0 && i == 0) {
207 dst = base;
208 } else if (i == 0) {
209 // base of new octave is halved image from end of previous octave
Brian Silvermanf1196122020-01-16 00:41:54 -0800210 const Mat &src = pyr[(o - 1) * (nOctaveLayers + 3) + nOctaveLayers];
211 resize(src, dst, Size(src.cols / 2, src.rows / 2), 0, 0, INTER_NEAREST);
212 } else {
213 const Mat &src = pyr[o * (nOctaveLayers + 3) + i - 1];
Brian Silverman3fec6482020-01-19 17:56:20 -0800214 if (use_fast_gaussian_pyramid_) {
215 FastGaussian(src, &dst, sig[i]);
216 } else {
217 GaussianBlur(src, dst, Size(), sig[i], sig[i]);
218 }
Brian Silvermanf1196122020-01-16 00:41:54 -0800219 }
220 }
221 }
222}
223
224namespace {
225
226class buildDoGPyramidComputer : public ParallelLoopBody {
227 public:
228 buildDoGPyramidComputer(int _nOctaveLayers, const std::vector<Mat> &_gpyr,
Brian Silverman3fec6482020-01-19 17:56:20 -0800229 std::vector<Mat> &_dogpyr,
230 bool use_fast_subtract_dogpyr)
231 : nOctaveLayers(_nOctaveLayers),
232 gpyr(_gpyr),
233 dogpyr(_dogpyr),
234 use_fast_subtract_dogpyr_(use_fast_subtract_dogpyr) {}
Brian Silvermanf1196122020-01-16 00:41:54 -0800235
236 void operator()(const cv::Range &range) const override {
237 const int begin = range.start;
238 const int end = range.end;
239
240 for (int a = begin; a < end; a++) {
241 const int o = a / (nOctaveLayers + 2);
242 const int i = a % (nOctaveLayers + 2);
243
244 const Mat &src1 = gpyr[o * (nOctaveLayers + 3) + i];
245 const Mat &src2 = gpyr[o * (nOctaveLayers + 3) + i + 1];
Brian Silverman3fec6482020-01-19 17:56:20 -0800246 CHECK_EQ(a, o * (nOctaveLayers + 2) + i);
Brian Silvermanf1196122020-01-16 00:41:54 -0800247 Mat &dst = dogpyr[o * (nOctaveLayers + 2) + i];
Brian Silverman3fec6482020-01-19 17:56:20 -0800248 if (use_fast_subtract_dogpyr_) {
249 FastSubtract(src2, src1, &dst);
250 } else {
251 subtract(src2, src1, dst, noArray(), DataType<sift_wt>::type);
252 }
Brian Silvermanf1196122020-01-16 00:41:54 -0800253 }
254 }
255
256 private:
Brian Silverman3fec6482020-01-19 17:56:20 -0800257 const int nOctaveLayers;
Brian Silvermanf1196122020-01-16 00:41:54 -0800258 const std::vector<Mat> &gpyr;
259 std::vector<Mat> &dogpyr;
Brian Silverman3fec6482020-01-19 17:56:20 -0800260 const bool use_fast_subtract_dogpyr_;
Brian Silvermanf1196122020-01-16 00:41:54 -0800261};
262
263} // namespace
264
265void SIFT971_Impl::buildDoGPyramid(const std::vector<Mat> &gpyr,
266 std::vector<Mat> &dogpyr) const {
267 int nOctaves = (int)gpyr.size() / (nOctaveLayers + 3);
268 dogpyr.resize(nOctaves * (nOctaveLayers + 2));
269
Brian Silverman3fec6482020-01-19 17:56:20 -0800270#if 0
Brian Silvermanf1196122020-01-16 00:41:54 -0800271 parallel_for_(Range(0, nOctaves * (nOctaveLayers + 2)),
Brian Silverman3fec6482020-01-19 17:56:20 -0800272 buildDoGPyramidComputer(nOctaveLayers, gpyr, dogpyr, use_fast_subtract_dogpyr_));
273#else
274 buildDoGPyramidComputer(
275 nOctaveLayers, gpyr, dogpyr,
276 use_fast_subtract_dogpyr_)(Range(0, nOctaves * (nOctaveLayers + 2)));
277#endif
278}
279
280// base is the image to start with.
281// gpyr is the pyramid of gaussian blurs. This is both an output and a place
282// where we store intermediates.
283// dogpyr is the pyramid of gaussian differences which we fill out.
284// number_octaves is the number of octaves to calculate.
285void SIFT971_Impl::buildGaussianAndDifferencePyramid(
286 const cv::Mat &base, std::vector<cv::Mat> &gpyr,
287 std::vector<cv::Mat> &dogpyr, int number_octaves) const {
288 const int layers_per_octave = nOctaveLayers;
289 // We use the base (possibly after downscaling) as the first "blurred" image.
290 // Then we calculate 2 more than the number of octaves.
291 // TODO(Brian): Why are there 2 extra?
292 const int gpyr_layers_per_octave = layers_per_octave + 3;
293 // There is 1 less difference than the number of blurs.
294 const int dogpyr_layers_per_octave = gpyr_layers_per_octave - 1;
295 gpyr.resize(number_octaves * gpyr_layers_per_octave);
296 dogpyr.resize(number_octaves * dogpyr_layers_per_octave);
297
Brian Silverman97ba4b12020-02-15 23:36:36 -0800298 // Precompute Gaussian sigmas using the following formula:
299 // \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2
300 // We need one for each of the layers in the pyramid we blur, which skips the
301 // first one because it's just the base image without any blurring.
302 std::vector<double> sig(gpyr_layers_per_octave - 1);
Brian Silverman3fec6482020-01-19 17:56:20 -0800303 double k = std::pow(2., 1. / layers_per_octave);
Brian Silverman97ba4b12020-02-15 23:36:36 -0800304 for (int i = 0; i < gpyr_layers_per_octave - 1; i++) {
305 double sig_prev = std::pow<double>(k, i) * sigma;
Brian Silverman3fec6482020-01-19 17:56:20 -0800306 double sig_total = sig_prev * k;
307 sig[i] = std::sqrt(sig_total * sig_total - sig_prev * sig_prev);
308 }
309
310 for (int octave = 0; octave < number_octaves; octave++) {
Brian Silverman97ba4b12020-02-15 23:36:36 -0800311 const int octave_gpyr_index = octave * gpyr_layers_per_octave;
312 const int octave_dogpyr_index = octave * dogpyr_layers_per_octave;
313
Brian Silverman3fec6482020-01-19 17:56:20 -0800314 // At the beginning of each octave, calculate the new base image.
315 {
Brian Silverman97ba4b12020-02-15 23:36:36 -0800316 Mat &dst = gpyr[octave_gpyr_index];
Brian Silverman3fec6482020-01-19 17:56:20 -0800317 if (octave == 0) {
318 // For the first octave, it's just the base image.
319 dst = base;
320 } else {
Brian Silverman97ba4b12020-02-15 23:36:36 -0800321 // For the other octaves, OpenCV's code claims that it's a halved
322 // version of the end of the previous octave.
323 // TODO(Brian): But this isn't really the end of the previous octave?
324 // But if you use the end, it finds way fewer features? Maybe this is
325 // just a arbitrarily-ish-somewhat-blurred thing from the previous
326 // octave??
327 const int gpyr_index = octave_gpyr_index - 3;
328 // Verify that the indexing in the original OpenCV code gives the same
329 // result. It's unclear which one makes more logical sense.
330 CHECK_EQ((octave - 1) * gpyr_layers_per_octave + layers_per_octave,
331 gpyr_index);
332 const Mat &src = gpyr[gpyr_index];
Brian Silverman3fec6482020-01-19 17:56:20 -0800333 resize(src, dst, Size(src.cols / 2, src.rows / 2), 0, 0, INTER_NEAREST);
334 }
335 }
Brian Silverman97ba4b12020-02-15 23:36:36 -0800336
337 // Then, go through all the layers and calculate the appropriate
338 // differences.
339 for (int layer = 0; layer < dogpyr_layers_per_octave; layer++) {
Brian Silverman3fec6482020-01-19 17:56:20 -0800340 // The index where the current layer starts.
Brian Silverman97ba4b12020-02-15 23:36:36 -0800341 const int layer_gpyr_index = octave_gpyr_index + layer;
342 const int layer_dogpyr_index = octave_dogpyr_index + layer;
343
Brian Silverman3fec6482020-01-19 17:56:20 -0800344 if (use_fast_pyramid_difference_) {
Brian Silverman97ba4b12020-02-15 23:36:36 -0800345 const Mat &input = gpyr[layer_gpyr_index];
346 Mat &blurred = gpyr[layer_gpyr_index + 1];
347 Mat &difference = dogpyr[layer_dogpyr_index];
Brian Silverman3fec6482020-01-19 17:56:20 -0800348 FastGaussianAndSubtract(input, &blurred, &difference, sig[layer]);
349 } else {
350 // First, calculate the new gaussian blur.
351 {
Brian Silverman97ba4b12020-02-15 23:36:36 -0800352 const Mat &src = gpyr[layer_gpyr_index];
353 Mat &dst = gpyr[layer_gpyr_index + 1];
Brian Silverman3fec6482020-01-19 17:56:20 -0800354 if (use_fast_gaussian_pyramid_) {
355 FastGaussian(src, &dst, sig[layer]);
356 } else {
357 GaussianBlur(src, dst, Size(), sig[layer], sig[layer]);
358 }
359 }
360
361 // Then, calculate the difference from the previous one.
362 {
Brian Silverman97ba4b12020-02-15 23:36:36 -0800363 const Mat &src1 = gpyr[layer_gpyr_index];
364 const Mat &src2 = gpyr[layer_gpyr_index + 1];
365 Mat &dst = dogpyr[layer_dogpyr_index];
Brian Silverman3fec6482020-01-19 17:56:20 -0800366 if (use_fast_subtract_dogpyr_) {
367 FastSubtract(src2, src1, &dst);
368 } else {
369 subtract(src2, src1, dst, noArray(), DataType<sift_wt>::type);
370 }
371 }
372 }
373 }
374 }
Brian Silvermanf1196122020-01-16 00:41:54 -0800375}
376
377namespace {
378
379// Computes a gradient orientation histogram at a specified pixel
380static float calcOrientationHist(const Mat &img, Point pt, int radius,
381 float sigma, float *hist, int n) {
382 int i, j, k, len = (radius * 2 + 1) * (radius * 2 + 1);
383
384 float expf_scale = -1.f / (2.f * sigma * sigma);
385 AutoBuffer<float> buf(len * 4 + n + 4);
386 float *X = buf, *Y = X + len, *Mag = X, *Ori = Y + len, *W = Ori + len;
387 float *temphist = W + len + 2;
388
389 for (i = 0; i < n; i++) temphist[i] = 0.f;
390
391 for (i = -radius, k = 0; i <= radius; i++) {
392 int y = pt.y + i;
393 if (y <= 0 || y >= img.rows - 1) continue;
394 for (j = -radius; j <= radius; j++) {
395 int x = pt.x + j;
396 if (x <= 0 || x >= img.cols - 1) continue;
397
398 float dx = (float)(img.at<sift_wt>(y, x + 1) - img.at<sift_wt>(y, x - 1));
399 float dy = (float)(img.at<sift_wt>(y - 1, x) - img.at<sift_wt>(y + 1, x));
400
401 X[k] = dx;
402 Y[k] = dy;
403 W[k] = (i * i + j * j) * expf_scale;
404 k++;
405 }
406 }
407
408 len = k;
409
410 // compute gradient values, orientations and the weights over the pixel
411 // neighborhood
412 cv::hal::exp32f(W, W, len);
413 cv::hal::fastAtan2(Y, X, Ori, len, true);
414 cv::hal::magnitude32f(X, Y, Mag, len);
415
416 k = 0;
417#if CV_AVX2
418 if (USE_AVX2) {
419 __m256 __nd360 = _mm256_set1_ps(n / 360.f);
420 __m256i __n = _mm256_set1_epi32(n);
421 int CV_DECL_ALIGNED(32) bin_buf[8];
422 float CV_DECL_ALIGNED(32) w_mul_mag_buf[8];
423 for (; k <= len - 8; k += 8) {
424 __m256i __bin =
425 _mm256_cvtps_epi32(_mm256_mul_ps(__nd360, _mm256_loadu_ps(&Ori[k])));
426
427 __bin = _mm256_sub_epi32(
428 __bin, _mm256_andnot_si256(_mm256_cmpgt_epi32(__n, __bin), __n));
429 __bin = _mm256_add_epi32(
430 __bin, _mm256_and_si256(
431 __n, _mm256_cmpgt_epi32(_mm256_setzero_si256(), __bin)));
432
433 __m256 __w_mul_mag =
434 _mm256_mul_ps(_mm256_loadu_ps(&W[k]), _mm256_loadu_ps(&Mag[k]));
435
436 _mm256_store_si256((__m256i *)bin_buf, __bin);
437 _mm256_store_ps(w_mul_mag_buf, __w_mul_mag);
438
439 temphist[bin_buf[0]] += w_mul_mag_buf[0];
440 temphist[bin_buf[1]] += w_mul_mag_buf[1];
441 temphist[bin_buf[2]] += w_mul_mag_buf[2];
442 temphist[bin_buf[3]] += w_mul_mag_buf[3];
443 temphist[bin_buf[4]] += w_mul_mag_buf[4];
444 temphist[bin_buf[5]] += w_mul_mag_buf[5];
445 temphist[bin_buf[6]] += w_mul_mag_buf[6];
446 temphist[bin_buf[7]] += w_mul_mag_buf[7];
447 }
448 }
449#endif
450 for (; k < len; k++) {
451 int bin = cvRound((n / 360.f) * Ori[k]);
452 if (bin >= n) bin -= n;
453 if (bin < 0) bin += n;
454 temphist[bin] += W[k] * Mag[k];
455 }
456
457 // smooth the histogram
458 temphist[-1] = temphist[n - 1];
459 temphist[-2] = temphist[n - 2];
460 temphist[n] = temphist[0];
461 temphist[n + 1] = temphist[1];
462
463 i = 0;
464#if CV_AVX2
465 if (USE_AVX2) {
466 __m256 __d_1_16 = _mm256_set1_ps(1.f / 16.f);
467 __m256 __d_4_16 = _mm256_set1_ps(4.f / 16.f);
468 __m256 __d_6_16 = _mm256_set1_ps(6.f / 16.f);
469 for (; i <= n - 8; i += 8) {
470#if CV_FMA3
471 __m256 __hist = _mm256_fmadd_ps(
472 _mm256_add_ps(_mm256_loadu_ps(&temphist[i - 2]),
473 _mm256_loadu_ps(&temphist[i + 2])),
474 __d_1_16,
475 _mm256_fmadd_ps(
476 _mm256_add_ps(_mm256_loadu_ps(&temphist[i - 1]),
477 _mm256_loadu_ps(&temphist[i + 1])),
478 __d_4_16,
479 _mm256_mul_ps(_mm256_loadu_ps(&temphist[i]), __d_6_16)));
480#else
481 __m256 __hist = _mm256_add_ps(
482 _mm256_mul_ps(_mm256_add_ps(_mm256_loadu_ps(&temphist[i - 2]),
483 _mm256_loadu_ps(&temphist[i + 2])),
484 __d_1_16),
485 _mm256_add_ps(
486 _mm256_mul_ps(_mm256_add_ps(_mm256_loadu_ps(&temphist[i - 1]),
487 _mm256_loadu_ps(&temphist[i + 1])),
488 __d_4_16),
489 _mm256_mul_ps(_mm256_loadu_ps(&temphist[i]), __d_6_16)));
490#endif
491 _mm256_storeu_ps(&hist[i], __hist);
492 }
493 }
494#endif
495 for (; i < n; i++) {
496 hist[i] = (temphist[i - 2] + temphist[i + 2]) * (1.f / 16.f) +
497 (temphist[i - 1] + temphist[i + 1]) * (4.f / 16.f) +
498 temphist[i] * (6.f / 16.f);
499 }
500
501 float maxval = hist[0];
502 for (i = 1; i < n; i++) maxval = std::max(maxval, hist[i]);
503
504 return maxval;
505}
506
507//
508// Interpolates a scale-space extremum's location and scale to subpixel
509// accuracy to form an image feature. Rejects features with low contrast.
510// Based on Section 4 of Lowe's paper.
511static bool adjustLocalExtrema(const std::vector<Mat> &dog_pyr, KeyPoint &kpt,
512 int octv, int &layer, int &r, int &c,
513 int nOctaveLayers, float contrastThreshold,
514 float edgeThreshold, float sigma) {
515 const float img_scale = 1.f / (255 * SIFT_FIXPT_SCALE);
516 const float deriv_scale = img_scale * 0.5f;
517 const float second_deriv_scale = img_scale;
518 const float cross_deriv_scale = img_scale * 0.25f;
519
520 float xi = 0, xr = 0, xc = 0, contr = 0;
521 int i = 0;
522
523 for (; i < SIFT_MAX_INTERP_STEPS; i++) {
524 int idx = octv * (nOctaveLayers + 2) + layer;
525 const Mat &img = dog_pyr[idx];
526 const Mat &prev = dog_pyr[idx - 1];
527 const Mat &next = dog_pyr[idx + 1];
528
529 Vec3f dD(
530 (img.at<sift_wt>(r, c + 1) - img.at<sift_wt>(r, c - 1)) * deriv_scale,
531 (img.at<sift_wt>(r + 1, c) - img.at<sift_wt>(r - 1, c)) * deriv_scale,
532 (next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c)) * deriv_scale);
533
534 float v2 = (float)img.at<sift_wt>(r, c) * 2;
535 float dxx = (img.at<sift_wt>(r, c + 1) + img.at<sift_wt>(r, c - 1) - v2) *
536 second_deriv_scale;
537 float dyy = (img.at<sift_wt>(r + 1, c) + img.at<sift_wt>(r - 1, c) - v2) *
538 second_deriv_scale;
539 float dss = (next.at<sift_wt>(r, c) + prev.at<sift_wt>(r, c) - v2) *
540 second_deriv_scale;
541 float dxy =
542 (img.at<sift_wt>(r + 1, c + 1) - img.at<sift_wt>(r + 1, c - 1) -
543 img.at<sift_wt>(r - 1, c + 1) + img.at<sift_wt>(r - 1, c - 1)) *
544 cross_deriv_scale;
545 float dxs = (next.at<sift_wt>(r, c + 1) - next.at<sift_wt>(r, c - 1) -
546 prev.at<sift_wt>(r, c + 1) + prev.at<sift_wt>(r, c - 1)) *
547 cross_deriv_scale;
548 float dys = (next.at<sift_wt>(r + 1, c) - next.at<sift_wt>(r - 1, c) -
549 prev.at<sift_wt>(r + 1, c) + prev.at<sift_wt>(r - 1, c)) *
550 cross_deriv_scale;
551
552 Matx33f H(dxx, dxy, dxs, dxy, dyy, dys, dxs, dys, dss);
553
554 Vec3f X = H.solve(dD, DECOMP_LU);
555
556 xi = -X[2];
557 xr = -X[1];
558 xc = -X[0];
559
560 if (std::abs(xi) < 0.5f && std::abs(xr) < 0.5f && std::abs(xc) < 0.5f)
561 break;
562
563 if (std::abs(xi) > (float)(INT_MAX / 3) ||
564 std::abs(xr) > (float)(INT_MAX / 3) ||
565 std::abs(xc) > (float)(INT_MAX / 3))
566 return false;
567
568 c += cvRound(xc);
569 r += cvRound(xr);
570 layer += cvRound(xi);
571
572 if (layer < 1 || layer > nOctaveLayers || c < SIFT_IMG_BORDER ||
573 c >= img.cols - SIFT_IMG_BORDER || r < SIFT_IMG_BORDER ||
574 r >= img.rows - SIFT_IMG_BORDER)
575 return false;
576 }
577
578 // ensure convergence of interpolation
579 if (i >= SIFT_MAX_INTERP_STEPS) return false;
580
581 {
582 int idx = octv * (nOctaveLayers + 2) + layer;
583 const Mat &img = dog_pyr[idx];
584 const Mat &prev = dog_pyr[idx - 1];
585 const Mat &next = dog_pyr[idx + 1];
586 Matx31f dD(
587 (img.at<sift_wt>(r, c + 1) - img.at<sift_wt>(r, c - 1)) * deriv_scale,
588 (img.at<sift_wt>(r + 1, c) - img.at<sift_wt>(r - 1, c)) * deriv_scale,
589 (next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c)) * deriv_scale);
590 float t = dD.dot(Matx31f(xc, xr, xi));
591
592 contr = img.at<sift_wt>(r, c) * img_scale + t * 0.5f;
593 if (std::abs(contr) * nOctaveLayers < contrastThreshold) return false;
594
595 // principal curvatures are computed using the trace and det of Hessian
596 float v2 = img.at<sift_wt>(r, c) * 2.f;
597 float dxx = (img.at<sift_wt>(r, c + 1) + img.at<sift_wt>(r, c - 1) - v2) *
598 second_deriv_scale;
599 float dyy = (img.at<sift_wt>(r + 1, c) + img.at<sift_wt>(r - 1, c) - v2) *
600 second_deriv_scale;
601 float dxy =
602 (img.at<sift_wt>(r + 1, c + 1) - img.at<sift_wt>(r + 1, c - 1) -
603 img.at<sift_wt>(r - 1, c + 1) + img.at<sift_wt>(r - 1, c - 1)) *
604 cross_deriv_scale;
605 float tr = dxx + dyy;
606 float det = dxx * dyy - dxy * dxy;
607
608 if (det <= 0 || tr * tr * edgeThreshold >=
609 (edgeThreshold + 1) * (edgeThreshold + 1) * det)
610 return false;
611 }
612
613 kpt.pt.x = (c + xc) * (1 << octv);
614 kpt.pt.y = (r + xr) * (1 << octv);
615 kpt.octave = octv + (layer << 8) + (cvRound((xi + 0.5) * 255) << 16);
616 kpt.size = sigma * powf(2.f, (layer + xi) / nOctaveLayers) * (1 << octv) * 2;
617 kpt.response = std::abs(contr);
618
619 return true;
620}
621
622template <typename T>
623class PerThreadAccumulator {
624 public:
625 void Add(std::vector<T> &&data) {
626 std::unique_lock locker(mutex_);
627 result_.emplace_back(data);
628 }
629
630 std::vector<std::vector<T>> move_result() { return std::move(result_); }
631
632 private:
633 // Should we do something more intelligent with per-thread std::vector that we
634 // merge at the end?
635 std::vector<std::vector<T>> result_;
636 std::mutex mutex_;
637};
638
639class findScaleSpaceExtremaComputer : public ParallelLoopBody {
640 public:
641 findScaleSpaceExtremaComputer(
642 int _o, int _i, int _threshold, int _idx, int _step, int _cols,
643 int _nOctaveLayers, double _contrastThreshold, double _edgeThreshold,
644 double _sigma, const std::vector<Mat> &_gauss_pyr,
645 const std::vector<Mat> &_dog_pyr,
646 PerThreadAccumulator<KeyPoint> &_tls_kpts_struct)
647
648 : o(_o),
649 i(_i),
650 threshold(_threshold),
651 idx(_idx),
652 step(_step),
653 cols(_cols),
654 nOctaveLayers(_nOctaveLayers),
655 contrastThreshold(_contrastThreshold),
656 edgeThreshold(_edgeThreshold),
657 sigma(_sigma),
658 gauss_pyr(_gauss_pyr),
659 dog_pyr(_dog_pyr),
660 tls_kpts_struct(_tls_kpts_struct) {}
661 void operator()(const cv::Range &range) const override {
662 const int begin = range.start;
663 const int end = range.end;
664
665 static const int n = SIFT_ORI_HIST_BINS;
666 float hist[n];
667
668 const Mat &img = dog_pyr[idx];
669 const Mat &prev = dog_pyr[idx - 1];
670 const Mat &next = dog_pyr[idx + 1];
671
672 std::vector<KeyPoint> tls_kpts;
673
674 KeyPoint kpt;
675 for (int r = begin; r < end; r++) {
676 const sift_wt *currptr = img.ptr<sift_wt>(r);
677 const sift_wt *prevptr = prev.ptr<sift_wt>(r);
678 const sift_wt *nextptr = next.ptr<sift_wt>(r);
679
680 for (int c = SIFT_IMG_BORDER; c < cols - SIFT_IMG_BORDER; c++) {
681 sift_wt val = currptr[c];
682
683 // find local extrema with pixel accuracy
684 if (std::abs(val) > threshold &&
685 ((val > 0 && val >= currptr[c - 1] && val >= currptr[c + 1] &&
686 val >= currptr[c - step - 1] && val >= currptr[c - step] &&
687 val >= currptr[c - step + 1] && val >= currptr[c + step - 1] &&
688 val >= currptr[c + step] && val >= currptr[c + step + 1] &&
689 val >= nextptr[c] && val >= nextptr[c - 1] &&
690 val >= nextptr[c + 1] && val >= nextptr[c - step - 1] &&
691 val >= nextptr[c - step] && val >= nextptr[c - step + 1] &&
692 val >= nextptr[c + step - 1] && val >= nextptr[c + step] &&
693 val >= nextptr[c + step + 1] && val >= prevptr[c] &&
694 val >= prevptr[c - 1] && val >= prevptr[c + 1] &&
695 val >= prevptr[c - step - 1] && val >= prevptr[c - step] &&
696 val >= prevptr[c - step + 1] && val >= prevptr[c + step - 1] &&
697 val >= prevptr[c + step] && val >= prevptr[c + step + 1]) ||
698 (val < 0 && val <= currptr[c - 1] && val <= currptr[c + 1] &&
699 val <= currptr[c - step - 1] && val <= currptr[c - step] &&
700 val <= currptr[c - step + 1] && val <= currptr[c + step - 1] &&
701 val <= currptr[c + step] && val <= currptr[c + step + 1] &&
702 val <= nextptr[c] && val <= nextptr[c - 1] &&
703 val <= nextptr[c + 1] && val <= nextptr[c - step - 1] &&
704 val <= nextptr[c - step] && val <= nextptr[c - step + 1] &&
705 val <= nextptr[c + step - 1] && val <= nextptr[c + step] &&
706 val <= nextptr[c + step + 1] && val <= prevptr[c] &&
707 val <= prevptr[c - 1] && val <= prevptr[c + 1] &&
708 val <= prevptr[c - step - 1] && val <= prevptr[c - step] &&
709 val <= prevptr[c - step + 1] && val <= prevptr[c + step - 1] &&
710 val <= prevptr[c + step] && val <= prevptr[c + step + 1]))) {
711 int r1 = r, c1 = c, layer = i;
712 if (!adjustLocalExtrema(dog_pyr, kpt, o, layer, r1, c1, nOctaveLayers,
713 (float)contrastThreshold,
714 (float)edgeThreshold, (float)sigma))
715 continue;
716 float scl_octv = kpt.size * 0.5f / (1 << o);
717 float omax = calcOrientationHist(
718 gauss_pyr[o * (nOctaveLayers + 3) + layer], Point(c1, r1),
719 cvRound(SIFT_ORI_RADIUS * scl_octv), SIFT_ORI_SIG_FCTR * scl_octv,
720 hist, n);
721 float mag_thr = (float)(omax * SIFT_ORI_PEAK_RATIO);
722 for (int j = 0; j < n; j++) {
723 int l = j > 0 ? j - 1 : n - 1;
724 int r2 = j < n - 1 ? j + 1 : 0;
725
726 if (hist[j] > hist[l] && hist[j] > hist[r2] && hist[j] >= mag_thr) {
727 float bin = j + 0.5f * (hist[l] - hist[r2]) /
728 (hist[l] - 2 * hist[j] + hist[r2]);
729 bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;
730 kpt.angle = 360.f - (float)((360.f / n) * bin);
731 if (std::abs(kpt.angle - 360.f) < FLT_EPSILON) kpt.angle = 0.f;
732 { tls_kpts.push_back(kpt); }
733 }
734 }
735 }
736 }
737 }
738
739 tls_kpts_struct.Add(std::move(tls_kpts));
740 }
741
742 private:
743 int o, i;
744 int threshold;
745 int idx, step, cols;
746 int nOctaveLayers;
747 double contrastThreshold;
748 double edgeThreshold;
749 double sigma;
750 const std::vector<Mat> &gauss_pyr;
751 const std::vector<Mat> &dog_pyr;
752 PerThreadAccumulator<KeyPoint> &tls_kpts_struct;
753};
754
755} // namespace
756
757//
758// Detects features at extrema in DoG scale space. Bad features are discarded
759// based on contrast and ratio of principal curvatures.
760void SIFT971_Impl::findScaleSpaceExtrema(
761 const std::vector<Mat> &gauss_pyr, const std::vector<Mat> &dog_pyr,
762 std::vector<KeyPoint> &keypoints) const {
763 const int nOctaves = (int)gauss_pyr.size() / (nOctaveLayers + 3);
764 const int threshold =
765 cvFloor(0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE);
766
767 keypoints.clear();
768 PerThreadAccumulator<KeyPoint> tls_kpts_struct;
769
770 for (int o = 0; o < nOctaves; o++)
771 for (int i = 1; i <= nOctaveLayers; i++) {
772 const int idx = o * (nOctaveLayers + 2) + i;
773 const Mat &img = dog_pyr[idx];
774 const int step = (int)img.step1();
775 const int rows = img.rows, cols = img.cols;
776
777 parallel_for_(Range(SIFT_IMG_BORDER, rows - SIFT_IMG_BORDER),
778 findScaleSpaceExtremaComputer(
779 o, i, threshold, idx, step, cols, nOctaveLayers,
780 contrastThreshold, edgeThreshold, sigma, gauss_pyr,
781 dog_pyr, tls_kpts_struct));
782 }
783
784 const std::vector<std::vector<KeyPoint>> kpt_vecs =
785 tls_kpts_struct.move_result();
786 for (size_t i = 0; i < kpt_vecs.size(); ++i) {
787 keypoints.insert(keypoints.end(), kpt_vecs[i].begin(), kpt_vecs[i].end());
788 }
789}
790
791namespace {
792
793static void calcSIFTDescriptor(const Mat &img, Point2f ptf, float ori,
794 float scl, int d, int n, float *dst) {
795 Point pt(cvRound(ptf.x), cvRound(ptf.y));
796 float cos_t = cosf(ori * (float)(CV_PI / 180));
797 float sin_t = sinf(ori * (float)(CV_PI / 180));
798 float bins_per_rad = n / 360.f;
799 float exp_scale = -1.f / (d * d * 0.5f);
800 float hist_width = SIFT_DESCR_SCL_FCTR * scl;
801 int radius = cvRound(hist_width * 1.4142135623730951f * (d + 1) * 0.5f);
802 // Clip the radius to the diagonal of the image to avoid autobuffer too large
803 // exception
804 radius = std::min(radius, (int)sqrt(((double)img.cols) * img.cols +
805 ((double)img.rows) * img.rows));
806 cos_t /= hist_width;
807 sin_t /= hist_width;
808
809 int i, j, k, len = (radius * 2 + 1) * (radius * 2 + 1),
810 histlen = (d + 2) * (d + 2) * (n + 2);
811 int rows = img.rows, cols = img.cols;
812
813 AutoBuffer<float> buf(len * 6 + histlen);
814 float *X = buf, *Y = X + len, *Mag = Y, *Ori = Mag + len, *W = Ori + len;
815 float *RBin = W + len, *CBin = RBin + len, *hist = CBin + len;
816
817 for (i = 0; i < d + 2; i++) {
818 for (j = 0; j < d + 2; j++)
819 for (k = 0; k < n + 2; k++) hist[(i * (d + 2) + j) * (n + 2) + k] = 0.;
820 }
821
822 for (i = -radius, k = 0; i <= radius; i++)
823 for (j = -radius; j <= radius; j++) {
824 // Calculate sample's histogram array coords rotated relative to ori.
825 // Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e.
826 // r_rot = 1.5) have full weight placed in row 1 after interpolation.
827 float c_rot = j * cos_t - i * sin_t;
828 float r_rot = j * sin_t + i * cos_t;
829 float rbin = r_rot + d / 2 - 0.5f;
830 float cbin = c_rot + d / 2 - 0.5f;
831 int r = pt.y + i, c = pt.x + j;
832
833 if (rbin > -1 && rbin < d && cbin > -1 && cbin < d && r > 0 &&
834 r < rows - 1 && c > 0 && c < cols - 1) {
835 float dx =
836 (float)(img.at<sift_wt>(r, c + 1) - img.at<sift_wt>(r, c - 1));
837 float dy =
838 (float)(img.at<sift_wt>(r - 1, c) - img.at<sift_wt>(r + 1, c));
839 X[k] = dx;
840 Y[k] = dy;
841 RBin[k] = rbin;
842 CBin[k] = cbin;
843 W[k] = (c_rot * c_rot + r_rot * r_rot) * exp_scale;
844 k++;
845 }
846 }
847
848 len = k;
849 cv::hal::fastAtan2(Y, X, Ori, len, true);
850 cv::hal::magnitude32f(X, Y, Mag, len);
851 cv::hal::exp32f(W, W, len);
852
853 k = 0;
854#if CV_AVX2
855 if (USE_AVX2) {
856 int CV_DECL_ALIGNED(32) idx_buf[8];
857 float CV_DECL_ALIGNED(32) rco_buf[64];
858 const __m256 __ori = _mm256_set1_ps(ori);
859 const __m256 __bins_per_rad = _mm256_set1_ps(bins_per_rad);
860 const __m256i __n = _mm256_set1_epi32(n);
861 for (; k <= len - 8; k += 8) {
862 __m256 __rbin = _mm256_loadu_ps(&RBin[k]);
863 __m256 __cbin = _mm256_loadu_ps(&CBin[k]);
864 __m256 __obin = _mm256_mul_ps(
865 _mm256_sub_ps(_mm256_loadu_ps(&Ori[k]), __ori), __bins_per_rad);
866 __m256 __mag =
867 _mm256_mul_ps(_mm256_loadu_ps(&Mag[k]), _mm256_loadu_ps(&W[k]));
868
869 __m256 __r0 = _mm256_floor_ps(__rbin);
870 __rbin = _mm256_sub_ps(__rbin, __r0);
871 __m256 __c0 = _mm256_floor_ps(__cbin);
872 __cbin = _mm256_sub_ps(__cbin, __c0);
873 __m256 __o0 = _mm256_floor_ps(__obin);
874 __obin = _mm256_sub_ps(__obin, __o0);
875
876 __m256i __o0i = _mm256_cvtps_epi32(__o0);
877 __o0i = _mm256_add_epi32(
878 __o0i, _mm256_and_si256(
879 __n, _mm256_cmpgt_epi32(_mm256_setzero_si256(), __o0i)));
880 __o0i = _mm256_sub_epi32(
881 __o0i, _mm256_andnot_si256(_mm256_cmpgt_epi32(__n, __o0i), __n));
882
883 __m256 __v_r1 = _mm256_mul_ps(__mag, __rbin);
884 __m256 __v_r0 = _mm256_sub_ps(__mag, __v_r1);
885
886 __m256 __v_rc11 = _mm256_mul_ps(__v_r1, __cbin);
887 __m256 __v_rc10 = _mm256_sub_ps(__v_r1, __v_rc11);
888
889 __m256 __v_rc01 = _mm256_mul_ps(__v_r0, __cbin);
890 __m256 __v_rc00 = _mm256_sub_ps(__v_r0, __v_rc01);
891
892 __m256 __v_rco111 = _mm256_mul_ps(__v_rc11, __obin);
893 __m256 __v_rco110 = _mm256_sub_ps(__v_rc11, __v_rco111);
894
895 __m256 __v_rco101 = _mm256_mul_ps(__v_rc10, __obin);
896 __m256 __v_rco100 = _mm256_sub_ps(__v_rc10, __v_rco101);
897
898 __m256 __v_rco011 = _mm256_mul_ps(__v_rc01, __obin);
899 __m256 __v_rco010 = _mm256_sub_ps(__v_rc01, __v_rco011);
900
901 __m256 __v_rco001 = _mm256_mul_ps(__v_rc00, __obin);
902 __m256 __v_rco000 = _mm256_sub_ps(__v_rc00, __v_rco001);
903
904 __m256i __one = _mm256_set1_epi32(1);
905 __m256i __idx = _mm256_add_epi32(
906 _mm256_mullo_epi32(
907 _mm256_add_epi32(
908 _mm256_mullo_epi32(
909 _mm256_add_epi32(_mm256_cvtps_epi32(__r0), __one),
910 _mm256_set1_epi32(d + 2)),
911 _mm256_add_epi32(_mm256_cvtps_epi32(__c0), __one)),
912 _mm256_set1_epi32(n + 2)),
913 __o0i);
914
915 _mm256_store_si256((__m256i *)idx_buf, __idx);
916
917 _mm256_store_ps(&(rco_buf[0]), __v_rco000);
918 _mm256_store_ps(&(rco_buf[8]), __v_rco001);
919 _mm256_store_ps(&(rco_buf[16]), __v_rco010);
920 _mm256_store_ps(&(rco_buf[24]), __v_rco011);
921 _mm256_store_ps(&(rco_buf[32]), __v_rco100);
922 _mm256_store_ps(&(rco_buf[40]), __v_rco101);
923 _mm256_store_ps(&(rco_buf[48]), __v_rco110);
924 _mm256_store_ps(&(rco_buf[56]), __v_rco111);
925#define HIST_SUM_HELPER(id) \
926 hist[idx_buf[(id)]] += rco_buf[(id)]; \
927 hist[idx_buf[(id)] + 1] += rco_buf[8 + (id)]; \
928 hist[idx_buf[(id)] + (n + 2)] += rco_buf[16 + (id)]; \
929 hist[idx_buf[(id)] + (n + 3)] += rco_buf[24 + (id)]; \
930 hist[idx_buf[(id)] + (d + 2) * (n + 2)] += rco_buf[32 + (id)]; \
931 hist[idx_buf[(id)] + (d + 2) * (n + 2) + 1] += rco_buf[40 + (id)]; \
932 hist[idx_buf[(id)] + (d + 3) * (n + 2)] += rco_buf[48 + (id)]; \
933 hist[idx_buf[(id)] + (d + 3) * (n + 2) + 1] += rco_buf[56 + (id)];
934
935 HIST_SUM_HELPER(0);
936 HIST_SUM_HELPER(1);
937 HIST_SUM_HELPER(2);
938 HIST_SUM_HELPER(3);
939 HIST_SUM_HELPER(4);
940 HIST_SUM_HELPER(5);
941 HIST_SUM_HELPER(6);
942 HIST_SUM_HELPER(7);
943
944#undef HIST_SUM_HELPER
945 }
946 }
947#endif
948 for (; k < len; k++) {
949 float rbin = RBin[k], cbin = CBin[k];
950 float obin = (Ori[k] - ori) * bins_per_rad;
951 float mag = Mag[k] * W[k];
952
953 int r0 = cvFloor(rbin);
954 int c0 = cvFloor(cbin);
955 int o0 = cvFloor(obin);
956 rbin -= r0;
957 cbin -= c0;
958 obin -= o0;
959
960 if (o0 < 0) o0 += n;
961 if (o0 >= n) o0 -= n;
962
963 // histogram update using tri-linear interpolation
964 float v_r1 = mag * rbin, v_r0 = mag - v_r1;
965 float v_rc11 = v_r1 * cbin, v_rc10 = v_r1 - v_rc11;
966 float v_rc01 = v_r0 * cbin, v_rc00 = v_r0 - v_rc01;
967 float v_rco111 = v_rc11 * obin, v_rco110 = v_rc11 - v_rco111;
968 float v_rco101 = v_rc10 * obin, v_rco100 = v_rc10 - v_rco101;
969 float v_rco011 = v_rc01 * obin, v_rco010 = v_rc01 - v_rco011;
970 float v_rco001 = v_rc00 * obin, v_rco000 = v_rc00 - v_rco001;
971
972 int idx = ((r0 + 1) * (d + 2) + c0 + 1) * (n + 2) + o0;
973 hist[idx] += v_rco000;
974 hist[idx + 1] += v_rco001;
975 hist[idx + (n + 2)] += v_rco010;
976 hist[idx + (n + 3)] += v_rco011;
977 hist[idx + (d + 2) * (n + 2)] += v_rco100;
978 hist[idx + (d + 2) * (n + 2) + 1] += v_rco101;
979 hist[idx + (d + 3) * (n + 2)] += v_rco110;
980 hist[idx + (d + 3) * (n + 2) + 1] += v_rco111;
981 }
982
983 // finalize histogram, since the orientation histograms are circular
984 for (i = 0; i < d; i++)
985 for (j = 0; j < d; j++) {
986 int idx = ((i + 1) * (d + 2) + (j + 1)) * (n + 2);
987 hist[idx] += hist[idx + n];
988 hist[idx + 1] += hist[idx + n + 1];
989 for (k = 0; k < n; k++) dst[(i * d + j) * n + k] = hist[idx + k];
990 }
991 // copy histogram to the descriptor,
992 // apply hysteresis thresholding
993 // and scale the result, so that it can be easily converted
994 // to byte array
995 float nrm2 = 0;
996 len = d * d * n;
997 k = 0;
998#if CV_AVX2
999 if (USE_AVX2) {
1000 float CV_DECL_ALIGNED(32) nrm2_buf[8];
1001 __m256 __nrm2 = _mm256_setzero_ps();
1002 __m256 __dst;
1003 for (; k <= len - 8; k += 8) {
1004 __dst = _mm256_loadu_ps(&dst[k]);
1005#if CV_FMA3
1006 __nrm2 = _mm256_fmadd_ps(__dst, __dst, __nrm2);
1007#else
1008 __nrm2 = _mm256_add_ps(__nrm2, _mm256_mul_ps(__dst, __dst));
1009#endif
1010 }
1011 _mm256_store_ps(nrm2_buf, __nrm2);
1012 nrm2 = nrm2_buf[0] + nrm2_buf[1] + nrm2_buf[2] + nrm2_buf[3] + nrm2_buf[4] +
1013 nrm2_buf[5] + nrm2_buf[6] + nrm2_buf[7];
1014 }
1015#endif
1016 for (; k < len; k++) nrm2 += dst[k] * dst[k];
1017
1018 float thr = std::sqrt(nrm2) * SIFT_DESCR_MAG_THR;
1019
1020 i = 0, nrm2 = 0;
1021#if 0 // CV_AVX2
1022 // This code cannot be enabled because it sums nrm2 in a different order,
1023 // thus producing slightly different results
1024 if( USE_AVX2 )
1025 {
1026 float CV_DECL_ALIGNED(32) nrm2_buf[8];
1027 __m256 __dst;
1028 __m256 __nrm2 = _mm256_setzero_ps();
1029 __m256 __thr = _mm256_set1_ps(thr);
1030 for( ; i <= len - 8; i += 8 )
1031 {
1032 __dst = _mm256_loadu_ps(&dst[i]);
1033 __dst = _mm256_min_ps(__dst, __thr);
1034 _mm256_storeu_ps(&dst[i], __dst);
1035#if CV_FMA3
1036 __nrm2 = _mm256_fmadd_ps(__dst, __dst, __nrm2);
1037#else
1038 __nrm2 = _mm256_add_ps(__nrm2, _mm256_mul_ps(__dst, __dst));
1039#endif
1040 }
1041 _mm256_store_ps(nrm2_buf, __nrm2);
1042 nrm2 = nrm2_buf[0] + nrm2_buf[1] + nrm2_buf[2] + nrm2_buf[3] +
1043 nrm2_buf[4] + nrm2_buf[5] + nrm2_buf[6] + nrm2_buf[7];
1044 }
1045#endif
1046 for (; i < len; i++) {
1047 float val = std::min(dst[i], thr);
1048 dst[i] = val;
1049 nrm2 += val * val;
1050 }
1051 nrm2 = SIFT_INT_DESCR_FCTR / std::max(std::sqrt(nrm2), FLT_EPSILON);
1052
1053#if 1
1054 k = 0;
1055#if CV_AVX2
1056 if (USE_AVX2) {
1057 __m256 __dst;
1058 __m256 __min = _mm256_setzero_ps();
1059 __m256 __max = _mm256_set1_ps(255.0f); // max of uchar
1060 __m256 __nrm2 = _mm256_set1_ps(nrm2);
1061 for (k = 0; k <= len - 8; k += 8) {
1062 __dst = _mm256_loadu_ps(&dst[k]);
1063 __dst = _mm256_min_ps(
1064 _mm256_max_ps(
1065 _mm256_round_ps(_mm256_mul_ps(__dst, __nrm2),
1066 _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC),
1067 __min),
1068 __max);
1069 _mm256_storeu_ps(&dst[k], __dst);
1070 }
1071 }
1072#endif
1073 for (; k < len; k++) {
1074 dst[k] = saturate_cast<uchar>(dst[k] * nrm2);
1075 }
1076#else
1077 float nrm1 = 0;
1078 for (k = 0; k < len; k++) {
1079 dst[k] *= nrm2;
1080 nrm1 += dst[k];
1081 }
1082 nrm1 = 1.f / std::max(nrm1, FLT_EPSILON);
1083 for (k = 0; k < len; k++) {
1084 dst[k] = std::sqrt(dst[k] * nrm1); // saturate_cast<uchar>(std::sqrt(dst[k]
1085 // * nrm1)*SIFT_INT_DESCR_FCTR);
1086 }
1087#endif
1088}
1089
1090class calcDescriptorsComputer : public ParallelLoopBody {
1091 public:
1092 calcDescriptorsComputer(const std::vector<Mat> &_gpyr,
1093 const std::vector<KeyPoint> &_keypoints,
1094 Mat &_descriptors, int _nOctaveLayers,
1095 int _firstOctave)
1096 : gpyr(_gpyr),
1097 keypoints(_keypoints),
1098 descriptors(_descriptors),
1099 nOctaveLayers(_nOctaveLayers),
1100 firstOctave(_firstOctave) {}
1101
1102 void operator()(const cv::Range &range) const override {
1103 const int begin = range.start;
1104 const int end = range.end;
1105
1106 static const int d = SIFT_DESCR_WIDTH, n = SIFT_DESCR_HIST_BINS;
1107
1108 for (int i = begin; i < end; i++) {
1109 KeyPoint kpt = keypoints[i];
1110 int octave, layer;
1111 float scale;
1112 unpackOctave(kpt, octave, layer, scale);
1113 CV_Assert(octave >= firstOctave && layer <= nOctaveLayers + 2);
1114 float size = kpt.size * scale;
1115 Point2f ptf(kpt.pt.x * scale, kpt.pt.y * scale);
1116 const Mat &img =
1117 gpyr[(octave - firstOctave) * (nOctaveLayers + 3) + layer];
1118
1119 float angle = 360.f - kpt.angle;
1120 if (std::abs(angle - 360.f) < FLT_EPSILON) angle = 0.f;
1121 calcSIFTDescriptor(img, ptf, angle, size * 0.5f, d, n,
1122 descriptors.ptr<float>((int)i));
1123 }
1124 }
1125
1126 private:
1127 const std::vector<Mat> &gpyr;
1128 const std::vector<KeyPoint> &keypoints;
1129 Mat &descriptors;
1130 int nOctaveLayers;
1131 int firstOctave;
1132};
1133
1134static void calcDescriptors(const std::vector<Mat> &gpyr,
1135 const std::vector<KeyPoint> &keypoints,
1136 Mat &descriptors, int nOctaveLayers,
1137 int firstOctave) {
1138 parallel_for_(Range(0, static_cast<int>(keypoints.size())),
1139 calcDescriptorsComputer(gpyr, keypoints, descriptors,
1140 nOctaveLayers, firstOctave));
1141}
1142
1143} // namespace
1144
1145//////////////////////////////////////////////////////////////////////////////////////////
1146
1147SIFT971_Impl::SIFT971_Impl(int _nfeatures, int _nOctaveLayers,
1148 double _contrastThreshold, double _edgeThreshold,
1149 double _sigma)
1150 : nfeatures(_nfeatures),
1151 nOctaveLayers(_nOctaveLayers),
1152 contrastThreshold(_contrastThreshold),
1153 edgeThreshold(_edgeThreshold),
1154 sigma(_sigma) {}
1155
1156int SIFT971_Impl::descriptorSize() const {
1157 return SIFT_DESCR_WIDTH * SIFT_DESCR_WIDTH * SIFT_DESCR_HIST_BINS;
1158}
1159
1160int SIFT971_Impl::descriptorType() const { return CV_32F; }
1161
1162int SIFT971_Impl::defaultNorm() const { return NORM_L2; }
1163
1164void SIFT971_Impl::detectAndCompute(InputArray _image, InputArray _mask,
1165 std::vector<KeyPoint> &keypoints,
1166 OutputArray _descriptors,
1167 bool useProvidedKeypoints) {
Brian Silverman950bffa2020-02-01 16:53:49 -08001168 int firstOctave = -1, actualNOctaves = 0, actualNLayers = 0;
Brian Silvermanf1196122020-01-16 00:41:54 -08001169 Mat image = _image.getMat(), mask = _mask.getMat();
1170
1171 if (image.empty() || image.depth() != CV_8U)
1172 CV_Error(Error::StsBadArg,
1173 "image is empty or has incorrect depth (!=CV_8U)");
1174
1175 if (!mask.empty() && mask.type() != CV_8UC1)
1176 CV_Error(Error::StsBadArg, "mask has incorrect type (!=CV_8UC1)");
1177
1178 if (useProvidedKeypoints) {
Brian Silverman3fec6482020-01-19 17:56:20 -08001179 LOG_IF(INFO, kLogTiming);
Brian Silvermanf1196122020-01-16 00:41:54 -08001180 firstOctave = 0;
1181 int maxOctave = INT_MIN;
1182 for (size_t i = 0; i < keypoints.size(); i++) {
1183 int octave, layer;
1184 float scale;
1185 unpackOctave(keypoints[i], octave, layer, scale);
1186 firstOctave = std::min(firstOctave, octave);
1187 maxOctave = std::max(maxOctave, octave);
1188 actualNLayers = std::max(actualNLayers, layer - 2);
1189 }
1190
1191 firstOctave = std::min(firstOctave, 0);
1192 CV_Assert(firstOctave >= -1 && actualNLayers <= nOctaveLayers);
1193 actualNOctaves = maxOctave - firstOctave + 1;
1194 }
1195
Brian Silverman3fec6482020-01-19 17:56:20 -08001196 LOG_IF(INFO, kLogTiming);
1197 Mat base = createInitialImage(image, firstOctave < 0);
1198 LOG_IF(INFO, kLogTiming);
Brian Silvermanf1196122020-01-16 00:41:54 -08001199 std::vector<Mat> gpyr;
Brian Silverman3fec6482020-01-19 17:56:20 -08001200 int nOctaves;
1201 if (actualNOctaves > 0) {
1202 nOctaves = actualNOctaves;
1203 } else {
1204 nOctaves = cvRound(std::log((double)std::min(base.cols, base.rows)) /
1205 std::log(2.) -
1206 2) -
1207 firstOctave;
1208 }
Brian Silvermanf1196122020-01-16 00:41:54 -08001209
1210 if (!useProvidedKeypoints) {
1211 std::vector<Mat> dogpyr;
Brian Silverman3fec6482020-01-19 17:56:20 -08001212 if (use_fused_pyramid_difference_) {
1213 buildGaussianAndDifferencePyramid(base, gpyr, dogpyr, nOctaves);
1214 LOG_IF(INFO, kLogTiming);
1215 } else {
1216 buildGaussianPyramid(base, gpyr, nOctaves);
1217 LOG_IF(INFO, kLogTiming);
1218
1219 buildDoGPyramid(gpyr, dogpyr);
1220 LOG_IF(INFO, kLogTiming);
1221 }
1222
Brian Silvermanf1196122020-01-16 00:41:54 -08001223 findScaleSpaceExtrema(gpyr, dogpyr, keypoints);
1224 // TODO(Brian): I think it can go faster by knowing they're sorted?
1225 // KeyPointsFilter::removeDuplicatedSorted( keypoints );
1226 KeyPointsFilter::removeDuplicated(keypoints);
1227
1228 if (nfeatures > 0) KeyPointsFilter::retainBest(keypoints, nfeatures);
Brian Silvermanf1196122020-01-16 00:41:54 -08001229
1230 if (firstOctave < 0)
1231 for (size_t i = 0; i < keypoints.size(); i++) {
1232 KeyPoint &kpt = keypoints[i];
1233 float scale = 1.f / (float)(1 << -firstOctave);
1234 kpt.octave = (kpt.octave & ~255) | ((kpt.octave + firstOctave) & 255);
1235 kpt.pt *= scale;
1236 kpt.size *= scale;
1237 }
1238
1239 if (!mask.empty()) KeyPointsFilter::runByPixelsMask(keypoints, mask);
Brian Silverman3fec6482020-01-19 17:56:20 -08001240 LOG_IF(INFO, kLogTiming);
Brian Silvermanf1196122020-01-16 00:41:54 -08001241 } else {
Brian Silverman3fec6482020-01-19 17:56:20 -08001242 buildGaussianPyramid(base, gpyr, nOctaves);
1243 LOG_IF(INFO, kLogTiming);
Brian Silvermanf1196122020-01-16 00:41:54 -08001244 // filter keypoints by mask
1245 // KeyPointsFilter::runByPixelsMask( keypoints, mask );
1246 }
1247
1248 if (_descriptors.needed()) {
Brian Silvermanf1196122020-01-16 00:41:54 -08001249 int dsize = descriptorSize();
1250 _descriptors.create((int)keypoints.size(), dsize, CV_32F);
1251 Mat descriptors = _descriptors.getMat();
1252
1253 calcDescriptors(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave);
Brian Silverman3fec6482020-01-19 17:56:20 -08001254 LOG_IF(INFO, kLogTiming);
1255 }
1256}
1257
1258Mat SIFT971_Impl::createInitialImage(const Mat &img,
1259 bool doubleImageSize) const {
1260 Mat gray, gray_fpt;
1261 if (img.channels() == 3 || img.channels() == 4) {
1262 cvtColor(img, gray, COLOR_BGR2GRAY);
1263 gray.convertTo(gray_fpt, DataType<sift_wt>::type, SIFT_FIXPT_SCALE, 0);
1264 } else {
1265 img.convertTo(gray_fpt, DataType<sift_wt>::type, SIFT_FIXPT_SCALE, 0);
1266 }
1267
1268 float sig_diff;
1269
1270 Mat maybe_doubled;
1271 if (doubleImageSize) {
1272 sig_diff = std::sqrt(
1273 std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4, 0.01));
1274 resize(gray_fpt, maybe_doubled, Size(gray_fpt.cols * 2, gray_fpt.rows * 2),
1275 0, 0, INTER_LINEAR);
1276 } else {
1277 sig_diff = std::sqrt(
1278 std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA, 0.01));
1279 maybe_doubled = gray_fpt;
1280 }
1281 if (use_fast_guassian_initial_) {
1282 Mat temp;
1283 FastGaussian(maybe_doubled, &temp, sig_diff);
1284 return temp;
1285 } else {
1286 GaussianBlur(maybe_doubled, maybe_doubled, Size(), sig_diff, sig_diff);
1287 return maybe_doubled;
Brian Silvermanf1196122020-01-16 00:41:54 -08001288 }
1289}
1290
1291} // namespace vision
1292} // namespace frc971