Path: blob/master/modules/features2d/src/kaze/KAZEFeatures.cpp
16337 views
1//=============================================================================2//3// KAZE.cpp4// Author: Pablo F. Alcantarilla5// Institution: University d'Auvergne6// Address: Clermont Ferrand, France7// Date: 21/01/20128// Email: [email protected]9//10// KAZE Features Copyright 2012, Pablo F. Alcantarilla11// All Rights Reserved12// See LICENSE for the license information13//=============================================================================1415/**16* @file KAZEFeatures.cpp17* @brief Main class for detecting and describing features in a nonlinear18* scale space19* @date Jan 21, 201220* @author Pablo F. Alcantarilla21*/22#include "../precomp.hpp"23#include "KAZEFeatures.h"24#include "utils.h"2526namespace cv27{2829// Namespaces30using namespace std;3132/* ************************************************************************* */33/**34* @brief KAZE constructor with input options35* @param options KAZE configuration options36* @note The constructor allocates memory for the nonlinear scale space37*/38KAZEFeatures::KAZEFeatures(KAZEOptions& options)39: options_(options)40{41ncycles_ = 0;42reordering_ = true;4344// Now allocate memory for the evolution45Allocate_Memory_Evolution();46}4748/* ************************************************************************* */49/**50* @brief This method allocates the memory for the nonlinear diffusion evolution51*/52void KAZEFeatures::Allocate_Memory_Evolution(void) {5354// Allocate the dimension of the matrices for the evolution55for (int i = 0; i <= options_.omax - 1; i++)56{57for (int j = 0; j <= options_.nsublevels - 1; j++)58{59TEvolution aux;60aux.Lx = Mat::zeros(options_.img_height, options_.img_width, CV_32F);61aux.Ly = Mat::zeros(options_.img_height, options_.img_width, CV_32F);62aux.Lxx = Mat::zeros(options_.img_height, options_.img_width, CV_32F);63aux.Lxy = Mat::zeros(options_.img_height, options_.img_width, CV_32F);64aux.Lyy = Mat::zeros(options_.img_height, options_.img_width, CV_32F);65aux.Lt = Mat::zeros(options_.img_height, options_.img_width, CV_32F);66aux.Lsmooth = Mat::zeros(options_.img_height, options_.img_width, CV_32F);67aux.Ldet = Mat::zeros(options_.img_height, options_.img_width, CV_32F);68aux.esigma = options_.soffset*pow((float)2.0f, (float)(j) / (float)(options_.nsublevels)+i);69aux.etime = 0.5f*(aux.esigma*aux.esigma);70aux.sigma_size = cvRound(aux.esigma);71aux.octave = i;72aux.sublevel = j;73evolution_.push_back(aux);74}75}7677// Allocate memory for the FED number of cycles and time steps78for (size_t i = 1; i < evolution_.size(); i++)79{80int naux = 0;81vector<float> tau;82float ttime = 0.0;83ttime = evolution_[i].etime - evolution_[i - 1].etime;84naux = fed_tau_by_process_time(ttime, 1, 0.25f, reordering_, tau);85nsteps_.push_back(naux);86tsteps_.push_back(tau);87ncycles_++;88}89}9091/* ************************************************************************* */92/**93* @brief This method creates the nonlinear scale space for a given image94* @param img Input image for which the nonlinear scale space needs to be created95* @return 0 if the nonlinear scale space was created successfully. -1 otherwise96*/97int KAZEFeatures::Create_Nonlinear_Scale_Space(const Mat &img)98{99CV_Assert(evolution_.size() > 0);100101// Copy the original image to the first level of the evolution102img.copyTo(evolution_[0].Lt);103gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lt, 0, 0, options_.soffset);104gaussian_2D_convolution(evolution_[0].Lt, evolution_[0].Lsmooth, 0, 0, options_.sderivatives);105106// Firstly compute the kcontrast factor107Compute_KContrast(evolution_[0].Lt, options_.kcontrast_percentille);108109// Allocate memory for the flow and step images110Mat Lflow = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F);111Mat Lstep = Mat::zeros(evolution_[0].Lt.rows, evolution_[0].Lt.cols, CV_32F);112113// Now generate the rest of evolution levels114for (size_t i = 1; i < evolution_.size(); i++)115{116evolution_[i - 1].Lt.copyTo(evolution_[i].Lt);117gaussian_2D_convolution(evolution_[i - 1].Lt, evolution_[i].Lsmooth, 0, 0, options_.sderivatives);118119// Compute the Gaussian derivatives Lx and Ly120Scharr(evolution_[i].Lsmooth, evolution_[i].Lx, CV_32F, 1, 0, 1, 0, BORDER_DEFAULT);121Scharr(evolution_[i].Lsmooth, evolution_[i].Ly, CV_32F, 0, 1, 1, 0, BORDER_DEFAULT);122123// Compute the conductivity equation124if (options_.diffusivity == KAZE::DIFF_PM_G1)125pm_g1(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);126else if (options_.diffusivity == KAZE::DIFF_PM_G2)127pm_g2(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);128else if (options_.diffusivity == KAZE::DIFF_WEICKERT)129weickert_diffusivity(evolution_[i].Lx, evolution_[i].Ly, Lflow, options_.kcontrast);130131// Perform FED n inner steps132for (int j = 0; j < nsteps_[i - 1]; j++)133nld_step_scalar(evolution_[i].Lt, Lflow, Lstep, tsteps_[i - 1][j]);134}135136return 0;137}138139/* ************************************************************************* */140/**141* @brief This method computes the k contrast factor142* @param img Input image143* @param kpercentile Percentile of the gradient histogram144*/145void KAZEFeatures::Compute_KContrast(const Mat &img, const float &kpercentile)146{147options_.kcontrast = compute_k_percentile(img, kpercentile, options_.sderivatives, options_.kcontrast_bins, 0, 0);148}149150/* ************************************************************************* */151/**152* @brief This method computes the feature detector response for the nonlinear scale space153* @note We use the Hessian determinant as feature detector154*/155void KAZEFeatures::Compute_Detector_Response(void)156{157float lxx = 0.0, lxy = 0.0, lyy = 0.0;158159// Firstly compute the multiscale derivatives160Compute_Multiscale_Derivatives();161162for (size_t i = 0; i < evolution_.size(); i++)163{164for (int ix = 0; ix < options_.img_height; ix++)165{166for (int jx = 0; jx < options_.img_width; jx++)167{168lxx = *(evolution_[i].Lxx.ptr<float>(ix)+jx);169lxy = *(evolution_[i].Lxy.ptr<float>(ix)+jx);170lyy = *(evolution_[i].Lyy.ptr<float>(ix)+jx);171*(evolution_[i].Ldet.ptr<float>(ix)+jx) = (lxx*lyy - lxy*lxy);172}173}174}175}176177/* ************************************************************************* */178/**179* @brief This method selects interesting keypoints through the nonlinear scale space180* @param kpts Vector of keypoints181*/182void KAZEFeatures::Feature_Detection(std::vector<KeyPoint>& kpts)183{184kpts.clear();185Compute_Detector_Response();186Determinant_Hessian(kpts);187Do_Subpixel_Refinement(kpts);188}189190/* ************************************************************************* */191class MultiscaleDerivativesKAZEInvoker : public ParallelLoopBody192{193public:194explicit MultiscaleDerivativesKAZEInvoker(std::vector<TEvolution>& ev) : evolution_(&ev)195{196}197198void operator()(const Range& range) const CV_OVERRIDE199{200std::vector<TEvolution>& evolution = *evolution_;201for (int i = range.start; i < range.end; i++)202{203compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Lx, 1, 0, evolution[i].sigma_size);204compute_scharr_derivatives(evolution[i].Lsmooth, evolution[i].Ly, 0, 1, evolution[i].sigma_size);205compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxx, 1, 0, evolution[i].sigma_size);206compute_scharr_derivatives(evolution[i].Ly, evolution[i].Lyy, 0, 1, evolution[i].sigma_size);207compute_scharr_derivatives(evolution[i].Lx, evolution[i].Lxy, 0, 1, evolution[i].sigma_size);208209evolution[i].Lx = evolution[i].Lx*((evolution[i].sigma_size));210evolution[i].Ly = evolution[i].Ly*((evolution[i].sigma_size));211evolution[i].Lxx = evolution[i].Lxx*((evolution[i].sigma_size)*(evolution[i].sigma_size));212evolution[i].Lxy = evolution[i].Lxy*((evolution[i].sigma_size)*(evolution[i].sigma_size));213evolution[i].Lyy = evolution[i].Lyy*((evolution[i].sigma_size)*(evolution[i].sigma_size));214}215}216217private:218std::vector<TEvolution>* evolution_;219};220221/* ************************************************************************* */222/**223* @brief This method computes the multiscale derivatives for the nonlinear scale space224*/225void KAZEFeatures::Compute_Multiscale_Derivatives(void)226{227parallel_for_(Range(0, (int)evolution_.size()),228MultiscaleDerivativesKAZEInvoker(evolution_));229}230231232/* ************************************************************************* */233class FindExtremumKAZEInvoker : public ParallelLoopBody234{235public:236explicit FindExtremumKAZEInvoker(std::vector<TEvolution>& ev, std::vector<std::vector<KeyPoint> >& kpts_par,237const KAZEOptions& options) : evolution_(&ev), kpts_par_(&kpts_par), options_(options)238{239}240241void operator()(const Range& range) const CV_OVERRIDE242{243std::vector<TEvolution>& evolution = *evolution_;244std::vector<std::vector<KeyPoint> >& kpts_par = *kpts_par_;245for (int i = range.start; i < range.end; i++)246{247float value = 0.0;248bool is_extremum = false;249250for (int ix = 1; ix < options_.img_height - 1; ix++)251{252for (int jx = 1; jx < options_.img_width - 1; jx++)253{254is_extremum = false;255value = *(evolution[i].Ldet.ptr<float>(ix)+jx);256257// Filter the points with the detector threshold258if (value > options_.dthreshold)259{260if (value >= *(evolution[i].Ldet.ptr<float>(ix)+jx - 1))261{262// First check on the same scale263if (check_maximum_neighbourhood(evolution[i].Ldet, 1, value, ix, jx, 1))264{265// Now check on the lower scale266if (check_maximum_neighbourhood(evolution[i - 1].Ldet, 1, value, ix, jx, 0))267{268// Now check on the upper scale269if (check_maximum_neighbourhood(evolution[i + 1].Ldet, 1, value, ix, jx, 0))270is_extremum = true;271}272}273}274}275276// Add the point of interest!!277if (is_extremum)278{279KeyPoint point;280point.pt.x = (float)jx;281point.pt.y = (float)ix;282point.response = fabs(value);283point.size = evolution[i].esigma;284point.octave = (int)evolution[i].octave;285point.class_id = i;286287// We use the angle field for the sublevel value288// Then, we will replace this angle field with the main orientation289point.angle = static_cast<float>(evolution[i].sublevel);290kpts_par[i - 1].push_back(point);291}292}293}294}295}296297private:298std::vector<TEvolution>* evolution_;299std::vector<std::vector<KeyPoint> >* kpts_par_;300KAZEOptions options_;301};302303/* ************************************************************************* */304/**305* @brief This method performs the detection of keypoints by using the normalized306* score of the Hessian determinant through the nonlinear scale space307* @param kpts Vector of keypoints308* @note We compute features for each of the nonlinear scale space level in a different processing thread309*/310void KAZEFeatures::Determinant_Hessian(std::vector<KeyPoint>& kpts)311{312int level = 0;313float dist = 0.0, smax = 3.0;314int npoints = 0, id_repeated = 0;315int left_x = 0, right_x = 0, up_y = 0, down_y = 0;316bool is_extremum = false, is_repeated = false, is_out = false;317318// Delete the memory of the vector of keypoints vectors319// In case we use the same kaze object for multiple images320for (size_t i = 0; i < kpts_par_.size(); i++) {321vector<KeyPoint>().swap(kpts_par_[i]);322}323kpts_par_.clear();324vector<KeyPoint> aux;325326// Allocate memory for the vector of vectors327for (size_t i = 1; i < evolution_.size() - 1; i++) {328kpts_par_.push_back(aux);329}330331parallel_for_(Range(1, (int)evolution_.size()-1),332FindExtremumKAZEInvoker(evolution_, kpts_par_, options_));333334// Now fill the vector of keypoints!!!335for (int i = 0; i < (int)kpts_par_.size(); i++)336{337for (int j = 0; j < (int)kpts_par_[i].size(); j++)338{339level = i + 1;340is_extremum = true;341is_repeated = false;342is_out = false;343344// Check in case we have the same point as maxima in previous evolution levels345for (int ik = 0; ik < (int)kpts.size(); ik++) {346if (kpts[ik].class_id == level || kpts[ik].class_id == level + 1 || kpts[ik].class_id == level - 1) {347dist = pow(kpts_par_[i][j].pt.x - kpts[ik].pt.x, 2) + pow(kpts_par_[i][j].pt.y - kpts[ik].pt.y, 2);348349if (dist < evolution_[level].sigma_size*evolution_[level].sigma_size) {350if (kpts_par_[i][j].response > kpts[ik].response) {351id_repeated = ik;352is_repeated = true;353}354else {355is_extremum = false;356}357358break;359}360}361}362363if (is_extremum == true) {364// Check that the point is under the image limits for the descriptor computation365left_x = cvRound(kpts_par_[i][j].pt.x - smax*kpts_par_[i][j].size);366right_x = cvRound(kpts_par_[i][j].pt.x + smax*kpts_par_[i][j].size);367up_y = cvRound(kpts_par_[i][j].pt.y - smax*kpts_par_[i][j].size);368down_y = cvRound(kpts_par_[i][j].pt.y + smax*kpts_par_[i][j].size);369370if (left_x < 0 || right_x >= evolution_[level].Ldet.cols ||371up_y < 0 || down_y >= evolution_[level].Ldet.rows) {372is_out = true;373}374375if (is_out == false) {376if (is_repeated == false) {377kpts.push_back(kpts_par_[i][j]);378npoints++;379}380else {381kpts[id_repeated] = kpts_par_[i][j];382}383}384}385}386}387}388389/* ************************************************************************* */390/**391* @brief This method performs subpixel refinement of the detected keypoints392* @param kpts Vector of detected keypoints393*/394void KAZEFeatures::Do_Subpixel_Refinement(std::vector<KeyPoint> &kpts) {395396int step = 1;397int x = 0, y = 0;398float Dx = 0.0, Dy = 0.0, Ds = 0.0, dsc = 0.0;399float Dxx = 0.0, Dyy = 0.0, Dss = 0.0, Dxy = 0.0, Dxs = 0.0, Dys = 0.0;400Mat A = Mat::zeros(3, 3, CV_32F);401Mat b = Mat::zeros(3, 1, CV_32F);402Mat dst = Mat::zeros(3, 1, CV_32F);403404vector<KeyPoint> kpts_(kpts);405406for (size_t i = 0; i < kpts_.size(); i++) {407408x = static_cast<int>(kpts_[i].pt.x);409y = static_cast<int>(kpts_[i].pt.y);410411// Compute the gradient412Dx = (1.0f / (2.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x + step)413- *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x - step));414Dy = (1.0f / (2.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x)415- *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x));416Ds = 0.5f*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x)417- *(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x));418419// Compute the Hessian420Dxx = (1.0f / (step*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x + step)421+ *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x - step)422- 2.0f*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x)));423424Dyy = (1.0f / (step*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x)425+ *(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x)426- 2.0f*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x)));427428Dss = *(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x)429+ *(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x)430- 2.0f*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y)+x));431432Dxy = (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x + step)433+ (*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x - step)))434- (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y - step) + x + step)435+ (*(evolution_[kpts_[i].class_id].Ldet.ptr<float>(y + step) + x - step)));436437Dxs = (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x + step)438+ (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x - step)))439- (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y)+x - step)440+ (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y)+x + step)));441442Dys = (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y + step) + x)443+ (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y - step) + x)))444- (1.0f / (4.0f*step))*(*(evolution_[kpts_[i].class_id + 1].Ldet.ptr<float>(y - step) + x)445+ (*(evolution_[kpts_[i].class_id - 1].Ldet.ptr<float>(y + step) + x)));446447// Solve the linear system448*(A.ptr<float>(0)) = Dxx;449*(A.ptr<float>(1) + 1) = Dyy;450*(A.ptr<float>(2) + 2) = Dss;451452*(A.ptr<float>(0) + 1) = *(A.ptr<float>(1)) = Dxy;453*(A.ptr<float>(0) + 2) = *(A.ptr<float>(2)) = Dxs;454*(A.ptr<float>(1) + 2) = *(A.ptr<float>(2) + 1) = Dys;455456*(b.ptr<float>(0)) = -Dx;457*(b.ptr<float>(1)) = -Dy;458*(b.ptr<float>(2)) = -Ds;459460solve(A, b, dst, DECOMP_LU);461462if (fabs(*(dst.ptr<float>(0))) <= 1.0f && fabs(*(dst.ptr<float>(1))) <= 1.0f && fabs(*(dst.ptr<float>(2))) <= 1.0f) {463kpts_[i].pt.x += *(dst.ptr<float>(0));464kpts_[i].pt.y += *(dst.ptr<float>(1));465dsc = kpts_[i].octave + (kpts_[i].angle + *(dst.ptr<float>(2))) / ((float)(options_.nsublevels));466467// In OpenCV the size of a keypoint is the diameter!!468kpts_[i].size = 2.0f*options_.soffset*pow((float)2.0f, dsc);469kpts_[i].angle = 0.0;470}471// Set the points to be deleted after the for loop472else {473kpts_[i].response = -1;474}475}476477// Clear the vector of keypoints478kpts.clear();479480for (size_t i = 0; i < kpts_.size(); i++) {481if (kpts_[i].response != -1) {482kpts.push_back(kpts_[i]);483}484}485}486487/* ************************************************************************* */488class KAZE_Descriptor_Invoker : public ParallelLoopBody489{490public:491KAZE_Descriptor_Invoker(std::vector<KeyPoint> &kpts, Mat &desc, std::vector<TEvolution>& evolution, const KAZEOptions& options)492: kpts_(&kpts)493, desc_(&desc)494, evolution_(&evolution)495, options_(options)496{497}498499virtual ~KAZE_Descriptor_Invoker()500{501}502503void operator() (const Range& range) const CV_OVERRIDE504{505std::vector<KeyPoint> &kpts = *kpts_;506Mat &desc = *desc_;507std::vector<TEvolution> &evolution = *evolution_;508509for (int i = range.start; i < range.end; i++)510{511kpts[i].angle = 0.0;512if (options_.upright)513{514kpts[i].angle = 0.0;515if (options_.extended)516Get_KAZE_Upright_Descriptor_128(kpts[i], desc.ptr<float>((int)i));517else518Get_KAZE_Upright_Descriptor_64(kpts[i], desc.ptr<float>((int)i));519}520else521{522KAZEFeatures::Compute_Main_Orientation(kpts[i], evolution, options_);523524if (options_.extended)525Get_KAZE_Descriptor_128(kpts[i], desc.ptr<float>((int)i));526else527Get_KAZE_Descriptor_64(kpts[i], desc.ptr<float>((int)i));528}529}530}531private:532void Get_KAZE_Upright_Descriptor_64(const KeyPoint& kpt, float* desc) const;533void Get_KAZE_Descriptor_64(const KeyPoint& kpt, float* desc) const;534void Get_KAZE_Upright_Descriptor_128(const KeyPoint& kpt, float* desc) const;535void Get_KAZE_Descriptor_128(const KeyPoint& kpt, float *desc) const;536537std::vector<KeyPoint> * kpts_;538Mat * desc_;539std::vector<TEvolution> * evolution_;540KAZEOptions options_;541};542543/* ************************************************************************* */544/**545* @brief This method computes the set of descriptors through the nonlinear scale space546* @param kpts Vector of keypoints547* @param desc Matrix with the feature descriptors548*/549void KAZEFeatures::Feature_Description(std::vector<KeyPoint> &kpts, Mat &desc)550{551for(size_t i = 0; i < kpts.size(); i++)552{553CV_Assert(0 <= kpts[i].class_id && kpts[i].class_id < static_cast<int>(evolution_.size()));554}555556// Allocate memory for the matrix of descriptors557if (options_.extended == true) {558desc = Mat::zeros((int)kpts.size(), 128, CV_32FC1);559}560else {561desc = Mat::zeros((int)kpts.size(), 64, CV_32FC1);562}563564parallel_for_(Range(0, (int)kpts.size()), KAZE_Descriptor_Invoker(kpts, desc, evolution_, options_));565}566567/* ************************************************************************* */568/**569* @brief This method computes the main orientation for a given keypoint570* @param kpt Input keypoint571* @note The orientation is computed using a similar approach as described in the572* original SURF method. See Bay et al., Speeded Up Robust Features, ECCV 2006573*/574void KAZEFeatures::Compute_Main_Orientation(KeyPoint &kpt, const std::vector<TEvolution>& evolution_, const KAZEOptions& options)575{576int ix = 0, iy = 0, idx = 0, s = 0, level = 0;577float xf = 0.0, yf = 0.0, gweight = 0.0;578vector<float> resX(109), resY(109), Ang(109);579580// Variables for computing the dominant direction581float sumX = 0.0, sumY = 0.0, max = 0.0, ang1 = 0.0, ang2 = 0.0;582583// Get the information from the keypoint584xf = kpt.pt.x;585yf = kpt.pt.y;586level = kpt.class_id;587s = cvRound(kpt.size / 2.0f);588589// Calculate derivatives responses for points within radius of 6*scale590for (int i = -6; i <= 6; ++i) {591for (int j = -6; j <= 6; ++j) {592if (i*i + j*j < 36) {593iy = cvRound(yf + j*s);594ix = cvRound(xf + i*s);595596if (iy >= 0 && iy < options.img_height && ix >= 0 && ix < options.img_width) {597gweight = gaussian(iy - yf, ix - xf, 2.5f*s);598resX[idx] = gweight*(*(evolution_[level].Lx.ptr<float>(iy)+ix));599resY[idx] = gweight*(*(evolution_[level].Ly.ptr<float>(iy)+ix));600}601else {602resX[idx] = 0.0;603resY[idx] = 0.0;604}605606Ang[idx] = fastAtan2(resY[idx], resX[idx]) * (float)(CV_PI / 180.0f);607++idx;608}609}610}611612// Loop slides pi/3 window around feature point613for (ang1 = 0; ang1 < 2.0f*CV_PI; ang1 += 0.15f) {614ang2 = (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));615sumX = sumY = 0.f;616617for (size_t k = 0; k < Ang.size(); ++k) {618// Get angle from the x-axis of the sample point619const float & ang = Ang[k];620621// Determine whether the point is within the window622if (ang1 < ang2 && ang1 < ang && ang < ang2) {623sumX += resX[k];624sumY += resY[k];625}626else if (ang2 < ang1 &&627((ang > 0 && ang < ang2) || (ang > ang1 && ang < (float)(2.0*CV_PI)))) {628sumX += resX[k];629sumY += resY[k];630}631}632633// if the vector produced from this window is longer than all634// previous vectors then this forms the new dominant direction635if (sumX*sumX + sumY*sumY > max) {636// store largest orientation637max = sumX*sumX + sumY*sumY;638kpt.angle = fastAtan2(sumY, sumX);639}640}641}642643/* ************************************************************************* */644/**645* @brief This method computes the upright descriptor (not rotation invariant) of646* the provided keypoint647* @param kpt Input keypoint648* @param desc Descriptor vector649* @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired650* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,651* ECCV 2008652*/653void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_64(const KeyPoint &kpt, float *desc) const654{655float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;656float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;657float sample_x = 0.0, sample_y = 0.0;658int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;659int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0;660float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;661int dsize = 0, scale = 0, level = 0;662663std::vector<TEvolution>& evolution = *evolution_;664665// Subregion centers for the 4x4 gaussian weighting666float cx = -0.5f, cy = 0.5f;667668// Set the descriptor size and the sample and pattern sizes669dsize = 64;670sample_step = 5;671pattern_size = 12;672673// Get the information from the keypoint674yf = kpt.pt.y;675xf = kpt.pt.x;676scale = cvRound(kpt.size / 2.0f);677level = kpt.class_id;678679i = -8;680681// Calculate descriptor for this interest point682// Area of size 24 s x 24 s683while (i < pattern_size) {684j = -8;685i = i - 4;686687cx += 1.0f;688cy = -0.5f;689690while (j < pattern_size) {691692dx = dy = mdx = mdy = 0.0;693cy += 1.0f;694j = j - 4;695696ky = i + sample_step;697kx = j + sample_step;698699ys = yf + (ky*scale);700xs = xf + (kx*scale);701702for (int k = i; k < i + 9; k++) {703for (int l = j; l < j + 9; l++) {704705sample_y = k*scale + yf;706sample_x = l*scale + xf;707708//Get the gaussian weighted x and y responses709gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);710711y1 = (int)(sample_y - 0.5f);712x1 = (int)(sample_x - 0.5f);713714checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);715716y2 = (int)(sample_y + 0.5f);717x2 = (int)(sample_x + 0.5f);718719checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);720721fx = sample_x - x1;722fy = sample_y - y1;723724res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);725res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);726res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);727res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);728rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;729730res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);731res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);732res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);733res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);734ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;735736rx = gauss_s1*rx;737ry = gauss_s1*ry;738739// Sum the derivatives to the cumulative descriptor740dx += rx;741dy += ry;742mdx += fabs(rx);743mdy += fabs(ry);744}745}746747// Add the values to the descriptor vector748gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);749750desc[dcount++] = dx*gauss_s2;751desc[dcount++] = dy*gauss_s2;752desc[dcount++] = mdx*gauss_s2;753desc[dcount++] = mdy*gauss_s2;754755len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;756757j += 9;758}759760i += 9;761}762763// convert to unit vector764len = sqrt(len);765766for (i = 0; i < dsize; i++) {767desc[i] /= len;768}769}770771/* ************************************************************************* */772/**773* @brief This method computes the descriptor of the provided keypoint given the774* main orientation of the keypoint775* @param kpt Input keypoint776* @param desc Descriptor vector777* @note Rectangular grid of 24 s x 24 s. Descriptor Length 64. The descriptor is inspired778* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,779* ECCV 2008780*/781void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_64(const KeyPoint &kpt, float *desc) const782{783float dx = 0.0, dy = 0.0, mdx = 0.0, mdy = 0.0, gauss_s1 = 0.0, gauss_s2 = 0.0;784float 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;785float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0;786float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;787int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0;788int kx = 0, ky = 0, i = 0, j = 0, dcount = 0;789int dsize = 0, scale = 0, level = 0;790791std::vector<TEvolution>& evolution = *evolution_;792793// Subregion centers for the 4x4 gaussian weighting794float cx = -0.5f, cy = 0.5f;795796// Set the descriptor size and the sample and pattern sizes797dsize = 64;798sample_step = 5;799pattern_size = 12;800801// Get the information from the keypoint802yf = kpt.pt.y;803xf = kpt.pt.x;804scale = cvRound(kpt.size / 2.0f);805angle = kpt.angle * static_cast<float>(CV_PI / 180.f);806level = kpt.class_id;807co = cos(angle);808si = sin(angle);809810i = -8;811812// Calculate descriptor for this interest point813// Area of size 24 s x 24 s814while (i < pattern_size) {815816j = -8;817i = i - 4;818819cx += 1.0f;820cy = -0.5f;821822while (j < pattern_size) {823824dx = dy = mdx = mdy = 0.0;825cy += 1.0f;826j = j - 4;827828ky = i + sample_step;829kx = j + sample_step;830831xs = xf + (-kx*scale*si + ky*scale*co);832ys = yf + (kx*scale*co + ky*scale*si);833834for (int k = i; k < i + 9; ++k) {835for (int l = j; l < j + 9; ++l) {836837// Get coords of sample point on the rotated axis838sample_y = yf + (l*scale*co + k*scale*si);839sample_x = xf + (-l*scale*si + k*scale*co);840841// Get the gaussian weighted x and y responses842gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);843y1 = cvFloor(sample_y);844x1 = cvFloor(sample_x);845846checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);847848y2 = y1 + 1;849x2 = x1 + 1;850851checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);852853fx = sample_x - x1;854fy = sample_y - y1;855856res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);857res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);858res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);859res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);860rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;861862res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);863res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);864res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);865res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);866ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;867868// Get the x and y derivatives on the rotated axis869rry = gauss_s1*(rx*co + ry*si);870rrx = gauss_s1*(-rx*si + ry*co);871872// Sum the derivatives to the cumulative descriptor873dx += rrx;874dy += rry;875mdx += fabs(rrx);876mdy += fabs(rry);877}878}879880// Add the values to the descriptor vector881gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);882desc[dcount++] = dx*gauss_s2;883desc[dcount++] = dy*gauss_s2;884desc[dcount++] = mdx*gauss_s2;885desc[dcount++] = mdy*gauss_s2;886len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy)*gauss_s2*gauss_s2;887j += 9;888}889i += 9;890}891892// convert to unit vector893len = sqrt(len);894895for (i = 0; i < dsize; i++) {896desc[i] /= len;897}898}899900/* ************************************************************************* */901/**902* @brief This method computes the extended upright descriptor (not rotation invariant) of903* the provided keypoint904* @param kpt Input keypoint905* @param desc Descriptor vector906* @note Rectangular grid of 24 s x 24 s. Descriptor Length 128. The descriptor is inspired907* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,908* ECCV 2008909*/910void KAZE_Descriptor_Invoker::Get_KAZE_Upright_Descriptor_128(const KeyPoint &kpt, float *desc) const911{912float gauss_s1 = 0.0, gauss_s2 = 0.0;913float rx = 0.0, ry = 0.0, len = 0.0, xf = 0.0, yf = 0.0, ys = 0.0, xs = 0.0;914float sample_x = 0.0, sample_y = 0.0;915int x1 = 0, y1 = 0, sample_step = 0, pattern_size = 0;916int x2 = 0, y2 = 0, kx = 0, ky = 0, i = 0, j = 0, dcount = 0;917float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;918float dxp = 0.0, dyp = 0.0, mdxp = 0.0, mdyp = 0.0;919float dxn = 0.0, dyn = 0.0, mdxn = 0.0, mdyn = 0.0;920int dsize = 0, scale = 0, level = 0;921922// Subregion centers for the 4x4 gaussian weighting923float cx = -0.5f, cy = 0.5f;924925std::vector<TEvolution>& evolution = *evolution_;926927// Set the descriptor size and the sample and pattern sizes928dsize = 128;929sample_step = 5;930pattern_size = 12;931932// Get the information from the keypoint933yf = kpt.pt.y;934xf = kpt.pt.x;935scale = cvRound(kpt.size / 2.0f);936level = kpt.class_id;937938i = -8;939940// Calculate descriptor for this interest point941// Area of size 24 s x 24 s942while (i < pattern_size) {943944j = -8;945i = i - 4;946947cx += 1.0f;948cy = -0.5f;949950while (j < pattern_size) {951952dxp = dxn = mdxp = mdxn = 0.0;953dyp = dyn = mdyp = mdyn = 0.0;954955cy += 1.0f;956j = j - 4;957958ky = i + sample_step;959kx = j + sample_step;960961ys = yf + (ky*scale);962xs = xf + (kx*scale);963964for (int k = i; k < i + 9; k++) {965for (int l = j; l < j + 9; l++) {966967sample_y = k*scale + yf;968sample_x = l*scale + xf;969970//Get the gaussian weighted x and y responses971gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);972973y1 = (int)(sample_y - 0.5f);974x1 = (int)(sample_x - 0.5f);975976checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);977978y2 = (int)(sample_y + 0.5f);979x2 = (int)(sample_x + 0.5f);980981checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);982983fx = sample_x - x1;984fy = sample_y - y1;985986res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);987res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);988res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);989res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);990rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;991992res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);993res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);994res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);995res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);996ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;997998rx = gauss_s1*rx;999ry = gauss_s1*ry;10001001// Sum the derivatives to the cumulative descriptor1002if (ry >= 0.0) {1003dxp += rx;1004mdxp += fabs(rx);1005}1006else {1007dxn += rx;1008mdxn += fabs(rx);1009}10101011if (rx >= 0.0) {1012dyp += ry;1013mdyp += fabs(ry);1014}1015else {1016dyn += ry;1017mdyn += fabs(ry);1018}1019}1020}10211022// Add the values to the descriptor vector1023gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);10241025desc[dcount++] = dxp*gauss_s2;1026desc[dcount++] = dxn*gauss_s2;1027desc[dcount++] = mdxp*gauss_s2;1028desc[dcount++] = mdxn*gauss_s2;1029desc[dcount++] = dyp*gauss_s2;1030desc[dcount++] = dyn*gauss_s2;1031desc[dcount++] = mdyp*gauss_s2;1032desc[dcount++] = mdyn*gauss_s2;10331034// Store the current length^2 of the vector1035len += (dxp*dxp + dxn*dxn + mdxp*mdxp + mdxn*mdxn +1036dyp*dyp + dyn*dyn + mdyp*mdyp + mdyn*mdyn)*gauss_s2*gauss_s2;10371038j += 9;1039}10401041i += 9;1042}10431044// convert to unit vector1045len = sqrt(len);10461047for (i = 0; i < dsize; i++) {1048desc[i] /= len;1049}1050}10511052/* ************************************************************************* */1053/**1054* @brief This method computes the extended G-SURF descriptor of the provided keypoint1055* given the main orientation of the keypoint1056* @param kpt Input keypoint1057* @param desc Descriptor vector1058* @note Rectangular grid of 24 s x 24 s. Descriptor Length 128. The descriptor is inspired1059* from Agrawal et al., CenSurE: Center Surround Extremas for Realtime Feature Detection and Matching,1060* ECCV 20081061*/1062void KAZE_Descriptor_Invoker::Get_KAZE_Descriptor_128(const KeyPoint &kpt, float *desc) const1063{1064float gauss_s1 = 0.0, gauss_s2 = 0.0;1065float 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;1066float sample_x = 0.0, sample_y = 0.0, co = 0.0, si = 0.0, angle = 0.0;1067float fx = 0.0, fy = 0.0, res1 = 0.0, res2 = 0.0, res3 = 0.0, res4 = 0.0;1068float dxp = 0.0, dyp = 0.0, mdxp = 0.0, mdyp = 0.0;1069float dxn = 0.0, dyn = 0.0, mdxn = 0.0, mdyn = 0.0;1070int x1 = 0, y1 = 0, x2 = 0, y2 = 0, sample_step = 0, pattern_size = 0;1071int kx = 0, ky = 0, i = 0, j = 0, dcount = 0;1072int dsize = 0, scale = 0, level = 0;10731074std::vector<TEvolution>& evolution = *evolution_;10751076// Subregion centers for the 4x4 gaussian weighting1077float cx = -0.5f, cy = 0.5f;10781079// Set the descriptor size and the sample and pattern sizes1080dsize = 128;1081sample_step = 5;1082pattern_size = 12;10831084// Get the information from the keypoint1085yf = kpt.pt.y;1086xf = kpt.pt.x;1087scale = cvRound(kpt.size / 2.0f);1088angle = kpt.angle * static_cast<float>(CV_PI / 180.f);1089level = kpt.class_id;1090co = cos(angle);1091si = sin(angle);10921093i = -8;10941095// Calculate descriptor for this interest point1096// Area of size 24 s x 24 s1097while (i < pattern_size) {10981099j = -8;1100i = i - 4;11011102cx += 1.0f;1103cy = -0.5f;11041105while (j < pattern_size) {11061107dxp = dxn = mdxp = mdxn = 0.0;1108dyp = dyn = mdyp = mdyn = 0.0;11091110cy += 1.0f;1111j = j - 4;11121113ky = i + sample_step;1114kx = j + sample_step;11151116xs = xf + (-kx*scale*si + ky*scale*co);1117ys = yf + (kx*scale*co + ky*scale*si);11181119for (int k = i; k < i + 9; ++k) {1120for (int l = j; l < j + 9; ++l) {11211122// Get coords of sample point on the rotated axis1123sample_y = yf + (l*scale*co + k*scale*si);1124sample_x = xf + (-l*scale*si + k*scale*co);11251126// Get the gaussian weighted x and y responses1127gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f*scale);11281129y1 = cvFloor(sample_y);1130x1 = cvFloor(sample_x);11311132checkDescriptorLimits(x1, y1, options_.img_width, options_.img_height);11331134y2 = y1 + 1;1135x2 = x1 + 1;11361137checkDescriptorLimits(x2, y2, options_.img_width, options_.img_height);11381139fx = sample_x - x1;1140fy = sample_y - y1;11411142res1 = *(evolution[level].Lx.ptr<float>(y1)+x1);1143res2 = *(evolution[level].Lx.ptr<float>(y1)+x2);1144res3 = *(evolution[level].Lx.ptr<float>(y2)+x1);1145res4 = *(evolution[level].Lx.ptr<float>(y2)+x2);1146rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;11471148res1 = *(evolution[level].Ly.ptr<float>(y1)+x1);1149res2 = *(evolution[level].Ly.ptr<float>(y1)+x2);1150res3 = *(evolution[level].Ly.ptr<float>(y2)+x1);1151res4 = *(evolution[level].Ly.ptr<float>(y2)+x2);1152ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2 + (1.0f - fx)*fy*res3 + fx*fy*res4;11531154// Get the x and y derivatives on the rotated axis1155rry = gauss_s1*(rx*co + ry*si);1156rrx = gauss_s1*(-rx*si + ry*co);11571158// Sum the derivatives to the cumulative descriptor1159if (rry >= 0.0) {1160dxp += rrx;1161mdxp += fabs(rrx);1162}1163else {1164dxn += rrx;1165mdxn += fabs(rrx);1166}11671168if (rrx >= 0.0) {1169dyp += rry;1170mdyp += fabs(rry);1171}1172else {1173dyn += rry;1174mdyn += fabs(rry);1175}1176}1177}11781179// Add the values to the descriptor vector1180gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);11811182desc[dcount++] = dxp*gauss_s2;1183desc[dcount++] = dxn*gauss_s2;1184desc[dcount++] = mdxp*gauss_s2;1185desc[dcount++] = mdxn*gauss_s2;1186desc[dcount++] = dyp*gauss_s2;1187desc[dcount++] = dyn*gauss_s2;1188desc[dcount++] = mdyp*gauss_s2;1189desc[dcount++] = mdyn*gauss_s2;11901191// Store the current length^2 of the vector1192len += (dxp*dxp + dxn*dxn + mdxp*mdxp + mdxn*mdxn +1193dyp*dyp + dyn*dyn + mdyp*mdyp + mdyn*mdyn)*gauss_s2*gauss_s2;11941195j += 9;1196}11971198i += 9;1199}12001201// convert to unit vector1202len = sqrt(len);12031204for (i = 0; i < dsize; i++) {1205desc[i] /= len;1206}1207}12081209}121012111212