Last active
August 15, 2018 01:24
-
-
Save shimat/472ef5ad618ba705fd670b36c5c352c0 to your computer and use it in GitHub Desktop.
diff AKAZEFeatures.cpp 3.2.0 <-> 3.3.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/modules/features2d/src/kaze/AKAZEFeatures.cpp b/modules/features2d/src/kaze/AKAZEFeatures.cpp | |
index 77a68a5..024a5ca 100644 | |
--- a/modules/features2d/src/kaze/AKAZEFeatures.cpp | |
+++ b/modules/features2d/src/kaze/AKAZEFeatures.cpp | |
@@ -11,9 +11,14 @@ | |
#include "fed.h" | |
#include "nldiffusion_functions.h" | |
#include "utils.h" | |
+#include "opencl_kernels_features2d.hpp" | |
#include <iostream> | |
+#ifdef HAVE_OPENCL // OpenCL is not well supported | |
+#undef HAVE_OPENCL | |
+#endif | |
+ | |
// Namespaces | |
namespace cv | |
{ | |
@@ -43,10 +48,20 @@ AKAZEFeatures::AKAZEFeatures(const AKAZEOptions& options) : options_(options) { | |
* @brief This method allocates the memory for the nonlinear diffusion evolution | |
*/ | |
void AKAZEFeatures::Allocate_Memory_Evolution(void) { | |
+ CV_INSTRUMENT_REGION() | |
float rfactor = 0.0f; | |
int level_height = 0, level_width = 0; | |
+ // maximum size of the area for the descriptor computation | |
+ float smax = 0.0; | |
+ if (options_.descriptor == AKAZE::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_MLDB) { | |
+ smax = 10.0f*sqrtf(2.0f); | |
+ } | |
+ else if (options_.descriptor == AKAZE::DESCRIPTOR_KAZE_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_KAZE) { | |
+ smax = 12.0f*sqrtf(2.0f); | |
+ } | |
+ | |
// Allocate the dimension of the matrices for the evolution | |
for (int i = 0, power = 1; i <= options_.omax - 1; i++, power *= 2) { | |
rfactor = 1.0f / power; | |
@@ -60,20 +75,16 @@ void AKAZEFeatures::Allocate_Memory_Evolution(void) { | |
} | |
for (int j = 0; j < options_.nsublevels; j++) { | |
- TEvolution step; | |
- step.Lx = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Ly = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Lxx = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Lxy = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Lyy = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Lt = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Ldet = Mat::zeros(level_height, level_width, CV_32F); | |
- step.Lsmooth = Mat::zeros(level_height, level_width, CV_32F); | |
+ Evolution step; | |
+ step.size = Size(level_width, level_height); | |
step.esigma = options_.soffset*pow(2.f, (float)(j) / (float)(options_.nsublevels) + i); | |
- step.sigma_size = fRound(step.esigma); | |
- step.etime = 0.5f*(step.esigma*step.esigma); | |
+ step.sigma_size = cvRound(step.esigma * options_.derivative_factor / power); // In fact sigma_size only depends on j | |
+ step.etime = 0.5f * (step.esigma * step.esigma); | |
step.octave = i; | |
step.sublevel = j; | |
+ step.octave_ratio = (float)power; | |
+ step.border = cvRound(smax * step.sigma_size) + 1; | |
+ | |
evolution_.push_back(step); | |
} | |
} | |
@@ -93,363 +104,758 @@ void AKAZEFeatures::Allocate_Memory_Evolution(void) { | |
/* ************************************************************************* */ | |
/** | |
+ * @brief Computes kernel size for Gaussian smoothing if the image | |
+ * @param sigma Kernel standard deviation | |
+ * @returns kernel size | |
+ */ | |
+static inline int getGaussianKernelSize(float sigma) { | |
+ // Compute an appropriate kernel size according to the specified sigma | |
+ int ksize = (int)cvCeil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f))); | |
+ ksize |= 1; // kernel should be odd | |
+ return ksize; | |
+} | |
+ | |
+/* ************************************************************************* */ | |
+/** | |
+* @brief This function computes a scalar non-linear diffusion step | |
+* @param Lt Base image in the evolution | |
+* @param Lf Conductivity image | |
+* @param Lstep Output image that gives the difference between the current | |
+* Ld and the next Ld being evolved | |
+* @param row_begin row where to start | |
+* @param row_end last row to fill exclusive. the range is [row_begin, row_end). | |
+* @note Forward Euler Scheme 3x3 stencil | |
+* The function c is a scalar value that depends on the gradient norm | |
+* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy | |
+*/ | |
+static inline void | |
+nld_step_scalar_one_lane(const Mat& Lt, const Mat& Lf, Mat& Lstep, float step_size, int row_begin, int row_end) | |
+{ | |
+ CV_INSTRUMENT_REGION() | |
+ /* The labeling scheme for this five star stencil: | |
+ [ a ] | |
+ [ -1 c +1 ] | |
+ [ b ] | |
+ */ | |
+ | |
+ Lstep.create(Lt.size(), Lt.type()); | |
+ const int cols = Lt.cols - 2; | |
+ int row = row_begin; | |
+ | |
+ const float *lt_a, *lt_c, *lt_b; | |
+ const float *lf_a, *lf_c, *lf_b; | |
+ float *dst; | |
+ float step_r = 0.f; | |
+ | |
+ // Process the top row | |
+ if (row == 0) { | |
+ lt_c = Lt.ptr<float>(0) + 1; /* Skip the left-most column by +1 */ | |
+ lf_c = Lf.ptr<float>(0) + 1; | |
+ lt_b = Lt.ptr<float>(1) + 1; | |
+ lf_b = Lf.ptr<float>(1) + 1; | |
+ | |
+ // fill the corner to prevent uninitialized values | |
+ dst = Lstep.ptr<float>(0); | |
+ dst[0] = 0.0f; | |
+ ++dst; | |
+ | |
+ for (int j = 0; j < cols; j++) { | |
+ step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) + | |
+ (lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) + | |
+ (lf_c[j] + lf_b[j ])*(lt_b[j ] - lt_c[j]); | |
+ dst[j] = step_r * step_size; | |
+ } | |
+ | |
+ // fill the corner to prevent uninitialized values | |
+ dst[cols] = 0.0f; | |
+ ++row; | |
+ } | |
+ | |
+ // Process the middle rows | |
+ int middle_end = std::min(Lt.rows - 1, row_end); | |
+ for (; row < middle_end; ++row) | |
+ { | |
+ lt_a = Lt.ptr<float>(row - 1); | |
+ lf_a = Lf.ptr<float>(row - 1); | |
+ lt_c = Lt.ptr<float>(row ); | |
+ lf_c = Lf.ptr<float>(row ); | |
+ lt_b = Lt.ptr<float>(row + 1); | |
+ lf_b = Lf.ptr<float>(row + 1); | |
+ dst = Lstep.ptr<float>(row); | |
+ | |
+ // The left-most column | |
+ step_r = (lf_c[0] + lf_c[1])*(lt_c[1] - lt_c[0]) + | |
+ (lf_c[0] + lf_b[0])*(lt_b[0] - lt_c[0]) + | |
+ (lf_c[0] + lf_a[0])*(lt_a[0] - lt_c[0]); | |
+ dst[0] = step_r * step_size; | |
+ | |
+ lt_a++; lt_c++; lt_b++; | |
+ lf_a++; lf_c++; lf_b++; | |
+ dst++; | |
+ | |
+ // The middle columns | |
+ for (int j = 0; j < cols; j++) | |
+ { | |
+ step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) + | |
+ (lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) + | |
+ (lf_c[j] + lf_b[j ])*(lt_b[j ] - lt_c[j]) + | |
+ (lf_c[j] + lf_a[j ])*(lt_a[j ] - lt_c[j]); | |
+ dst[j] = step_r * step_size; | |
+ } | |
+ | |
+ // The right-most column | |
+ step_r = (lf_c[cols] + lf_c[cols - 1])*(lt_c[cols - 1] - lt_c[cols]) + | |
+ (lf_c[cols] + lf_b[cols ])*(lt_b[cols ] - lt_c[cols]) + | |
+ (lf_c[cols] + lf_a[cols ])*(lt_a[cols ] - lt_c[cols]); | |
+ dst[cols] = step_r * step_size; | |
+ } | |
+ | |
+ // Process the bottom row (row == Lt.rows - 1) | |
+ if (row_end == Lt.rows) { | |
+ lt_a = Lt.ptr<float>(row - 1) + 1; /* Skip the left-most column by +1 */ | |
+ lf_a = Lf.ptr<float>(row - 1) + 1; | |
+ lt_c = Lt.ptr<float>(row ) + 1; | |
+ lf_c = Lf.ptr<float>(row ) + 1; | |
+ | |
+ // fill the corner to prevent uninitialized values | |
+ dst = Lstep.ptr<float>(row); | |
+ dst[0] = 0.0f; | |
+ ++dst; | |
+ | |
+ for (int j = 0; j < cols; j++) { | |
+ step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) + | |
+ (lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) + | |
+ (lf_c[j] + lf_a[j ])*(lt_a[j ] - lt_c[j]); | |
+ dst[j] = step_r * step_size; | |
+ } | |
+ | |
+ // fill the corner to prevent uninitialized values | |
+ dst[cols] = 0.0f; | |
+ } | |
+} | |
+ | |
+class NonLinearScalarDiffusionStep : public ParallelLoopBody | |
+{ | |
+public: | |
+ NonLinearScalarDiffusionStep(const Mat& Lt, const Mat& Lf, Mat& Lstep, float step_size) | |
+ : Lt_(&Lt), Lf_(&Lf), Lstep_(&Lstep), step_size_(step_size) | |
+ {} | |
+ | |
+ void operator()(const Range& range) const | |
+ { | |
+ nld_step_scalar_one_lane(*Lt_, *Lf_, *Lstep_, step_size_, range.start, range.end); | |
+ } | |
+ | |
+private: | |
+ const Mat* Lt_; | |
+ const Mat* Lf_; | |
+ Mat* Lstep_; | |
+ float step_size_; | |
+}; | |
+ | |
+#ifdef HAVE_OPENCL | |
+static inline bool | |
+ocl_non_linear_diffusion_step(InputArray Lt_, InputArray Lf_, OutputArray Lstep_, float step_size) | |
+{ | |
+ if (!Lt_.isContinuous()) | |
+ return false; | |
+ | |
+ UMat Lt = Lt_.getUMat(), Lf = Lf_.getUMat(), Lstep = Lstep_.getUMat(); | |
+ | |
+ size_t globalSize[] = {(size_t)Lt.cols, (size_t)Lt.rows}; | |
+ | |
+ ocl::Kernel ker("AKAZE_nld_step_scalar", ocl::features2d::akaze_oclsrc); | |
+ if (ker.empty()) | |
+ return false; | |
+ | |
+ return ker.args( | |
+ ocl::KernelArg::ReadOnly(Lt), | |
+ ocl::KernelArg::PtrReadOnly(Lf), | |
+ ocl::KernelArg::PtrWriteOnly(Lstep), | |
+ step_size) | |
+ .run(2, globalSize, 0, true); | |
+} | |
+#endif // HAVE_OPENCL | |
+ | |
+static inline void | |
+non_linear_diffusion_step(InputArray Lt, InputArray Lf, OutputArray Lstep, float step_size) | |
+{ | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ Lstep.create(Lt.size(), Lt.type()); | |
+ | |
+#ifdef HAVE_OPENCL | |
+ CV_OCL_RUN(OCL_PERFORMANCE_CHECK(Lstep.isUMat()), ocl_non_linear_diffusion_step(Lt, Lf, Lstep, step_size)); | |
+#endif | |
+ | |
+ Mat Mstep = Lstep.getMat(); | |
+ parallel_for_(Range(0, Lt.rows()), NonLinearScalarDiffusionStep(Lt.getMat(), Lf.getMat(), Mstep, step_size)); | |
+} | |
+ | |
+/** | |
+ * @brief This function computes a good empirical value for the k contrast factor | |
+ * given two gradient images, the percentile (0-1), the temporal storage to hold | |
+ * gradient norms and the histogram bins | |
+ * @param Lx Horizontal gradient of the input image | |
+ * @param Ly Vertical gradient of the input image | |
+ * @param nbins Number of histogram bins | |
+ * @return k contrast factor | |
+ */ | |
+static inline float | |
+compute_kcontrast(const cv::Mat& Lx, const cv::Mat& Ly, float perc, int nbins) | |
+{ | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ CV_Assert(nbins > 2); | |
+ CV_Assert(!Lx.empty()); | |
+ | |
+ // temporary square roots of dot product | |
+ Mat modgs (Lx.rows - 2, Lx.cols - 2, CV_32F); | |
+ const int total = modgs.cols * modgs.rows; | |
+ float *modg = modgs.ptr<float>(); | |
+ float hmax = 0.0f; | |
+ | |
+ for (int i = 1; i < Lx.rows - 1; i++) { | |
+ const float *lx = Lx.ptr<float>(i) + 1; | |
+ const float *ly = Ly.ptr<float>(i) + 1; | |
+ const int cols = Lx.cols - 2; | |
+ | |
+ for (int j = 0; j < cols; j++) { | |
+ float dist = sqrtf(lx[j] * lx[j] + ly[j] * ly[j]); | |
+ *modg++ = dist; | |
+ hmax = std::max(hmax, dist); | |
+ } | |
+ } | |
+ modg = modgs.ptr<float>(); | |
+ | |
+ if (hmax == 0.0f) | |
+ return 0.03f; // e.g. a blank image | |
+ | |
+ // Compute the bin numbers: the value range [0, hmax] -> [0, nbins-1] | |
+ modgs *= (nbins - 1) / hmax; | |
+ | |
+ // Count up histogram | |
+ std::vector<int> hist(nbins, 0); | |
+ for (int i = 0; i < total; i++) | |
+ hist[(int)modg[i]]++; | |
+ | |
+ // Now find the perc of the histogram percentile | |
+ const int nthreshold = (int)((total - hist[0]) * perc); // Exclude hist[0] as background | |
+ int nelements = 0; | |
+ for (int k = 1; k < nbins; k++) { | |
+ if (nelements >= nthreshold) | |
+ return (float)hmax * k / nbins; | |
+ | |
+ nelements += hist[k]; | |
+ } | |
+ | |
+ return 0.03f; | |
+} | |
+ | |
+#ifdef HAVE_OPENCL | |
+static inline bool | |
+ocl_pm_g2(InputArray Lx_, InputArray Ly_, OutputArray Lflow_, float kcontrast) | |
+{ | |
+ UMat Lx = Lx_.getUMat(), Ly = Ly_.getUMat(), Lflow = Lflow_.getUMat(); | |
+ | |
+ int total = Lx.rows * Lx.cols; | |
+ size_t globalSize[] = {(size_t)total}; | |
+ | |
+ ocl::Kernel ker("AKAZE_pm_g2", ocl::features2d::akaze_oclsrc); | |
+ if (ker.empty()) | |
+ return false; | |
+ | |
+ return ker.args( | |
+ ocl::KernelArg::PtrReadOnly(Lx), | |
+ ocl::KernelArg::PtrReadOnly(Ly), | |
+ ocl::KernelArg::PtrWriteOnly(Lflow), | |
+ kcontrast, total) | |
+ .run(1, globalSize, 0, true); | |
+} | |
+#endif // HAVE_OPENCL | |
+ | |
+static inline void | |
+compute_diffusivity(InputArray Lx, InputArray Ly, OutputArray Lflow, float kcontrast, int diffusivity) | |
+{ | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ Lflow.create(Lx.size(), Lx.type()); | |
+ | |
+ switch (diffusivity) { | |
+ case KAZE::DIFF_PM_G1: | |
+ pm_g1(Lx, Ly, Lflow, kcontrast); | |
+ break; | |
+ case KAZE::DIFF_PM_G2: | |
+#ifdef HAVE_OPENCL | |
+ CV_OCL_RUN(OCL_PERFORMANCE_CHECK(Lflow.isUMat()), ocl_pm_g2(Lx, Ly, Lflow, kcontrast)); | |
+#endif | |
+ pm_g2(Lx, Ly, Lflow, kcontrast); | |
+ break; | |
+ case KAZE::DIFF_WEICKERT: | |
+ weickert_diffusivity(Lx, Ly, Lflow, kcontrast); | |
+ break; | |
+ case KAZE::DIFF_CHARBONNIER: | |
+ charbonnier_diffusivity(Lx, Ly, Lflow, kcontrast); | |
+ break; | |
+ default: | |
+ CV_Error(diffusivity, "Diffusivity is not supported"); | |
+ break; | |
+ } | |
+} | |
+ | |
+/** | |
* @brief This method creates the nonlinear scale space for a given image | |
* @param img Input image for which the nonlinear scale space needs to be created | |
* @return 0 if the nonlinear scale space was created successfully, -1 otherwise | |
*/ | |
-int AKAZEFeatures::Create_Nonlinear_Scale_Space(const Mat& img) | |
+void AKAZEFeatures::Create_Nonlinear_Scale_Space(InputArray img) | |
{ | |
+ CV_INSTRUMENT_REGION() | |
CV_Assert(evolution_.size() > 0); | |
- // Copy the original image to the first level of the evolution | |
- img.copyTo(evolution_[0].Lt); | |
- gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset); | |
- evolution_[0].Lt.copyTo(evolution_[0].Lsmooth); | |
+ // create first level of the evolution | |
+ int ksize = getGaussianKernelSize(options_.soffset); | |
+ GaussianBlur(img, evolution_[0].Lsmooth, Size(ksize, ksize), options_.soffset, options_.soffset, BORDER_REPLICATE); | |
+ evolution_[0].Lsmooth.copyTo(evolution_[0].Lt); | |
+ | |
+ if (evolution_.size() == 1) { | |
+ // we don't need to compute kcontrast factor | |
+ Compute_Determinant_Hessian_Response(); | |
+ return; | |
+ } | |
- // Allocate memory for the flow and step images | |
- Mat Lflow = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); | |
- Mat Lstep = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F); | |
+ // derivatives, flow and diffusion step | |
+ Mat Lx, Ly, Lsmooth, Lflow, Lstep; | |
- // First compute the kcontrast factor | |
- options_.kcontrast = compute_k_percentile(img, options_.kcontrast_percentile, 1.0f, options_.kcontrast_nbins, 0, 0); | |
+ // compute derivatives for computing k contrast | |
+ GaussianBlur(img, Lsmooth, Size(5, 5), 1.0f, 1.0f, BORDER_REPLICATE); | |
+ Scharr(Lsmooth, Lx, CV_32F, 1, 0, 1, 0, BORDER_DEFAULT); | |
+ Scharr(Lsmooth, Ly, CV_32F, 0, 1, 1, 0, BORDER_DEFAULT); | |
+ Lsmooth.release(); | |
+ // compute the kcontrast factor | |
+ float kcontrast = compute_kcontrast(Lx, Ly, options_.kcontrast_percentile, options_.kcontrast_nbins); | |
// Now generate the rest of evolution levels | |
for (size_t i = 1; i < evolution_.size(); i++) { | |
+ Evolution &e = evolution_[i]; | |
- if (evolution_[i].octave > evolution_[i - 1].octave) { | |
- halfsample_image(evolution_[i - 1].Lt, evolution_[i].Lt); | |
- options_.kcontrast = options_.kcontrast*0.75f; | |
- | |
- // Allocate memory for the resized flow and step images | |
- Lflow = Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); | |
- Lstep = Mat::zeros(evolution_[i].Lt.rows, evolution_[i].Lt.cols, CV_32F); | |
+ if (e.octave > evolution_[i - 1].octave) { | |
+ // new octave will be half the size | |
+ resize(evolution_[i - 1].Lt, e.Lt, e.size, 0, 0, INTER_AREA); | |
+ kcontrast *= 0.75f; | |
} | |
else { | |
- evolution_[i - 1].Lt.copyTo(evolution_[i].Lt); | |
+ evolution_[i - 1].Lt.copyTo(e.Lt); | |
} | |
- gaussian_2D_convolution(evolution_[i].Lt, evolution_[i].Lsmooth, 0, 0, 1.0f); | |
+ GaussianBlur(e.Lt, e.Lsmooth, Size(5, 5), 1.0f, 1.0f, BORDER_REPLICATE); | |
// Compute the Gaussian derivatives Lx and Ly | |
- image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Lx, 1, 0); | |
- image_derivatives_scharr(evolution_[i].Lsmooth, evolution_[i].Ly, 0, 1); | |
+ Scharr(e.Lsmooth, Lx, CV_32F, 1, 0, 1.0, 0, BORDER_DEFAULT); | |
+ Scharr(e.Lsmooth, Ly, CV_32F, 0, 1, 1.0, 0, BORDER_DEFAULT); | |
// Compute the conductivity equation | |
- switch (options_.diffusivity) { | |
- case KAZE::DIFF_PM_G1: | |
- pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); | |
- break; | |
- case KAZE::DIFF_PM_G2: | |
- pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); | |
- break; | |
- case KAZE::DIFF_WEICKERT: | |
- weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); | |
- break; | |
- case KAZE::DIFF_CHARBONNIER: | |
- charbonnier_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast); | |
- break; | |
- default: | |
- CV_Error(options_.diffusivity, "Diffusivity is not supported"); | |
- break; | |
- } | |
- | |
- // Perform FED n inner steps | |
- for (int j = 0; j < nsteps_[i - 1]; j++) { | |
- nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]); | |
+ compute_diffusivity(Lx, Ly, Lflow, kcontrast, options_.diffusivity); | |
+ | |
+ // Perform Fast Explicit Diffusion on Lt | |
+ std::vector<float> &tsteps = tsteps_[i - 1]; | |
+ for (size_t j = 0; j < tsteps.size(); j++) { | |
+ const float step_size = tsteps[j] * 0.5f; | |
+ non_linear_diffusion_step(e.Lt, Lflow, Lstep, step_size); | |
+ add(e.Lt, Lstep, e.Lt); | |
} | |
} | |
- return 0; | |
+ Compute_Determinant_Hessian_Response(); | |
} | |
/* ************************************************************************* */ | |
+ | |
+#ifdef HAVE_OPENCL | |
+static inline bool | |
+ocl_compute_determinant(InputArray Lxx_, InputArray Lxy_, InputArray Lyy_, OutputArray Ldet_, float sigma) | |
+{ | |
+ UMat Lxx = Lxx_.getUMat(), Lxy = Lxy_.getUMat(), Lyy = Lyy_.getUMat(), Ldet = Ldet_.getUMat(); | |
+ | |
+ const int total = Lxx.rows * Lxx.cols; | |
+ size_t globalSize[] = {(size_t)total}; | |
+ | |
+ ocl::Kernel ker("AKAZE_compute_determinant", ocl::features2d::akaze_oclsrc); | |
+ if (ker.empty()) | |
+ return false; | |
+ | |
+ return ker.args( | |
+ ocl::KernelArg::PtrReadOnly(Lxx), | |
+ ocl::KernelArg::PtrReadOnly(Lxy), | |
+ ocl::KernelArg::PtrReadOnly(Lyy), | |
+ ocl::KernelArg::PtrWriteOnly(Ldet), | |
+ sigma, total) | |
+ .run(1, globalSize, 0, true); | |
+} | |
+#endif // HAVE_OPENCL | |
+ | |
/** | |
- * @brief This method selects interesting keypoints through the nonlinear scale space | |
- * @param kpts Vector of detected keypoints | |
+ * @brief Compute determinant from hessians | |
+ * @details Compute Ldet by (Lxx.mul(Lyy) - Lxy.mul(Lxy)) * sigma | |
+ * | |
+ * @param Lxx spatial derivates | |
+ * @param Lxy spatial derivates | |
+ * @param Lyy spatial derivates | |
+ * @param Ldet output determinant | |
+ * @param sigma determinant will be scaled by this sigma | |
*/ | |
-void AKAZEFeatures::Feature_Detection(std::vector<KeyPoint>& kpts) | |
+static inline void compute_determinant(InputArray Lxx, InputArray Lxy, InputArray Lyy, OutputArray Ldet, float sigma) | |
{ | |
- kpts.clear(); | |
- Compute_Determinant_Hessian_Response(); | |
- Find_Scale_Space_Extrema(kpts); | |
- Do_Subpixel_Refinement(kpts); | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ Ldet.create(Lxx.size(), Lxx.type()); | |
+ | |
+#ifdef HAVE_OPENCL | |
+ CV_OCL_RUN(OCL_PERFORMANCE_CHECK(Ldet.isUMat()), ocl_compute_determinant(Lxx, Lxy, Lyy, Ldet, sigma)); | |
+#endif | |
+ | |
+ // output determinant | |
+ Mat Mxx = Lxx.getMat(), Mxy = Lxy.getMat(), Myy = Lyy.getMat(), Mdet = Ldet.getMat(); | |
+ const int W = Mxx.cols, H = Mxx.rows; | |
+ for (int y = 0; y < H; y++) | |
+ { | |
+ float *lxx = Mxx.ptr<float>(y); | |
+ float *lxy = Mxy.ptr<float>(y); | |
+ float *lyy = Myy.ptr<float>(y); | |
+ float *ldet = Mdet.ptr<float>(y); | |
+ for (int x = 0; x < W; x++) | |
+ { | |
+ ldet[x] = (lxx[x] * lyy[x] - lxy[x] * lxy[x]) * sigma; | |
+ } | |
+ } | |
} | |
-/* ************************************************************************* */ | |
-class MultiscaleDerivativesAKAZEInvoker : public ParallelLoopBody | |
+class DeterminantHessianResponse : public ParallelLoopBody | |
{ | |
public: | |
- explicit MultiscaleDerivativesAKAZEInvoker(std::vector<TEvolution>& ev, const AKAZEOptions& opt) | |
+ explicit DeterminantHessianResponse(std::vector<Evolution>& ev) | |
: evolution_(&ev) | |
- , options_(opt) | |
{ | |
} | |
void operator()(const Range& range) const | |
{ | |
- std::vector<TEvolution>& evolution = *evolution_; | |
+ Mat Lxx, Lxy, Lyy; | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- float ratio = (float)fastpow(2, evolution[i].octave); | |
- int sigma_size_ = fRound(evolution[i].esigma * options_.derivative_factor / ratio); | |
- | |
- compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, sigma_size_); | |
- compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, sigma_size_); | |
- compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, sigma_size_); | |
- compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, sigma_size_); | |
- compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, sigma_size_); | |
- | |
- evolution[i].Lx = evolution[i].Lx*((sigma_size_)); | |
- evolution[i].Ly = evolution[i].Ly*((sigma_size_)); | |
- evolution[i].Lxx = evolution[i].Lxx*((sigma_size_)*(sigma_size_)); | |
- evolution[i].Lxy = evolution[i].Lxy*((sigma_size_)*(sigma_size_)); | |
- evolution[i].Lyy = evolution[i].Lyy*((sigma_size_)*(sigma_size_)); | |
+ Evolution &e = (*evolution_)[i]; | |
+ | |
+ // we cannot use cv:Scharr here, because we need to handle also | |
+ // kernel sizes other than 3, by default we are using 9x9, 5x5 and 7x7 | |
+ | |
+ // compute kernels | |
+ Mat DxKx, DxKy, DyKx, DyKy; | |
+ compute_derivative_kernels(DxKx, DxKy, 1, 0, e.sigma_size); | |
+ compute_derivative_kernels(DyKx, DyKy, 0, 1, e.sigma_size); | |
+ | |
+ // compute the multiscale derivatives | |
+ sepFilter2D(e.Lsmooth, e.Lx, CV_32F, DxKx, DxKy); | |
+ sepFilter2D(e.Lx, Lxx, CV_32F, DxKx, DxKy); | |
+ sepFilter2D(e.Lx, Lxy, CV_32F, DyKx, DyKy); | |
+ sepFilter2D(e.Lsmooth, e.Ly, CV_32F, DyKx, DyKy); | |
+ sepFilter2D(e.Ly, Lyy, CV_32F, DyKx, DyKy); | |
+ | |
+ // free Lsmooth to same some space in the pyramid, it is not needed anymore | |
+ e.Lsmooth.release(); | |
+ | |
+ // compute determinant scaled by sigma | |
+ float sigma_size_quat = (float)(e.sigma_size * e.sigma_size * e.sigma_size * e.sigma_size); | |
+ compute_determinant(Lxx, Lxy, Lyy, e.Ldet, sigma_size_quat); | |
} | |
} | |
private: | |
- std::vector<TEvolution>* evolution_; | |
- AKAZEOptions options_; | |
+ std::vector<Evolution>* evolution_; | |
}; | |
-/* ************************************************************************* */ | |
-/** | |
- * @brief This method computes the multiscale derivatives for the nonlinear scale space | |
- */ | |
-void AKAZEFeatures::Compute_Multiscale_Derivatives(void) | |
-{ | |
- parallel_for_(Range(0, (int)evolution_.size()), | |
- MultiscaleDerivativesAKAZEInvoker(evolution_, options_)); | |
-} | |
-/* ************************************************************************* */ | |
/** | |
* @brief This method computes the feature detector response for the nonlinear scale space | |
* @note We use the Hessian determinant as the feature detector response | |
*/ | |
void AKAZEFeatures::Compute_Determinant_Hessian_Response(void) { | |
+ CV_INSTRUMENT_REGION() | |
- // Firstly compute the multiscale derivatives | |
- Compute_Multiscale_Derivatives(); | |
- | |
- for (size_t i = 0; i < evolution_.size(); i++) | |
- { | |
- for (int ix = 0; ix < evolution_[i].Ldet.rows; ix++) | |
- { | |
- for (int jx = 0; jx < evolution_[i].Ldet.cols; jx++) | |
- { | |
- float lxx = *(evolution_[i].Lxx.ptr<float>(ix)+jx); | |
- float lxy = *(evolution_[i].Lxy.ptr<float>(ix)+jx); | |
- float lyy = *(evolution_[i].Lyy.ptr<float>(ix)+jx); | |
- *(evolution_[i].Ldet.ptr<float>(ix)+jx) = (lxx*lyy - lxy*lxy); | |
- } | |
- } | |
+ if (ocl::useOpenCL()) { | |
+ DeterminantHessianResponse body (evolution_); | |
+ body(Range(0, (int)evolution_.size())); | |
+ } else { | |
+ parallel_for_(Range(0, (int)evolution_.size()), DeterminantHessianResponse(evolution_)); | |
} | |
} | |
/* ************************************************************************* */ | |
+ | |
/** | |
- * @brief This method finds extrema in the nonlinear scale space | |
+ * @brief This method selects interesting keypoints through the nonlinear scale space | |
* @param kpts Vector of detected keypoints | |
*/ | |
-void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector<KeyPoint>& kpts) | |
+void AKAZEFeatures::Feature_Detection(std::vector<KeyPoint>& kpts) | |
{ | |
+ CV_INSTRUMENT_REGION() | |
- float value = 0.0; | |
- float dist = 0.0, ratio = 0.0, smax = 0.0; | |
- int npoints = 0, id_repeated = 0; | |
- int sigma_size_ = 0, left_x = 0, right_x = 0, up_y = 0, down_y = 0; | |
- bool is_extremum = false, is_repeated = false, is_out = false; | |
- KeyPoint point; | |
- vector<KeyPoint> kpts_aux; | |
+ kpts.clear(); | |
+ std::vector<Mat> keypoints_by_layers; | |
+ Find_Scale_Space_Extrema(keypoints_by_layers); | |
+ Do_Subpixel_Refinement(keypoints_by_layers, kpts); | |
+} | |
- // Set maximum size | |
- if (options_.descriptor == AKAZE::DESCRIPTOR_MLDB_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_MLDB) { | |
- smax = 10.0f*sqrtf(2.0f); | |
- } | |
- else if (options_.descriptor == AKAZE::DESCRIPTOR_KAZE_UPRIGHT || options_.descriptor == AKAZE::DESCRIPTOR_KAZE) { | |
- smax = 12.0f*sqrtf(2.0f); | |
+/** | |
+ * @brief This method searches v for a neighbor point of the point candidate p | |
+ * @param x Coordinates of the keypoint candidate to search a neighbor | |
+ * @param y Coordinates of the keypoint candidate to search a neighbor | |
+ * @param mask Matrix holding keypoints positions | |
+ * @param search_radius neighbour radius for searching keypoints | |
+ * @param idx The index to mask, pointing to keypoint found. | |
+ * @return true if a neighbor point is found; false otherwise | |
+ */ | |
+static inline bool | |
+find_neighbor_point(const int x, const int y, const Mat &mask, const int search_radius, int &idx) | |
+{ | |
+ // search neighborhood for keypoints | |
+ for (int i = y - search_radius; i < y + search_radius; ++i) { | |
+ const uchar *curr = mask.ptr<uchar>(i); | |
+ for (int j = x - search_radius; j < x + search_radius; ++j) { | |
+ if (curr[j] == 0) { | |
+ continue; // skip non-keypoint | |
+ } | |
+ // fine-compare with L2 metric (L2 is smaller than our search window) | |
+ int dx = j - x; | |
+ int dy = i - y; | |
+ if (dx * dx + dy * dy <= search_radius * search_radius) { | |
+ idx = i * mask.cols + j; | |
+ return true; | |
+ } | |
+ } | |
} | |
- for (size_t i = 0; i < evolution_.size(); i++) { | |
- float* prev = evolution_[i].Ldet.ptr<float>(0); | |
- float* curr = evolution_[i].Ldet.ptr<float>(1); | |
- for (int ix = 1; ix < evolution_[i].Ldet.rows - 1; ix++) { | |
- float* next = evolution_[i].Ldet.ptr<float>(ix + 1); | |
- | |
- for (int jx = 1; jx < evolution_[i].Ldet.cols - 1; jx++) { | |
- is_extremum = false; | |
- is_repeated = false; | |
- is_out = false; | |
- value = *(evolution_[i].Ldet.ptr<float>(ix)+jx); | |
- | |
- // Filter the points with the detector threshold | |
- if (value > options_.dthreshold && value >= options_.min_dthreshold && | |
- value > curr[jx-1] && | |
- value > curr[jx+1] && | |
- value > prev[jx-1] && | |
- value > prev[jx] && | |
- value > prev[jx+1] && | |
- value > next[jx-1] && | |
- value > next[jx] && | |
- value > next[jx+1]) { | |
- | |
- is_extremum = true; | |
- point.response = fabs(value); | |
- point.size = evolution_[i].esigma*options_.derivative_factor; | |
- point.octave = (int)evolution_[i].octave; | |
- point.class_id = (int)i; | |
- ratio = (float)fastpow(2, point.octave); | |
- sigma_size_ = fRound(point.size / ratio); | |
- point.pt.x = static_cast<float>(jx); | |
- point.pt.y = static_cast<float>(ix); | |
- | |
- // Compare response with the same and lower scale | |
- for (size_t ik = 0; ik < kpts_aux.size(); ik++) { | |
- | |
- if ((point.class_id - 1) == kpts_aux[ik].class_id || | |
- point.class_id == kpts_aux[ik].class_id) { | |
- float distx = point.pt.x*ratio - kpts_aux[ik].pt.x; | |
- float disty = point.pt.y*ratio - kpts_aux[ik].pt.y; | |
- dist = distx * distx + disty * disty; | |
- if (dist <= point.size * point.size) { | |
- if (point.response > kpts_aux[ik].response) { | |
- id_repeated = (int)ik; | |
- is_repeated = true; | |
- } | |
- else { | |
- is_extremum = false; | |
- } | |
- break; | |
- } | |
+ return false; | |
+} | |
+ | |
+/** | |
+ * @brief Find keypoints in parallel for each pyramid layer | |
+ */ | |
+class FindKeypointsSameScale : public ParallelLoopBody | |
+{ | |
+public: | |
+ explicit FindKeypointsSameScale(const std::vector<Evolution>& ev, | |
+ std::vector<Mat>& kpts, float dthreshold) | |
+ : evolution_(&ev), keypoints_by_layers_(&kpts), dthreshold_(dthreshold) | |
+ {} | |
+ | |
+ void operator()(const Range& range) const | |
+ { | |
+ for (int i = range.start; i < range.end; i++) | |
+ { | |
+ const Evolution &e = (*evolution_)[i]; | |
+ Mat &kpts = (*keypoints_by_layers_)[i]; | |
+ // this mask will hold positions of keypoints in this level | |
+ kpts = Mat::zeros(e.Ldet.size(), CV_8UC1); | |
+ | |
+ // if border is too big we shouldn't search any keypoints | |
+ if (e.border + 1 >= e.Ldet.rows) | |
+ continue; | |
+ | |
+ const float * prev = e.Ldet.ptr<float>(e.border - 1); | |
+ const float * curr = e.Ldet.ptr<float>(e.border ); | |
+ const float * next = e.Ldet.ptr<float>(e.border + 1); | |
+ const float * ldet = e.Ldet.ptr<float>(); | |
+ uchar *mask = kpts.ptr<uchar>(); | |
+ const int search_radius = e.sigma_size; // size of keypoint in this level | |
+ | |
+ for (int y = e.border; y < e.Ldet.rows - e.border; y++) { | |
+ for (int x = e.border; x < e.Ldet.cols - e.border; x++) { | |
+ const float value = curr[x]; | |
+ | |
+ // Filter the points with the detector threshold | |
+ if (value <= dthreshold_) | |
+ continue; | |
+ if (value <= curr[x-1] || value <= curr[x+1]) | |
+ continue; | |
+ if (value <= prev[x-1] || value <= prev[x ] || value <= prev[x+1]) | |
+ continue; | |
+ if (value <= next[x-1] || value <= next[x ] || value <= next[x+1]) | |
+ continue; | |
+ | |
+ int idx = 0; | |
+ // Compare response with the same scale | |
+ if (find_neighbor_point(x, y, kpts, search_radius, idx)) { | |
+ if (value > ldet[idx]) { | |
+ mask[idx] = 0; // clear old point - we have better candidate now | |
+ } else { | |
+ continue; // there already is a better keypoint | |
} | |
} | |
- // Check out of bounds | |
- if (is_extremum == true) { | |
+ kpts.at<uchar>(y, x) = 1; // we have a new keypoint | |
+ } | |
- // Check that the point is under the image limits for the descriptor computation | |
- left_x = fRound(point.pt.x - smax*sigma_size_) - 1; | |
- right_x = fRound(point.pt.x + smax*sigma_size_) + 1; | |
- up_y = fRound(point.pt.y - smax*sigma_size_) - 1; | |
- down_y = fRound(point.pt.y + smax*sigma_size_) + 1; | |
+ prev = curr; | |
+ curr = next; | |
+ next += e.Ldet.cols; | |
+ } | |
+ } | |
+ } | |
- if (left_x < 0 || right_x >= evolution_[i].Ldet.cols || | |
- up_y < 0 || down_y >= evolution_[i].Ldet.rows) { | |
- is_out = true; | |
- } | |
+private: | |
+ const std::vector<Evolution>* evolution_; | |
+ std::vector<Mat>* keypoints_by_layers_; | |
+ float dthreshold_; ///< Detector response threshold to accept point | |
+}; | |
- if (is_out == false) { | |
- if (is_repeated == false) { | |
- point.pt.x = (float)(point.pt.x*ratio + .5*(ratio-1.0)); | |
- point.pt.y = (float)(point.pt.y*ratio + .5*(ratio-1.0)); | |
- kpts_aux.push_back(point); | |
- npoints++; | |
- } | |
- else { | |
- point.pt.x = (float)(point.pt.x*ratio + .5*(ratio-1.0)); | |
- point.pt.y = (float)(point.pt.y*ratio + .5*(ratio-1.0)); | |
- kpts_aux[id_repeated] = point; | |
- } | |
- } // if is_out | |
- } //if is_extremum | |
+/** | |
+ * @brief This method finds extrema in the nonlinear scale space | |
+ * @param keypoints_by_layers Output vectors of detected keypoints; one vector for each evolution level | |
+ */ | |
+void AKAZEFeatures::Find_Scale_Space_Extrema(std::vector<Mat>& keypoints_by_layers) | |
+{ | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ keypoints_by_layers.resize(evolution_.size()); | |
+ | |
+ // find points in the same level | |
+ parallel_for_(Range(0, (int)evolution_.size()), | |
+ FindKeypointsSameScale(evolution_, keypoints_by_layers, options_.dthreshold)); | |
+ | |
+ // Filter points with the lower scale level | |
+ for (size_t i = 1; i < keypoints_by_layers.size(); i++) { | |
+ // constants for this level | |
+ const Mat &keypoints = keypoints_by_layers[i]; | |
+ const uchar *const kpts = keypoints_by_layers[i].ptr<uchar>(); | |
+ uchar *const kpts_prev = keypoints_by_layers[i-1].ptr<uchar>(); | |
+ const float *const ldet = evolution_[i].Ldet.ptr<float>(); | |
+ const float *const ldet_prev = evolution_[i-1].Ldet.ptr<float>(); | |
+ // ratios are just powers of 2 | |
+ const int diff_ratio = (int)evolution_[i].octave_ratio / (int)evolution_[i-1].octave_ratio; | |
+ const int search_radius = evolution_[i].sigma_size * diff_ratio; // size of keypoint in this level | |
+ | |
+ size_t j = 0; | |
+ for (int y = 0; y < keypoints.rows; y++) { | |
+ for (int x = 0; x < keypoints.cols; x++, j++) { | |
+ if (kpts[j] == 0) { | |
+ continue; // skip non-keypoints | |
} | |
- } // for jx | |
- prev = curr; | |
- curr = next; | |
- } // for ix | |
- } // for i | |
- | |
- // Now filter points with the upper scale level | |
- for (size_t i = 0; i < kpts_aux.size(); i++) { | |
- | |
- is_repeated = false; | |
- const KeyPoint& pt = kpts_aux[i]; | |
- for (size_t j = i + 1; j < kpts_aux.size(); j++) { | |
- | |
- // Compare response with the upper scale | |
- if ((pt.class_id + 1) == kpts_aux[j].class_id) { | |
- float distx = pt.pt.x - kpts_aux[j].pt.x; | |
- float disty = pt.pt.y - kpts_aux[j].pt.y; | |
- dist = distx * distx + disty * disty; | |
- if (dist <= pt.size * pt.size) { | |
- if (pt.response < kpts_aux[j].response) { | |
- is_repeated = true; | |
- break; | |
+ int idx = 0; | |
+ // project point to lower scale layer | |
+ const int p_x = x * diff_ratio; | |
+ const int p_y = y * diff_ratio; | |
+ if (find_neighbor_point(p_x, p_y, keypoints_by_layers[i-1], search_radius, idx)) { | |
+ if (ldet[j] > ldet_prev[idx]) { | |
+ kpts_prev[idx] = 0; // clear keypoint in lower layer | |
} | |
+ // else this pt may be pruned by the upper scale | |
} | |
} | |
} | |
+ } | |
- if (is_repeated == false) | |
- kpts.push_back(pt); | |
+ // Now filter points with the upper scale level (the other direction) | |
+ for (int i = (int)keypoints_by_layers.size() - 2; i >= 0; i--) { | |
+ // constants for this level | |
+ const Mat &keypoints = keypoints_by_layers[i]; | |
+ const uchar *const kpts = keypoints_by_layers[i].ptr<uchar>(); | |
+ uchar *const kpts_next = keypoints_by_layers[i+1].ptr<uchar>(); | |
+ const float *const ldet = evolution_[i].Ldet.ptr<float>(); | |
+ const float *const ldet_next = evolution_[i+1].Ldet.ptr<float>(); | |
+ // ratios are just powers of 2, i+1 ratio is always greater or equal to i | |
+ const int diff_ratio = (int)evolution_[i+1].octave_ratio / (int)evolution_[i].octave_ratio; | |
+ const int search_radius = evolution_[i+1].sigma_size; // size of keypoints in upper level | |
+ | |
+ size_t j = 0; | |
+ for (int y = 0; y < keypoints.rows; y++) { | |
+ for (int x = 0; x < keypoints.cols; x++, j++) { | |
+ if (kpts[j] == 0) { | |
+ continue; // skip non-keypoints | |
+ } | |
+ int idx = 0; | |
+ // project point to upper scale layer | |
+ const int p_x = x / diff_ratio; | |
+ const int p_y = y / diff_ratio; | |
+ if (find_neighbor_point(p_x, p_y, keypoints_by_layers[i+1], search_radius, idx)) { | |
+ if (ldet[j] > ldet_next[idx]) { | |
+ kpts_next[idx] = 0; // clear keypoint in upper layer | |
+ } | |
+ } | |
+ } | |
+ } | |
} | |
} | |
/* ************************************************************************* */ | |
/** | |
* @brief This method performs subpixel refinement of the detected keypoints | |
- * @param kpts Vector of detected keypoints | |
+ * @param keypoints_by_layers Input vectors of detected keypoints, sorted by evolution levels | |
+ * @param kpts Output vector of the final refined keypoints | |
*/ | |
-void AKAZEFeatures::Do_Subpixel_Refinement(std::vector<KeyPoint>& kpts) | |
+void AKAZEFeatures::Do_Subpixel_Refinement( | |
+ std::vector<Mat>& keypoints_by_layers, std::vector<KeyPoint>& output_keypoints) | |
{ | |
- float Dx = 0.0, Dy = 0.0, ratio = 0.0; | |
- float Dxx = 0.0, Dyy = 0.0, Dxy = 0.0; | |
- int x = 0, y = 0; | |
- Matx22f A(0, 0, 0, 0); | |
- Vec2f b(0, 0); | |
- Vec2f dst(0, 0); | |
- | |
- for (size_t i = 0; i < kpts.size(); i++) { | |
- ratio = (float)fastpow(2, kpts[i].octave); | |
- x = fRound(kpts[i].pt.x / ratio); | |
- y = fRound(kpts[i].pt.y / ratio); | |
- | |
- // Compute the gradient | |
- Dx = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x + 1) | |
- - *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x - 1)); | |
- Dy = (0.5f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x) | |
- - *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x)); | |
- | |
- // Compute the Hessian | |
- Dxx = (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x + 1) | |
- + *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x - 1) | |
- - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x))); | |
- | |
- Dyy = (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x) | |
- + *(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x) | |
- - 2.0f*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y)+x))); | |
- | |
- Dxy = (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x + 1) | |
- + (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x - 1))) | |
- - (0.25f)*(*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y - 1) + x + 1) | |
- + (*(evolution_[kpts[i].class_id].Ldet.ptr<float>(y + 1) + x - 1))); | |
- | |
- // Solve the linear system | |
- A(0, 0) = Dxx; | |
- A(1, 1) = Dyy; | |
- A(0, 1) = A(1, 0) = Dxy; | |
- b(0) = -Dx; | |
- b(1) = -Dy; | |
- | |
- solve(A, b, dst, DECOMP_LU); | |
- | |
- if (fabs(dst(0)) <= 1.0f && fabs(dst(1)) <= 1.0f) { | |
- kpts[i].pt.x = x + dst(0); | |
- kpts[i].pt.y = y + dst(1); | |
- int power = fastpow(2, evolution_[kpts[i].class_id].octave); | |
- kpts[i].pt.x = (float)(kpts[i].pt.x*power + .5*(power-1)); | |
- kpts[i].pt.y = (float)(kpts[i].pt.y*power + .5*(power-1)); | |
- kpts[i].angle = 0.0; | |
- | |
- // In OpenCV the size of a keypoint its the diameter | |
- kpts[i].size *= 2.0f; | |
- } | |
- // Delete the point since its not stable | |
- else { | |
- kpts.erase(kpts.begin() + i); | |
- i--; | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ for (size_t i = 0; i < keypoints_by_layers.size(); i++) { | |
+ const Evolution &e = evolution_[i]; | |
+ const float * const ldet = e.Ldet.ptr<float>(); | |
+ const float ratio = e.octave_ratio; | |
+ const int cols = e.Ldet.cols; | |
+ const Mat& keypoints = keypoints_by_layers[i]; | |
+ const uchar *const kpts = keypoints.ptr<uchar>(); | |
+ | |
+ size_t j = 0; | |
+ for (int y = 0; y < keypoints.rows; y++) { | |
+ for (int x = 0; x < keypoints.cols; x++, j++) { | |
+ if (kpts[j] == 0) { | |
+ continue; // skip non-keypoints | |
+ } | |
+ | |
+ // create a new keypoint | |
+ KeyPoint kp; | |
+ kp.pt.x = x * e.octave_ratio; | |
+ kp.pt.y = y * e.octave_ratio; | |
+ kp.size = e.esigma * options_.derivative_factor; | |
+ kp.angle = -1; | |
+ kp.response = ldet[j]; | |
+ kp.octave = e.octave; | |
+ kp.class_id = static_cast<int>(i); | |
+ | |
+ // Compute the gradient | |
+ float Dx = 0.5f * (ldet[ y *cols + x + 1] - ldet[ y *cols + x - 1]); | |
+ float Dy = 0.5f * (ldet[(y + 1)*cols + x ] - ldet[(y - 1)*cols + x ]); | |
+ | |
+ // Compute the Hessian | |
+ float Dxx = ldet[ y *cols + x + 1] + ldet[ y *cols + x - 1] - 2.0f * ldet[y*cols + x]; | |
+ float Dyy = ldet[(y + 1)*cols + x ] + ldet[(y - 1)*cols + x ] - 2.0f * ldet[y*cols + x]; | |
+ float Dxy = 0.25f * (ldet[(y + 1)*cols + x + 1] + ldet[(y - 1)*cols + x - 1] - | |
+ ldet[(y - 1)*cols + x + 1] - ldet[(y + 1)*cols + x - 1]); | |
+ | |
+ // Solve the linear system | |
+ Matx22f A( Dxx, Dxy, | |
+ Dxy, Dyy ); | |
+ Vec2f b( -Dx, -Dy ); | |
+ Vec2f dst( 0.0f, 0.0f ); | |
+ solve(A, b, dst, DECOMP_LU); | |
+ | |
+ float dx = dst(0); | |
+ float dy = dst(1); | |
+ | |
+ if (fabs(dx) > 1.0f || fabs(dy) > 1.0f) | |
+ continue; // Ignore the point that is not stable | |
+ | |
+ // Refine the coordinates | |
+ kp.pt.x += dx * ratio + .5f*(ratio-1.f); | |
+ kp.pt.y += dy * ratio + .5f*(ratio-1.f); | |
+ | |
+ kp.angle = 0.0; | |
+ kp.size *= 2.0f; // In OpenCV the size of a keypoint is the diameter | |
+ | |
+ // Push the refined keypoint to the final storage | |
+ output_keypoints.push_back(kp); | |
+ } | |
} | |
} | |
} | |
@@ -459,7 +865,7 @@ void AKAZEFeatures::Do_Subpixel_Refinement(std::vector<KeyPoint>& kpts) | |
class SURF_Descriptor_Upright_64_Invoker : public ParallelLoopBody | |
{ | |
public: | |
- SURF_Descriptor_Upright_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution) | |
+ SURF_Descriptor_Upright_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<Evolution>& evolution) | |
: keypoints_(&kpts) | |
, descriptors_(&desc) | |
, evolution_(&evolution) | |
@@ -470,22 +876,22 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- Get_SURF_Descriptor_Upright_64((*keypoints_)[i], descriptors_->ptr<float>(i)); | |
+ Get_SURF_Descriptor_Upright_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols); | |
} | |
} | |
- void Get_SURF_Descriptor_Upright_64(const KeyPoint& kpt, float* desc) const; | |
+ void Get_SURF_Descriptor_Upright_64(const KeyPoint& kpt, float* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
}; | |
class SURF_Descriptor_64_Invoker : public ParallelLoopBody | |
{ | |
public: | |
- SURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution) | |
+ SURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<Evolution>& evolution) | |
: keypoints_(&kpts) | |
, descriptors_(&desc) | |
, evolution_(&evolution) | |
@@ -496,23 +902,22 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); | |
- Get_SURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i)); | |
+ Get_SURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols); | |
} | |
} | |
- void Get_SURF_Descriptor_64(const KeyPoint& kpt, float* desc) const; | |
+ void Get_SURF_Descriptor_64(const KeyPoint& kpt, float* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
}; | |
class MSURF_Upright_Descriptor_64_Invoker : public ParallelLoopBody | |
{ | |
public: | |
- MSURF_Upright_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution) | |
+ MSURF_Upright_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<Evolution>& evolution) | |
: keypoints_(&kpts) | |
, descriptors_(&desc) | |
, evolution_(&evolution) | |
@@ -523,22 +928,22 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- Get_MSURF_Upright_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i)); | |
+ Get_MSURF_Upright_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols); | |
} | |
} | |
- void Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float* desc) const; | |
+ void Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
}; | |
class MSURF_Descriptor_64_Invoker : public ParallelLoopBody | |
{ | |
public: | |
- MSURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution) | |
+ MSURF_Descriptor_64_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<Evolution>& evolution) | |
: keypoints_(&kpts) | |
, descriptors_(&desc) | |
, evolution_(&evolution) | |
@@ -549,23 +954,22 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); | |
- Get_MSURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i)); | |
+ Get_MSURF_Descriptor_64((*keypoints_)[i], descriptors_->ptr<float>(i), descriptors_->cols); | |
} | |
} | |
- void Get_MSURF_Descriptor_64(const KeyPoint& kpt, float* desc) const; | |
+ void Get_MSURF_Descriptor_64(const KeyPoint& kpt, float* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
}; | |
class Upright_MLDB_Full_Descriptor_Invoker : public ParallelLoopBody | |
{ | |
public: | |
- Upright_MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution, AKAZEOptions& options) | |
+ Upright_MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<Evolution>& evolution, AKAZEOptions& options) | |
: keypoints_(&kpts) | |
, descriptors_(&desc) | |
, evolution_(&evolution) | |
@@ -577,16 +981,16 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- Get_Upright_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i)); | |
+ Get_Upright_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols); | |
} | |
} | |
- void Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc) const; | |
+ void Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
AKAZEOptions* options_; | |
}; | |
@@ -595,7 +999,7 @@ class Upright_MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody | |
public: | |
Upright_MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint>& kpts, | |
Mat& desc, | |
- std::vector<TEvolution>& evolution, | |
+ std::vector<Evolution>& evolution, | |
AKAZEOptions& options, | |
Mat descriptorSamples, | |
Mat descriptorBits) | |
@@ -612,16 +1016,16 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- Get_Upright_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i)); | |
+ Get_Upright_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols); | |
} | |
} | |
- void Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc) const; | |
+ void Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
AKAZEOptions* options_; | |
Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. | |
@@ -631,7 +1035,7 @@ private: | |
class MLDB_Full_Descriptor_Invoker : public ParallelLoopBody | |
{ | |
public: | |
- MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<TEvolution>& evolution, AKAZEOptions& options) | |
+ MLDB_Full_Descriptor_Invoker(std::vector<KeyPoint>& kpts, Mat& desc, std::vector<Evolution>& evolution, AKAZEOptions& options) | |
: keypoints_(&kpts) | |
, descriptors_(&desc) | |
, evolution_(&evolution) | |
@@ -643,12 +1047,11 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); | |
- Get_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i)); | |
+ Get_MLDB_Full_Descriptor((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols); | |
} | |
} | |
- void Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc) const; | |
+ void Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char* desc, int desc_size) const; | |
void MLDB_Fill_Values(float* values, int sample_step, int level, | |
float xf, float yf, float co, float si, float scale) const; | |
void MLDB_Binary_Comparisons(float* values, unsigned char* desc, | |
@@ -657,7 +1060,7 @@ public: | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
AKAZEOptions* options_; | |
}; | |
@@ -666,7 +1069,7 @@ class MLDB_Descriptor_Subset_Invoker : public ParallelLoopBody | |
public: | |
MLDB_Descriptor_Subset_Invoker(std::vector<KeyPoint>& kpts, | |
Mat& desc, | |
- std::vector<TEvolution>& evolution, | |
+ std::vector<Evolution>& evolution, | |
AKAZEOptions& options, | |
Mat descriptorSamples, | |
Mat descriptorBits) | |
@@ -683,17 +1086,16 @@ public: | |
{ | |
for (int i = range.start; i < range.end; i++) | |
{ | |
- AKAZEFeatures::Compute_Main_Orientation((*keypoints_)[i], *evolution_); | |
- Get_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i)); | |
+ Get_MLDB_Descriptor_Subset((*keypoints_)[i], descriptors_->ptr<unsigned char>(i), descriptors_->cols); | |
} | |
} | |
- void Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc) const; | |
+ void Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char* desc, int desc_size) const; | |
private: | |
std::vector<KeyPoint>* keypoints_; | |
Mat* descriptors_; | |
- std::vector<TEvolution>* evolution_; | |
+ std::vector<Evolution>* evolution_; | |
AKAZEOptions* options_; | |
Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from. | |
@@ -705,28 +1107,29 @@ private: | |
* @param kpts Vector of detected keypoints | |
* @param desc Matrix to store the descriptors | |
*/ | |
-void AKAZEFeatures::Compute_Descriptors(std::vector<KeyPoint>& kpts, Mat& desc) | |
+void AKAZEFeatures::Compute_Descriptors(std::vector<KeyPoint>& kpts, OutputArray descriptors) | |
{ | |
+ CV_INSTRUMENT_REGION() | |
+ | |
for(size_t i = 0; i < kpts.size(); i++) | |
{ | |
CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast<int>(evolution_.size())); | |
} | |
// Allocate memory for the matrix with the descriptors | |
- if (options_.descriptor < AKAZE::DESCRIPTOR_MLDB_UPRIGHT) { | |
- desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1); | |
- } | |
- else { | |
- // We use the full length binary descriptor -> 486 bits | |
- if (options_.descriptor_size == 0) { | |
- int t = (6 + 36 + 120)*options_.descriptor_channels; | |
- desc = Mat::zeros((int)kpts.size(), (int)ceil(t / 8.), CV_8UC1); | |
- } | |
- else { | |
- // We use the random bit selection length binary descriptor | |
- desc = Mat::zeros((int)kpts.size(), (int)ceil(options_.descriptor_size / 8.), CV_8UC1); | |
- } | |
+ int descriptor_size = 64; | |
+ int descriptor_type = CV_32FC1; | |
+ if (options_.descriptor >= AKAZE::DESCRIPTOR_MLDB_UPRIGHT) | |
+ { | |
+ int descriptor_bits = (options_.descriptor_size == 0) | |
+ ? (6 + 36 + 120)*options_.descriptor_channels // the full length binary descriptor -> 486 bits | |
+ : options_.descriptor_size; // the random bit selection length binary descriptor | |
+ descriptor_size = divUp(descriptor_bits, 8); | |
+ descriptor_type = CV_8UC1; | |
} | |
+ descriptors.create((int)kpts.size(), descriptor_size, descriptor_type); | |
+ | |
+ Mat desc = descriptors.getMat(); | |
switch (options_.descriptor) | |
{ | |
@@ -761,12 +1164,20 @@ void AKAZEFeatures::Compute_Descriptors(std::vector<KeyPoint>& kpts, Mat& desc) | |
/* ************************************************************************* */ | |
/** | |
- * @brief This method computes the main orientation for a given keypoint | |
- * @param kpt Input keypoint | |
- * @note The orientation is computed using a similar approach as described in the | |
- * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006 | |
+ * @brief This function samples the derivative responses Lx and Ly for the points | |
+ * within the radius of 6*scale from (x0, y0), then multiply 2D Gaussian weight | |
+ * @param Lx Horizontal derivative | |
+ * @param Ly Vertical derivative | |
+ * @param x0 X-coordinate of the center point | |
+ * @param y0 Y-coordinate of the center point | |
+ * @param scale The sampling step | |
+ * @param resX Output array of the weighted horizontal derivative responses | |
+ * @param resY Output array of the weighted vertical derivative responses | |
*/ | |
-void AKAZEFeatures::Compute_Main_Orientation(KeyPoint& kpt, const std::vector<TEvolution>& evolution_) | |
+static inline | |
+void Sample_Derivative_Response_Radius6(const Mat &Lx, const Mat &Ly, | |
+ const int x0, const int y0, const int scale, | |
+ float *resX, float *resY) | |
{ | |
/* ************************************************************************* */ | |
/// Lookup table for 2d gaussian (sigma = 2.5) where (0,0) is top left and (6,6) is bottom right | |
@@ -780,79 +1191,208 @@ void AKAZEFeatures::Compute_Main_Orientation(KeyPoint& kpt, const std::vector<TE | |
{ 0.00344629f, 0.00318132f, 0.00250252f, 0.00167749f, 0.00095820f, 0.00046640f, 0.00019346f }, | |
{ 0.00142946f, 0.00131956f, 0.00103800f, 0.00069579f, 0.00039744f, 0.00019346f, 0.00008024f } | |
}; | |
+ static const struct gtable | |
+ { | |
+ float weight[109]; | |
+ int xidx[109]; | |
+ int yidx[109]; | |
- int ix = 0, iy = 0, idx = 0, s = 0, level = 0; | |
- float xf = 0.0, yf = 0.0, gweight = 0.0, ratio = 0.0; | |
- const int ang_size = 109; | |
- float resX[ang_size], resY[ang_size], Ang[ang_size]; | |
- const int id[] = { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 }; | |
+ explicit gtable(void) | |
+ { | |
+ // Generate the weight and indices by one-time initialization | |
+ int k = 0; | |
+ for (int i = -6; i <= 6; ++i) { | |
+ for (int j = -6; j <= 6; ++j) { | |
+ if (i*i + j*j < 36) { | |
+ CV_Assert(k < 109); | |
+ weight[k] = gauss25[abs(i)][abs(j)]; | |
+ yidx[k] = i; | |
+ xidx[k] = j; | |
+ ++k; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } g; | |
- // Variables for computing the dominant direction | |
- float sumX = 0.0, sumY = 0.0, max = 0.0, ang1 = 0.0, ang2 = 0.0; | |
+ CV_Assert(x0 - 6 * scale >= 0 && x0 + 6 * scale < Lx.cols); | |
+ CV_Assert(y0 - 6 * scale >= 0 && y0 + 6 * scale < Lx.rows); | |
+ for (int i = 0; i < 109; i++) | |
+ { | |
+ int y = y0 + g.yidx[i] * scale; | |
+ int x = x0 + g.xidx[i] * scale; | |
+ | |
+ float w = g.weight[i]; | |
+ resX[i] = w * Lx.at<float>(y, x); | |
+ resY[i] = w * Ly.at<float>(y, x); | |
+ } | |
+} | |
+ | |
+/** | |
+ * @brief This function sorts a[] by quantized float values | |
+ * @param a[] Input floating point array to sort | |
+ * @param n The length of a[] | |
+ * @param quantum The interval to convert a[i]'s float values to integers | |
+ * @param nkeys a[i] < nkeys * quantum | |
+ * @param idx[] Output array of the indices: a[idx[i]] forms a sorted array | |
+ * @param cum[] Output array of the starting indices of quantized floats | |
+ * @note The values of a[] in [k*quantum, (k + 1)*quantum) is labeled by | |
+ * the integer k, which is calculated by floor(a[i]/quantum). After sorting, | |
+ * the values from a[idx[cum[k]]] to a[idx[cum[k+1]-1]] are all labeled by k. | |
+ * This sorting is unstable to reduce the memory access. | |
+ */ | |
+static inline | |
+void quantized_counting_sort(const float a[], const int n, | |
+ const float quantum, const int nkeys, | |
+ int idx[/*n*/], int cum[/*nkeys + 1*/]) | |
+{ | |
+ memset(cum, 0, sizeof(cum[0]) * (nkeys + 1)); | |
+ | |
+ // Count up the quantized values | |
+ for (int i = 0; i < n; i++) | |
+ { | |
+ int b = (int)(a[i] / quantum); | |
+ if (b < 0 || b >= nkeys) | |
+ b = 0; | |
+ cum[b]++; | |
+ } | |
+ | |
+ // Compute the inclusive prefix sum i.e. the end indices; cum[nkeys] is the total | |
+ for (int i = 1; i <= nkeys; i++) | |
+ { | |
+ cum[i] += cum[i - 1]; | |
+ } | |
+ CV_Assert(cum[nkeys] == n); | |
+ | |
+ // Generate the sorted indices; cum[] becomes the exclusive prefix sum i.e. the start indices of keys | |
+ for (int i = 0; i < n; i++) | |
+ { | |
+ int b = (int)(a[i] / quantum); | |
+ if (b < 0 || b >= nkeys) | |
+ b = 0; | |
+ idx[--cum[b]] = i; | |
+ } | |
+} | |
+ | |
+/** | |
+ * @brief This function computes the main orientation for a given keypoint | |
+ * @param kpt Input keypoint | |
+ * @note The orientation is computed using a similar approach as described in the | |
+ * original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006 | |
+ */ | |
+static inline | |
+void Compute_Main_Orientation(KeyPoint& kpt, const std::vector<Evolution>& evolution) | |
+{ | |
+ // get the right evolution level for this keypoint | |
+ const Evolution& e = evolution[kpt.class_id]; | |
// Get the information from the keypoint | |
- level = kpt.class_id; | |
- ratio = (float)(1 << evolution_[level].octave); | |
- s = fRound(0.5f*kpt.size / ratio); | |
- xf = kpt.pt.x / ratio; | |
- yf = kpt.pt.y / ratio; | |
+ int scale = cvRound(0.5f * kpt.size / e.octave_ratio); | |
+ int x0 = cvRound(kpt.pt.x / e.octave_ratio); | |
+ int y0 = cvRound(kpt.pt.y / e.octave_ratio); | |
+ | |
+ // Sample derivatives responses for the points within radius of 6*scale | |
+ const int ang_size = 109; | |
+ float resX[ang_size], resY[ang_size]; | |
+ Sample_Derivative_Response_Radius6(e.Lx, e.Ly, x0, y0, scale, resX, resY); | |
+ | |
+ // Compute the angle of each gradient vector | |
+ float Ang[ang_size]; | |
+ hal::fastAtan2(resY, resX, Ang, ang_size, false); | |
+ | |
+ // Sort by the angles; angles are labeled by slices of 0.15 radian | |
+ const int slices = 42; | |
+ const float ang_step = (float)(2.0 * CV_PI / slices); | |
+ int slice[slices + 1]; | |
+ int sorted_idx[ang_size]; | |
+ quantized_counting_sort(Ang, ang_size, ang_step, slices, sorted_idx, slice); | |
+ | |
+ // Find the main angle by sliding a window of 7-slice size(=PI/3) around the keypoint | |
+ const int win = 7; | |
+ | |
+ float maxX = 0.0f, maxY = 0.0f; | |
+ for (int i = slice[0]; i < slice[win]; i++) { | |
+ const int idx = sorted_idx[i]; | |
+ maxX += resX[idx]; | |
+ maxY += resY[idx]; | |
+ } | |
+ float maxNorm = maxX * maxX + maxY * maxY; | |
- // Calculate derivatives responses for points within radius of 6*scale | |
- for (int i = -6; i <= 6; ++i) { | |
- for (int j = -6; j <= 6; ++j) { | |
- if (i*i + j*j < 36) { | |
- iy = fRound(yf + j*s); | |
- ix = fRound(xf + i*s); | |
+ for (int sn = 1; sn <= slices - win; sn++) { | |
- gweight = gauss25[id[i + 6]][id[j + 6]]; | |
- resX[idx] = gweight*(*(evolution_[level].Lx.ptr<float>(iy)+ix)); | |
- resY[idx] = gweight*(*(evolution_[level].Ly.ptr<float>(iy)+ix)); | |
+ if (slice[sn] == slice[sn - 1] && slice[sn + win] == slice[sn + win - 1]) | |
+ continue; // The contents of the window didn't change; don't repeat the computation | |
- ++idx; | |
- } | |
+ float sumX = 0.0f, sumY = 0.0f; | |
+ for (int i = slice[sn]; i < slice[sn + win]; i++) { | |
+ const int idx = sorted_idx[i]; | |
+ sumX += resX[idx]; | |
+ sumY += resY[idx]; | |
} | |
+ | |
+ float norm = sumX * sumX + sumY * sumY; | |
+ if (norm > maxNorm) | |
+ maxNorm = norm, maxX = sumX, maxY = sumY; // Found bigger one; update | |
} | |
- hal::fastAtan32f(resY, resX, Ang, ang_size, false); | |
- // Loop slides pi/3 window around feature point | |
- for (ang1 = 0; ang1 < (float)(2.0 * CV_PI); ang1 += 0.15f) { | |
- ang2 = (ang1 + (float)(CV_PI / 3.0) >(float)(2.0*CV_PI) ? ang1 - (float)(5.0*CV_PI / 3.0) : ang1 + (float)(CV_PI / 3.0)); | |
- sumX = sumY = 0.f; | |
- | |
- for (int k = 0; k < ang_size; ++k) { | |
- // Get angle from the x-axis of the sample point | |
- const float & ang = Ang[k]; | |
- | |
- // Determine whether the point is within the window | |
- if (ang1 < ang2 && ang1 < ang && ang < ang2) { | |
- sumX += resX[k]; | |
- sumY += resY[k]; | |
- } | |
- else if (ang2 < ang1 && | |
- ((ang > 0 && ang < ang2) || (ang > ang1 && ang < 2.0f*CV_PI))) { | |
- sumX += resX[k]; | |
- sumY += resY[k]; | |
- } | |
- } | |
- // if the vector produced from this window is longer than all | |
- // previous vectors then this forms the new dominant direction | |
- if (sumX*sumX + sumY*sumY > max) { | |
- // store largest orientation | |
- max = sumX*sumX + sumY*sumY; | |
- kpt.angle = getAngle(sumX, sumY) * 180.f / static_cast<float>(CV_PI); | |
+ for (int sn = slices - win + 1; sn < slices; sn++) { | |
+ int remain = sn + win - slices; | |
+ | |
+ if (slice[sn] == slice[sn - 1] && slice[remain] == slice[remain - 1]) | |
+ continue; | |
+ | |
+ float sumX = 0.0f, sumY = 0.0f; | |
+ for (int i = slice[sn]; i < slice[slices]; i++) { | |
+ const int idx = sorted_idx[i]; | |
+ sumX += resX[idx]; | |
+ sumY += resY[idx]; | |
+ } | |
+ for (int i = slice[0]; i < slice[remain]; i++) { | |
+ const int idx = sorted_idx[i]; | |
+ sumX += resX[idx]; | |
+ sumY += resY[idx]; | |
} | |
+ | |
+ float norm = sumX * sumX + sumY * sumY; | |
+ if (norm > maxNorm) | |
+ maxNorm = norm, maxX = sumX, maxY = sumY; | |
} | |
+ | |
+ // Store the final result | |
+ kpt.angle = fastAtan2(maxY, maxX); | |
} | |
-/* ************************************************************************* */ | |
+class ComputeKeypointOrientation : public ParallelLoopBody | |
+{ | |
+public: | |
+ ComputeKeypointOrientation(std::vector<KeyPoint>& kpts, | |
+ const std::vector<Evolution>& evolution) | |
+ : keypoints_(&kpts) | |
+ , evolution_(&evolution) | |
+ { | |
+ } | |
+ | |
+ void operator() (const Range& range) const | |
+ { | |
+ for (int i = range.start; i < range.end; i++) | |
+ { | |
+ Compute_Main_Orientation((*keypoints_)[i], *evolution_); | |
+ } | |
+ } | |
+private: | |
+ std::vector<KeyPoint>* keypoints_; | |
+ const std::vector<Evolution>* evolution_; | |
+}; | |
+ | |
/** | |
* @brief This method computes the main orientation for a given keypoints | |
* @param kpts Input keypoints | |
*/ | |
void AKAZEFeatures::Compute_Keypoints_Orientation(std::vector<KeyPoint>& kpts) const | |
{ | |
- for(size_t i = 0; i < kpts.size(); i++) | |
- Compute_Main_Orientation(kpts[i], evolution_); | |
+ CV_INSTRUMENT_REGION() | |
+ | |
+ parallel_for_(Range(0, (int)kpts.size()), ComputeKeypointOrientation(kpts, evolution_)); | |
} | |
/* ************************************************************************* */ | |
@@ -865,7 +1405,10 @@ void AKAZEFeatures::Compute_Keypoints_Orientation(std::vector<KeyPoint>& kpts) c | |
* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, | |
* ECCV 2008 | |
*/ | |
-void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float *desc) const { | |
+void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const KeyPoint& kpt, float *desc, int desc_size) const { | |
+ | |
+ const int dsize = 64; | |
+ CV_Assert(desc_size == dsize); | |
float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; | |
float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; | |
@@ -873,22 +1416,23 @@ void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const | |
int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; | |
int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0; | |
float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; | |
- int scale = 0, dsize = 0, level = 0; | |
+ int scale = 0; | |
// Subregion centers for the 4x4 gaussian weighting | |
float cx = -0.5f, cy = 0.5f; | |
- const std::vector<TEvolution>& evolution = *evolution_; | |
+ const std::vector<Evolution>& evolution = *evolution_; | |
// Set the descriptor size and the sample and pattern sizes | |
- dsize = 64; | |
sample_step = 5; | |
pattern_size = 12; | |
// Get the information from the keypoint | |
ratio = (float)(1 << kpt.octave); | |
- scale = fRound(0.5f*kpt.size / ratio); | |
- level = kpt.class_id; | |
+ scale = cvRound(0.5f*kpt.size / ratio); | |
+ const int level = kpt.class_id; | |
+ const Mat Lx = evolution[level].Lx; | |
+ const Mat Ly = evolution[level].Ly; | |
yf = kpt.pt.y / ratio; | |
xf = kpt.pt.x / ratio; | |
@@ -922,25 +1466,28 @@ void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const | |
//Get the gaussian weighted x and y responses | |
gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.50f*scale); | |
- y1 = (int)(sample_y - .5); | |
- x1 = (int)(sample_x - .5); | |
+ y1 = cvFloor(sample_y); | |
+ x1 = cvFloor(sample_x); | |
+ | |
+ y2 = y1 + 1; | |
+ x2 = x1 + 1; | |
- y2 = (int)(sample_y + .5); | |
- x2 = (int)(sample_x + .5); | |
+ if (x1 < 0 || y1 < 0 || x2 >= Lx.cols || y2 >= Lx.rows) | |
+ continue; // FIXIT Boundaries | |
fx = sample_x - x1; | |
fy = sample_y - y1; | |
- res1 = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- res2 = *(evolution[level].Lx.ptr<float>(y1)+x2); | |
- res3 = *(evolution[level].Lx.ptr<float>(y2)+x1); | |
- res4 = *(evolution[level].Lx.ptr<float>(y2)+x2); | |
+ res1 = Lx.at<float>(y1, x1); | |
+ res2 = Lx.at<float>(y1, x2); | |
+ res3 = Lx.at<float>(y2, x1); | |
+ res4 = Lx.at<float>(y2, x2); | |
rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; | |
- res1 = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
- res2 = *(evolution[level].Ly.ptr<float>(y1)+x2); | |
- res3 = *(evolution[level].Ly.ptr<float>(y2)+x1); | |
- res4 = *(evolution[level].Ly.ptr<float>(y2)+x2); | |
+ res1 = Ly.at<float>(y1, x1); | |
+ res2 = Ly.at<float>(y1, x2); | |
+ res3 = Ly.at<float>(y2, x1); | |
+ res4 = Ly.at<float>(y2, x2); | |
ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; | |
rx = gauss_s1*rx; | |
@@ -970,11 +1517,14 @@ void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const | |
i += 9; | |
} | |
+ CV_Assert(dcount == desc_size); | |
+ | |
// convert to unit vector | |
len = sqrt(len); | |
+ const float len_inv = 1.0f / len; | |
for (i = 0; i < dsize; i++) { | |
- desc[i] /= len; | |
+ desc[i] *= len_inv; | |
} | |
} | |
@@ -988,7 +1538,10 @@ void MSURF_Upright_Descriptor_64_Invoker::Get_MSURF_Upright_Descriptor_64(const | |
* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching, | |
* ECCV 2008 | |
*/ | |
-void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, float *desc) const { | |
+void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, float *desc, int desc_size) const { | |
+ | |
+ const int dsize = 64; | |
+ CV_Assert(desc_size == dsize); | |
float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0; | |
float rx = 0.0, ry = 0.0, rrx = 0.0, rry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0; | |
@@ -996,23 +1549,24 @@ void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, f | |
float fx = 0.0, fy = 0.0, ratio = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0; | |
int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0; | |
int kx = 0, ky = 0, i = 0, j = 0, dcount = 0; | |
- int scale = 0, dsize = 0, level = 0; | |
+ int scale = 0; | |
// Subregion centers for the 4x4 gaussian weighting | |
float cx = -0.5f, cy = 0.5f; | |
- const std::vector<TEvolution>& evolution = *evolution_; | |
+ const std::vector<Evolution>& evolution = *evolution_; | |
// Set the descriptor size and the sample and pattern sizes | |
- dsize = 64; | |
sample_step = 5; | |
pattern_size = 12; | |
// Get the information from the keypoint | |
ratio = (float)(1 << kpt.octave); | |
- scale = fRound(0.5f*kpt.size / ratio); | |
- angle = (kpt.angle * static_cast<float>(CV_PI)) / 180.f; | |
- level = kpt.class_id; | |
+ scale = cvRound(0.5f*kpt.size / ratio); | |
+ angle = kpt.angle * static_cast<float>(CV_PI / 180.f); | |
+ const int level = kpt.class_id; | |
+ const Mat Lx = evolution[level].Lx; | |
+ const Mat Ly = evolution[level].Ly; | |
yf = kpt.pt.y / ratio; | |
xf = kpt.pt.x / ratio; | |
co = cos(angle); | |
@@ -1049,25 +1603,28 @@ void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, f | |
// Get the gaussian weighted x and y responses | |
gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale); | |
- y1 = fRound(sample_y - 0.5f); | |
- x1 = fRound(sample_x - 0.5f); | |
+ y1 = cvFloor(sample_y); | |
+ x1 = cvFloor(sample_x); | |
+ | |
+ y2 = y1 + 1; | |
+ x2 = x1 + 1; | |
- y2 = fRound(sample_y + 0.5f); | |
- x2 = fRound(sample_x + 0.5f); | |
+ if (x1 < 0 || y1 < 0 || x2 >= Lx.cols || y2 >= Lx.rows) | |
+ continue; // FIXIT Boundaries | |
fx = sample_x - x1; | |
fy = sample_y - y1; | |
- res1 = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- res2 = *(evolution[level].Lx.ptr<float>(y1)+x2); | |
- res3 = *(evolution[level].Lx.ptr<float>(y2)+x1); | |
- res4 = *(evolution[level].Lx.ptr<float>(y2)+x2); | |
+ res1 = Lx.at<float>(y1, x1); | |
+ res2 = Lx.at<float>(y1, x2); | |
+ res3 = Lx.at<float>(y2, x1); | |
+ res4 = Lx.at<float>(y2, x2); | |
rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; | |
- res1 = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
- res2 = *(evolution[level].Ly.ptr<float>(y1)+x2); | |
- res3 = *(evolution[level].Ly.ptr<float>(y2)+x1); | |
- res4 = *(evolution[level].Ly.ptr<float>(y2)+x2); | |
+ res1 = Ly.at<float>(y1, x1); | |
+ res2 = Ly.at<float>(y1, x2); | |
+ res3 = Ly.at<float>(y2, x1); | |
+ res4 = Ly.at<float>(y2, x2); | |
ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4; | |
// Get the x and y derivatives on the rotated axis | |
@@ -1097,11 +1654,14 @@ void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, f | |
i += 9; | |
} | |
+ CV_Assert(dcount == desc_size); | |
+ | |
// convert to unit vector | |
len = sqrt(len); | |
+ const float len_inv = 1.0f / len; | |
for (i = 0; i < dsize; i++) { | |
- desc[i] /= len; | |
+ desc[i] *= len_inv; | |
} | |
} | |
@@ -1112,244 +1672,145 @@ void MSURF_Descriptor_64_Invoker::Get_MSURF_Descriptor_64(const KeyPoint& kpt, f | |
* @param kpt Input keypoint | |
* @param desc Descriptor vector | |
*/ | |
-void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc) const { | |
- | |
- float di = 0.0, dx = 0.0, dy = 0.0; | |
- float ri = 0.0, rx = 0.0, ry = 0.0, xf = 0.0, yf = 0.0; | |
- float sample_x = 0.0, sample_y = 0.0, ratio = 0.0; | |
- int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0; | |
- int level = 0, nsamples = 0, scale = 0; | |
- int dcount1 = 0, dcount2 = 0; | |
+void Upright_MLDB_Full_Descriptor_Invoker::Get_Upright_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc, int desc_size) const { | |
const AKAZEOptions & options = *options_; | |
- const std::vector<TEvolution>& evolution = *evolution_; | |
+ const std::vector<Evolution>& evolution = *evolution_; | |
- // Matrices for the M-LDB descriptor | |
- Mat values_1 = Mat::zeros(4, options.descriptor_channels, CV_32FC1); | |
- Mat values_2 = Mat::zeros(9, options.descriptor_channels, CV_32FC1); | |
- Mat values_3 = Mat::zeros(16, options.descriptor_channels, CV_32FC1); | |
+ // Buffer for the M-LDB descriptor | |
+ const int max_channels = 3; | |
+ CV_Assert(options.descriptor_channels <= max_channels); | |
+ float values[16*max_channels]; | |
// Get the information from the keypoint | |
- ratio = (float)(1 << kpt.octave); | |
- scale = fRound(0.5f*kpt.size / ratio); | |
- level = kpt.class_id; | |
- yf = kpt.pt.y / ratio; | |
- xf = kpt.pt.x / ratio; | |
- | |
- // First 2x2 grid | |
- pattern_size = options_->descriptor_pattern_size; | |
- sample_step = pattern_size; | |
- | |
- for (int i = -pattern_size; i < pattern_size; i += sample_step) { | |
- for (int j = -pattern_size; j < pattern_size; j += sample_step) { | |
- di = dx = dy = 0.0; | |
- nsamples = 0; | |
- | |
- for (int k = i; k < i + sample_step; k++) { | |
- for (int l = j; l < j + sample_step; l++) { | |
- | |
- // Get the coordinates of the sample point | |
- sample_y = yf + l*scale; | |
- sample_x = xf + k*scale; | |
- | |
- y1 = fRound(sample_y); | |
- x1 = fRound(sample_x); | |
- | |
- ri = *(evolution[level].Lt.ptr<float>(y1)+x1); | |
- rx = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- ry = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
- | |
- di += ri; | |
- dx += rx; | |
- dy += ry; | |
- nsamples++; | |
+ const float ratio = (float)(1 << kpt.octave); | |
+ const int scale = cvRound(0.5f*kpt.size / ratio); | |
+ const int level = kpt.class_id; | |
+ const Mat Lx = evolution[level].Lx; | |
+ const Mat Ly = evolution[level].Ly; | |
+ const Mat Lt = evolution[level].Lt; | |
+ const float yf = kpt.pt.y / ratio; | |
+ const float xf = kpt.pt.x / ratio; | |
+ | |
+ // For 2x2 grid, 3x3 grid and 4x4 grid | |
+ const int pattern_size = options_->descriptor_pattern_size; | |
+ CV_Assert((pattern_size & 1) == 0); | |
+ const int sample_step[3] = { | |
+ pattern_size, | |
+ divUp(pattern_size * 2, 3), | |
+ divUp(pattern_size, 2) | |
+ }; | |
+ | |
+ memset(desc, 0, desc_size); | |
+ | |
+ // For the three grids | |
+ int dcount1 = 0; | |
+ for (int z = 0; z < 3; z++) { | |
+ int dcount2 = 0; | |
+ const int step = sample_step[z]; | |
+ for (int i = -pattern_size; i < pattern_size; i += step) { | |
+ for (int j = -pattern_size; j < pattern_size; j += step) { | |
+ float di = 0.0, dx = 0.0, dy = 0.0; | |
+ | |
+ int nsamples = 0; | |
+ for (int k = 0; k < step; k++) { | |
+ for (int l = 0; l < step; l++) { | |
+ | |
+ // Get the coordinates of the sample point | |
+ const float sample_y = yf + (l+j)*scale; | |
+ const float sample_x = xf + (k+i)*scale; | |
+ | |
+ const int y1 = cvRound(sample_y); | |
+ const int x1 = cvRound(sample_x); | |
+ | |
+ if (y1 < 0 || y1 >= Lt.rows || x1 < 0 || x1 >= Lt.cols) | |
+ continue; // Boundaries | |
+ | |
+ const float ri = Lt.at<float>(y1, x1); | |
+ const float rx = Lx.at<float>(y1, x1); | |
+ const float ry = Ly.at<float>(y1, x1); | |
+ | |
+ di += ri; | |
+ dx += rx; | |
+ dy += ry; | |
+ nsamples++; | |
+ } | |
} | |
- } | |
- | |
- di /= nsamples; | |
- dx /= nsamples; | |
- dy /= nsamples; | |
- | |
- *(values_1.ptr<float>(dcount2)) = di; | |
- *(values_1.ptr<float>(dcount2)+1) = dx; | |
- *(values_1.ptr<float>(dcount2)+2) = dy; | |
- dcount2++; | |
- } | |
- } | |
- | |
- // Do binary comparison first level | |
- for (int i = 0; i < 4; i++) { | |
- for (int j = i + 1; j < 4; j++) { | |
- if (*(values_1.ptr<float>(i)) > *(values_1.ptr<float>(j))) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
- | |
- if (*(values_1.ptr<float>(i)+1) > *(values_1.ptr<float>(j)+1)) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
- | |
- if (*(values_1.ptr<float>(i)+2) > *(values_1.ptr<float>(j)+2)) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
- } | |
- } | |
- | |
- // Second 3x3 grid | |
- sample_step = static_cast<int>(ceil(pattern_size*2. / 3.)); | |
- dcount2 = 0; | |
- for (int i = -pattern_size; i < pattern_size; i += sample_step) { | |
- for (int j = -pattern_size; j < pattern_size; j += sample_step) { | |
- di = dx = dy = 0.0; | |
- nsamples = 0; | |
- | |
- for (int k = i; k < i + sample_step; k++) { | |
- for (int l = j; l < j + sample_step; l++) { | |
- | |
- // Get the coordinates of the sample point | |
- sample_y = yf + l*scale; | |
- sample_x = xf + k*scale; | |
- | |
- y1 = fRound(sample_y); | |
- x1 = fRound(sample_x); | |
- | |
- ri = *(evolution[level].Lt.ptr<float>(y1)+x1); | |
- rx = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- ry = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
- | |
- di += ri; | |
- dx += rx; | |
- dy += ry; | |
- nsamples++; | |
+ if (nsamples > 0) | |
+ { | |
+ const float nsamples_inv = 1.0f / nsamples; | |
+ di *= nsamples_inv; | |
+ dx *= nsamples_inv; | |
+ dy *= nsamples_inv; | |
} | |
- } | |
- | |
- di /= nsamples; | |
- dx /= nsamples; | |
- dy /= nsamples; | |
- | |
- *(values_2.ptr<float>(dcount2)) = di; | |
- *(values_2.ptr<float>(dcount2)+1) = dx; | |
- *(values_2.ptr<float>(dcount2)+2) = dy; | |
- dcount2++; | |
- } | |
- } | |
- //Do binary comparison second level | |
- dcount2 = 0; | |
- for (int i = 0; i < 9; i++) { | |
- for (int j = i + 1; j < 9; j++) { | |
- if (*(values_2.ptr<float>(i)) > *(values_2.ptr<float>(j))) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
+ float *val = &values[dcount2*max_channels]; | |
+ *(val) = di; | |
+ *(val+1) = dx; | |
+ *(val+2) = dy; | |
+ dcount2++; | |
} | |
- dcount1++; | |
- | |
- if (*(values_2.ptr<float>(i)+1) > *(values_2.ptr<float>(j)+1)) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
- | |
- if (*(values_2.ptr<float>(i)+2) > *(values_2.ptr<float>(j)+2)) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
} | |
- } | |
- // Third 4x4 grid | |
- sample_step = pattern_size / 2; | |
- dcount2 = 0; | |
- | |
- for (int i = -pattern_size; i < pattern_size; i += sample_step) { | |
- for (int j = -pattern_size; j < pattern_size; j += sample_step) { | |
- di = dx = dy = 0.0; | |
- nsamples = 0; | |
- | |
- for (int k = i; k < i + sample_step; k++) { | |
- for (int l = j; l < j + sample_step; l++) { | |
- | |
- // Get the coordinates of the sample point | |
- sample_y = yf + l*scale; | |
- sample_x = xf + k*scale; | |
- | |
- y1 = fRound(sample_y); | |
- x1 = fRound(sample_x); | |
- | |
- ri = *(evolution[level].Lt.ptr<float>(y1)+x1); | |
- rx = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- ry = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
- | |
- di += ri; | |
- dx += rx; | |
- dy += ry; | |
- nsamples++; | |
+ // Do binary comparison | |
+ const int num = (z + 2) * (z + 2); | |
+ for (int i = 0; i < num; i++) { | |
+ for (int j = i + 1; j < num; j++) { | |
+ const float * valI = &values[i*max_channels]; | |
+ const float * valJ = &values[j*max_channels]; | |
+ for (int k = 0; k < 3; ++k) { | |
+ if (*(valI + k) > *(valJ + k)) { | |
+ desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
+ } | |
+ dcount1++; | |
} | |
} | |
- | |
- di /= nsamples; | |
- dx /= nsamples; | |
- dy /= nsamples; | |
- | |
- *(values_3.ptr<float>(dcount2)) = di; | |
- *(values_3.ptr<float>(dcount2)+1) = dx; | |
- *(values_3.ptr<float>(dcount2)+2) = dy; | |
- dcount2++; | |
} | |
- } | |
- //Do binary comparison third level | |
- dcount2 = 0; | |
- for (int i = 0; i < 16; i++) { | |
- for (int j = i + 1; j < 16; j++) { | |
- if (*(values_3.ptr<float>(i)) > *(values_3.ptr<float>(j))) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
- | |
- if (*(values_3.ptr<float>(i)+1) > *(values_3.ptr<float>(j)+1)) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
+ } // for (int z = 0; z < 3; z++) | |
- if (*(values_3.ptr<float>(i)+2) > *(values_3.ptr<float>(j)+2)) { | |
- desc[dcount1 / 8] |= (1 << (dcount1 % 8)); | |
- } | |
- dcount1++; | |
- } | |
- } | |
+ CV_Assert(dcount1 <= desc_size*8); | |
+ CV_Assert(divUp(dcount1, 8) == desc_size); | |
} | |
-void MLDB_Full_Descriptor_Invoker::MLDB_Fill_Values(float* values, int sample_step, int level, | |
+void MLDB_Full_Descriptor_Invoker::MLDB_Fill_Values(float* values, int sample_step, const int level, | |
float xf, float yf, float co, float si, float scale) const | |
{ | |
- const std::vector<TEvolution>& evolution = *evolution_; | |
+ const std::vector<Evolution>& evolution = *evolution_; | |
int pattern_size = options_->descriptor_pattern_size; | |
int chan = options_->descriptor_channels; | |
- int valpos = 0; | |
+ const Mat Lx = evolution[level].Lx; | |
+ const Mat Ly = evolution[level].Ly; | |
+ const Mat Lt = evolution[level].Lt; | |
+ | |
+ const Size size = Lt.size(); | |
+ CV_Assert(size == Lx.size()); | |
+ CV_Assert(size == Ly.size()); | |
+ int valpos = 0; | |
for (int i = -pattern_size; i < pattern_size; i += sample_step) { | |
for (int j = -pattern_size; j < pattern_size; j += sample_step) { | |
- float di, dx, dy; | |
- di = dx = dy = 0.0; | |
- int nsamples = 0; | |
+ float di = 0.0f, dx = 0.0f, dy = 0.0f; | |
+ int nsamples = 0; | |
for (int k = i; k < i + sample_step; k++) { | |
for (int l = j; l < j + sample_step; l++) { | |
float sample_y = yf + (l*co * scale + k*si*scale); | |
float sample_x = xf + (-l*si * scale + k*co*scale); | |
- int y1 = fRound(sample_y); | |
- int x1 = fRound(sample_x); | |
+ int y1 = cvRound(sample_y); | |
+ int x1 = cvRound(sample_x); | |
- float ri = *(evolution[level].Lt.ptr<float>(y1)+x1); | |
+ if (y1 < 0 || y1 >= Lt.rows || x1 < 0 || x1 >= Lt.cols) | |
+ continue; // Boundaries | |
+ | |
+ float ri = Lt.at<float>(y1, x1); | |
di += ri; | |
if(chan > 1) { | |
- float rx = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- float ry = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
+ float rx = Lx.at<float>(y1, x1); | |
+ float ry = Ly.at<float>(y1, x1); | |
if (chan == 2) { | |
dx += sqrtf(rx*rx + ry*ry); | |
} | |
@@ -1363,20 +1824,25 @@ void MLDB_Full_Descriptor_Invoker::MLDB_Fill_Values(float* values, int sample_st | |
nsamples++; | |
} | |
} | |
- di /= nsamples; | |
- dx /= nsamples; | |
- dy /= nsamples; | |
+ | |
+ if (nsamples > 0) | |
+ { | |
+ const float nsamples_inv = 1.0f / nsamples; | |
+ di *= nsamples_inv; | |
+ dx *= nsamples_inv; | |
+ dy *= nsamples_inv; | |
+ } | |
values[valpos] = di; | |
if (chan > 1) { | |
values[valpos + 1] = dx; | |
} | |
if (chan > 2) { | |
- values[valpos + 2] = dy; | |
+ values[valpos + 2] = dy; | |
} | |
valpos += chan; | |
- } | |
} | |
+ } | |
} | |
void MLDB_Full_Descriptor_Invoker::MLDB_Binary_Comparisons(float* values, unsigned char* desc, | |
@@ -1391,8 +1857,9 @@ void MLDB_Full_Descriptor_Invoker::MLDB_Binary_Comparisons(float* values, unsign | |
for (int i = 0; i < count; i++) { | |
int ival = ivalues[chan * i + pos]; | |
for (int j = i + 1; j < count; j++) { | |
- int res = ival > ivalues[chan * j + pos]; | |
- desc[dpos >> 3] |= (res << (dpos & 7)); | |
+ if (ival > ivalues[chan * j + pos]) { | |
+ desc[dpos >> 3] |= (1 << (dpos & 7)); | |
+ } | |
dpos++; | |
} | |
} | |
@@ -1406,30 +1873,41 @@ void MLDB_Full_Descriptor_Invoker::MLDB_Binary_Comparisons(float* values, unsign | |
* @param kpt Input keypoint | |
* @param desc Descriptor vector | |
*/ | |
-void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc) const { | |
+void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const KeyPoint& kpt, unsigned char *desc, int desc_size) const { | |
const int max_channels = 3; | |
CV_Assert(options_->descriptor_channels <= max_channels); | |
+ const int pattern_size = options_->descriptor_pattern_size; | |
+ | |
float values[16*max_channels]; | |
- const double size_mult[3] = {1, 2.0/3.0, 1.0/2.0}; | |
+ CV_Assert((pattern_size & 1) == 0); | |
+ //const double size_mult[3] = {1, 2.0/3.0, 1.0/2.0}; | |
+ const int sample_step[3] = { // static_cast<int>(ceil(pattern_size * size_mult[lvl])) | |
+ pattern_size, | |
+ divUp(pattern_size * 2, 3), | |
+ divUp(pattern_size, 2) | |
+ }; | |
float ratio = (float)(1 << kpt.octave); | |
- float scale = (float)fRound(0.5f*kpt.size / ratio); | |
+ float scale = (float)cvRound(0.5f*kpt.size / ratio); | |
float xf = kpt.pt.x / ratio; | |
float yf = kpt.pt.y / ratio; | |
- float angle = (kpt.angle * static_cast<float>(CV_PI)) / 180.f; | |
+ float angle = kpt.angle * static_cast<float>(CV_PI / 180.f); | |
float co = cos(angle); | |
float si = sin(angle); | |
- int pattern_size = options_->descriptor_pattern_size; | |
- int dpos = 0; | |
- for(int lvl = 0; lvl < 3; lvl++) { | |
+ memset(desc, 0, desc_size); | |
+ int dpos = 0; | |
+ for(int lvl = 0; lvl < 3; lvl++) | |
+ { | |
int val_count = (lvl + 2) * (lvl + 2); | |
- int sample_step = static_cast<int>(ceil(pattern_size * size_mult[lvl])); | |
- MLDB_Fill_Values(values, sample_step, kpt.class_id, xf, yf, co, si, scale); | |
+ MLDB_Fill_Values(values, sample_step[lvl], kpt.class_id, xf, yf, co, si, scale); | |
MLDB_Binary_Comparisons(values, desc, val_count, dpos); | |
} | |
+ | |
+ CV_Assert(dpos == 486); | |
+ CV_Assert(divUp(dpos, 8) == desc_size); | |
} | |
/* ************************************************************************* */ | |
@@ -1440,41 +1918,48 @@ void MLDB_Full_Descriptor_Invoker::Get_MLDB_Full_Descriptor(const KeyPoint& kpt, | |
* @param kpt Input keypoint | |
* @param desc Descriptor vector | |
*/ | |
-void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc) const { | |
+void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc, int desc_size) const { | |
- float di = 0.f, dx = 0.f, dy = 0.f; | |
float rx = 0.f, ry = 0.f; | |
float sample_x = 0.f, sample_y = 0.f; | |
- int x1 = 0, y1 = 0; | |
const AKAZEOptions & options = *options_; | |
- const std::vector<TEvolution>& evolution = *evolution_; | |
+ const std::vector<Evolution>& evolution = *evolution_; | |
// Get the information from the keypoint | |
float ratio = (float)(1 << kpt.octave); | |
- int scale = fRound(0.5f*kpt.size / ratio); | |
- float angle = (kpt.angle * static_cast<float>(CV_PI)) / 180.f; | |
- int level = kpt.class_id; | |
+ int scale = cvRound(0.5f*kpt.size / ratio); | |
+ float angle = kpt.angle * static_cast<float>(CV_PI / 180.f); | |
+ const int level = kpt.class_id; | |
+ const Mat Lx = evolution[level].Lx; | |
+ const Mat Ly = evolution[level].Ly; | |
+ const Mat Lt = evolution[level].Lt; | |
float yf = kpt.pt.y / ratio; | |
float xf = kpt.pt.x / ratio; | |
float co = cos(angle); | |
float si = sin(angle); | |
// Allocate memory for the matrix of values | |
- Mat values = Mat_<float>::zeros((4 + 9 + 16)*options.descriptor_channels, 1); | |
+ // Buffer for the M-LDB descriptor | |
+ const int max_channels = 3; | |
+ const int channels = options.descriptor_channels; | |
+ CV_Assert(channels <= max_channels); | |
+ float values[(4 + 9 + 16)*max_channels] = { 0 }; | |
// Sample everything, but only do the comparisons | |
- vector<int> steps(3); | |
- steps.at(0) = options.descriptor_pattern_size; | |
- steps.at(1) = (int)ceil(2.f*options.descriptor_pattern_size / 3.f); | |
- steps.at(2) = options.descriptor_pattern_size / 2; | |
+ const int pattern_size = options.descriptor_pattern_size; | |
+ CV_Assert((pattern_size & 1) == 0); | |
+ const int sample_steps[3] = { | |
+ pattern_size, | |
+ divUp(pattern_size * 2, 3), | |
+ divUp(pattern_size, 2) | |
+ }; | |
for (int i = 0; i < descriptorSamples_.rows; i++) { | |
const int *coords = descriptorSamples_.ptr<int>(i); | |
- int sample_step = steps.at(coords[0]); | |
- di = 0.0f; | |
- dx = 0.0f; | |
- dy = 0.0f; | |
+ CV_Assert(coords[0] >= 0 && coords[0] < 3); | |
+ const int sample_step = sample_steps[coords[0]]; | |
+ float di = 0.f, dx = 0.f, dy = 0.f; | |
for (int k = coords[1]; k < coords[1] + sample_step; k++) { | |
for (int l = coords[2]; l < coords[2] + sample_step; l++) { | |
@@ -1483,14 +1968,17 @@ void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& | |
sample_y = yf + (l*scale*co + k*scale*si); | |
sample_x = xf + (-l*scale*si + k*scale*co); | |
- y1 = fRound(sample_y); | |
- x1 = fRound(sample_x); | |
+ const int y1 = cvRound(sample_y); | |
+ const int x1 = cvRound(sample_x); | |
+ | |
+ if (x1 < 0 || y1 < 0 || x1 >= Lt.cols || y1 >= Lt.rows) | |
+ continue; // Boundaries | |
- di += *(evolution[level].Lt.ptr<float>(y1)+x1); | |
+ di += Lt.at<float>(y1, x1); | |
if (options.descriptor_channels > 1) { | |
- rx = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- ry = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
+ rx = Lx.at<float>(y1, x1); | |
+ ry = Ly.at<float>(y1, x1); | |
if (options.descriptor_channels == 2) { | |
dx += sqrtf(rx*rx + ry*ry); | |
@@ -1504,23 +1992,26 @@ void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& | |
} | |
} | |
- *(values.ptr<float>(options.descriptor_channels*i)) = di; | |
+ float* pValues = &values[channels * i]; | |
+ pValues[0] = di; | |
- if (options.descriptor_channels == 2) { | |
- *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx; | |
+ if (channels == 2) { | |
+ pValues[1] = dx; | |
} | |
- else if (options.descriptor_channels == 3) { | |
- *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx; | |
- *(values.ptr<float>(options.descriptor_channels*i + 2)) = dy; | |
+ else if (channels == 3) { | |
+ pValues[1] = dx; | |
+ pValues[2] = dy; | |
} | |
} | |
// Do the comparisons | |
- const float *vals = values.ptr<float>(0); | |
const int *comps = descriptorBits_.ptr<int>(0); | |
+ CV_Assert(divUp(descriptorBits_.rows, 8) == desc_size); | |
+ memset(desc, 0, desc_size); | |
+ | |
for (int i = 0; i<descriptorBits_.rows; i++) { | |
- if (vals[comps[2 * i]] > vals[comps[2 * i + 1]]) { | |
+ if (values[comps[2 * i]] > values[comps[2 * i + 1]]) { | |
desc[i / 8] |= (1 << (i % 8)); | |
} | |
} | |
@@ -1534,7 +2025,7 @@ void MLDB_Descriptor_Subset_Invoker::Get_MLDB_Descriptor_Subset(const KeyPoint& | |
* @param kpt Input keypoint | |
* @param desc Descriptor vector | |
*/ | |
-void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc) const { | |
+void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset(const KeyPoint& kpt, unsigned char *desc, int desc_size) const { | |
float di = 0.0f, dx = 0.0f, dy = 0.0f; | |
float rx = 0.0f, ry = 0.0f; | |
@@ -1542,26 +2033,36 @@ void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset( | |
int x1 = 0, y1 = 0; | |
const AKAZEOptions & options = *options_; | |
- const std::vector<TEvolution>& evolution = *evolution_; | |
+ const std::vector<Evolution>& evolution = *evolution_; | |
// Get the information from the keypoint | |
float ratio = (float)(1 << kpt.octave); | |
- int scale = fRound(0.5f*kpt.size / ratio); | |
- int level = kpt.class_id; | |
+ int scale = cvRound(0.5f*kpt.size / ratio); | |
+ const int level = kpt.class_id; | |
+ const Mat Lx = evolution[level].Lx; | |
+ const Mat Ly = evolution[level].Ly; | |
+ const Mat Lt = evolution[level].Lt; | |
float yf = kpt.pt.y / ratio; | |
float xf = kpt.pt.x / ratio; | |
// Allocate memory for the matrix of values | |
- Mat values = Mat_<float>::zeros((4 + 9 + 16)*options.descriptor_channels, 1); | |
- | |
- vector<int> steps(3); | |
- steps.at(0) = options.descriptor_pattern_size; | |
- steps.at(1) = static_cast<int>(ceil(2.f*options.descriptor_pattern_size / 3.f)); | |
- steps.at(2) = options.descriptor_pattern_size / 2; | |
+ const int max_channels = 3; | |
+ const int channels = options.descriptor_channels; | |
+ CV_Assert(channels <= max_channels); | |
+ float values[(4 + 9 + 16)*max_channels] = { 0 }; | |
+ | |
+ const int pattern_size = options.descriptor_pattern_size; | |
+ CV_Assert((pattern_size & 1) == 0); | |
+ const int sample_steps[3] = { | |
+ pattern_size, | |
+ divUp(pattern_size * 2, 3), | |
+ divUp(pattern_size, 2) | |
+ }; | |
for (int i = 0; i < descriptorSamples_.rows; i++) { | |
const int *coords = descriptorSamples_.ptr<int>(i); | |
- int sample_step = steps.at(coords[0]); | |
+ CV_Assert(coords[0] >= 0 && coords[0] < 3); | |
+ int sample_step = sample_steps[coords[0]]; | |
di = 0.0f, dx = 0.0f, dy = 0.0f; | |
for (int k = coords[1]; k < coords[1] + sample_step; k++) { | |
@@ -1571,13 +2072,17 @@ void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset( | |
sample_y = yf + l*scale; | |
sample_x = xf + k*scale; | |
- y1 = fRound(sample_y); | |
- x1 = fRound(sample_x); | |
- di += *(evolution[level].Lt.ptr<float>(y1)+x1); | |
+ y1 = cvRound(sample_y); | |
+ x1 = cvRound(sample_x); | |
+ | |
+ if (x1 < 0 || y1 < 0 || x1 >= Lt.cols || y1 >= Lt.rows) | |
+ continue; // Boundaries | |
+ | |
+ di += Lt.at<float>(y1, x1); | |
if (options.descriptor_channels > 1) { | |
- rx = *(evolution[level].Lx.ptr<float>(y1)+x1); | |
- ry = *(evolution[level].Ly.ptr<float>(y1)+x1); | |
+ rx = Lx.at<float>(y1, x1); | |
+ ry = Ly.at<float>(y1, x1); | |
if (options.descriptor_channels == 2) { | |
dx += sqrtf(rx*rx + ry*ry); | |
@@ -1590,23 +2095,26 @@ void Upright_MLDB_Descriptor_Subset_Invoker::Get_Upright_MLDB_Descriptor_Subset( | |
} | |
} | |
- *(values.ptr<float>(options.descriptor_channels*i)) = di; | |
+ float* pValues = &values[channels * i]; | |
+ pValues[0] = di; | |
if (options.descriptor_channels == 2) { | |
- *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx; | |
+ pValues[1] = dx; | |
} | |
else if (options.descriptor_channels == 3) { | |
- *(values.ptr<float>(options.descriptor_channels*i + 1)) = dx; | |
- *(values.ptr<float>(options.descriptor_channels*i + 2)) = dy; | |
+ pValues[1] = dx; | |
+ pValues[2] = dy; | |
} | |
} | |
// Do the comparisons | |
- const float *vals = values.ptr<float>(0); | |
const int *comps = descriptorBits_.ptr<int>(0); | |
+ CV_Assert(divUp(descriptorBits_.rows, 8) == desc_size); | |
+ memset(desc, 0, desc_size); | |
+ | |
for (int i = 0; i<descriptorBits_.rows; i++) { | |
- if (vals[comps[2 * i]] > vals[comps[2 * i + 1]]) { | |
+ if (values[comps[2 * i]] > values[comps[2 * i + 1]]) { | |
desc[i / 8] |= (1 << (i % 8)); | |
} | |
} | |
@@ -1636,7 +2144,8 @@ void generateDescriptorSubsample(Mat& sampleList, Mat& comparisons, int nbits, | |
} | |
ssz *= nchannels; | |
- CV_Assert(nbits <= ssz); // Descriptor size can't be bigger than full descriptor | |
+ CV_Assert(ssz == 162*nchannels); | |
+ CV_Assert(nbits <= ssz && "Descriptor size can't be bigger than full descriptor (486 = 162*3 - 3 channels)"); | |
// Since the full descriptor is usually under 10k elements, we pick | |
// the selection from the full matrix. We take as many samples per | |
@@ -1647,7 +2156,7 @@ void generateDescriptorSubsample(Mat& sampleList, Mat& comparisons, int nbits, | |
for (int i = 0, c = 0; i < 3; i++) { | |
int gdiv = i + 2; //grid divisions, per row | |
int gsz = gdiv*gdiv; | |
- int psz = (int)ceil(2.f*pattern_size / (float)gdiv); | |
+ int psz = divUp(2*pattern_size, gdiv); | |
for (int j = 0; j < gsz; j++) { | |
for (int k = j + 1; k < gsz; k++, c++) { | |
@@ -1660,19 +2169,19 @@ void generateDescriptorSubsample(Mat& sampleList, Mat& comparisons, int nbits, | |
} | |
} | |
- srand(1024); | |
- Mat_<int> comps = Mat_<int>(nchannels * (int)ceil(nbits / (float)nchannels), 2); | |
+ RNG rng(1024); | |
+ const int npicks = divUp(nbits, nchannels); | |
+ Mat_<int> comps = Mat_<int>(nchannels * npicks, 2); | |
comps = 1000; | |
// Select some samples. A sample includes all channels | |
int count = 0; | |
- int npicks = (int)ceil(nbits / (float)nchannels); | |
Mat_<int> samples(29, 3); | |
Mat_<int> fullcopy = fullM.clone(); | |
samples = -1; | |
for (int i = 0; i < npicks; i++) { | |
- int k = rand() % (fullM.rows - i); | |
+ int k = rng(fullM.rows - i); | |
if (i < 6) { | |
// Force use of the coarser grid values and comparisons | |
k = i; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment