Path: blob/master/modules/features2d/src/kaze/nldiffusion_functions.cpp
16337 views
//=============================================================================1//2// nldiffusion_functions.cpp3// Author: Pablo F. Alcantarilla4// Institution: University d'Auvergne5// Address: Clermont Ferrand, France6// Date: 27/12/20117// Email: [email protected]8//9// KAZE Features Copyright 2012, Pablo F. Alcantarilla10// All Rights Reserved11// See LICENSE for the license information12//=============================================================================1314/**15* @file nldiffusion_functions.cpp16* @brief Functions for non-linear diffusion applications:17* 2D Gaussian Derivatives18* Perona and Malik conductivity equations19* Perona and Malik evolution20* @date Dec 27, 201121* @author Pablo F. Alcantarilla22*/2324#include "../precomp.hpp"25#include "nldiffusion_functions.h"26#include <iostream>2728// Namespaces2930/* ************************************************************************* */3132namespace cv33{34using namespace std;3536/* ************************************************************************* */37/**38* @brief This function smoothes an image with a Gaussian kernel39* @param src Input image40* @param dst Output image41* @param ksize_x Kernel size in X-direction (horizontal)42* @param ksize_y Kernel size in Y-direction (vertical)43* @param sigma Kernel standard deviation44*/45void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma) {4647int ksize_x_ = 0, ksize_y_ = 0;4849// Compute an appropriate kernel size according to the specified sigma50if (sigma > ksize_x || sigma > ksize_y || ksize_x == 0 || ksize_y == 0) {51ksize_x_ = cvCeil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f)));52ksize_y_ = ksize_x_;53}5455// The kernel size must be and odd number56if ((ksize_x_ % 2) == 0) {57ksize_x_ += 1;58}5960if ((ksize_y_ % 2) == 0) {61ksize_y_ += 1;62}6364// Perform the Gaussian Smoothing with border replication65GaussianBlur(src, dst, Size(ksize_x_, ksize_y_), sigma, sigma, BORDER_REPLICATE);66}6768/* ************************************************************************* */69/**70* @brief This function computes image derivatives with Scharr kernel71* @param src Input image72* @param dst Output image73* @param xorder Derivative order in X-direction (horizontal)74* @param yorder Derivative order in Y-direction (vertical)75* @note Scharr operator approximates better rotation invariance than76* other stencils such as Sobel. See Weickert and Scharr,77* A Scheme for Coherence-Enhancing Diffusion Filtering with Optimized Rotation Invariance,78* Journal of Visual Communication and Image Representation 200279*/80void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder) {81Scharr(src, dst, CV_32F, xorder, yorder, 1.0, 0, BORDER_DEFAULT);82}8384/* ************************************************************************* */85/**86* @brief This function computes the Perona and Malik conductivity coefficient g187* g1 = exp(-|dL|^2/k^2)88* @param Lx First order image derivative in X-direction (horizontal)89* @param Ly First order image derivative in Y-direction (vertical)90* @param dst Output image91* @param k Contrast factor parameter92*/93void pm_g1(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {94_dst.create(_Lx.size(), _Lx.type());95Mat Lx = _Lx.getMat();96Mat Ly = _Ly.getMat();97Mat dst = _dst.getMat();9899Size sz = Lx.size();100float inv_k = 1.0f / (k*k);101for (int y = 0; y < sz.height; y++) {102103const float* Lx_row = Lx.ptr<float>(y);104const float* Ly_row = Ly.ptr<float>(y);105float* dst_row = dst.ptr<float>(y);106107for (int x = 0; x < sz.width; x++) {108dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));109}110}111112exp(dst, dst);113}114115/* ************************************************************************* */116/**117* @brief This function computes the Perona and Malik conductivity coefficient g2118* g2 = 1 / (1 + dL^2 / k^2)119* @param Lx First order image derivative in X-direction (horizontal)120* @param Ly First order image derivative in Y-direction (vertical)121* @param dst Output image122* @param k Contrast factor parameter123*/124void pm_g2(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {125CV_INSTRUMENT_REGION();126127_dst.create(_Lx.size(), _Lx.type());128Mat Lx = _Lx.getMat();129Mat Ly = _Ly.getMat();130Mat dst = _dst.getMat();131132Size sz = Lx.size();133dst.create(sz, Lx.type());134float k2inv = 1.0f / (k * k);135136for(int y = 0; y < sz.height; y++) {137const float *Lx_row = Lx.ptr<float>(y);138const float *Ly_row = Ly.ptr<float>(y);139float* dst_row = dst.ptr<float>(y);140for(int x = 0; x < sz.width; x++) {141dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv));142}143}144}145/* ************************************************************************* */146/**147* @brief This function computes Weickert conductivity coefficient gw148* @param Lx First order image derivative in X-direction (horizontal)149* @param Ly First order image derivative in Y-direction (vertical)150* @param dst Output image151* @param k Contrast factor parameter152* @note For more information check the following paper: J. Weickert153* Applications of nonlinear diffusion in image processing and computer vision,154* Proceedings of Algorithmy 2000155*/156void weickert_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {157_dst.create(_Lx.size(), _Lx.type());158Mat Lx = _Lx.getMat();159Mat Ly = _Ly.getMat();160Mat dst = _dst.getMat();161162Size sz = Lx.size();163float inv_k = 1.0f / (k*k);164for (int y = 0; y < sz.height; y++) {165166const float* Lx_row = Lx.ptr<float>(y);167const float* Ly_row = Ly.ptr<float>(y);168float* dst_row = dst.ptr<float>(y);169170for (int x = 0; x < sz.width; x++) {171float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]);172dst_row[x] = -3.315f/(dL*dL*dL*dL);173}174}175176exp(dst, dst);177dst = 1.0 - dst;178}179180181/* ************************************************************************* */182/**183* @brief This function computes Charbonnier conductivity coefficient gc184* gc = 1 / sqrt(1 + dL^2 / k^2)185* @param Lx First order image derivative in X-direction (horizontal)186* @param Ly First order image derivative in Y-direction (vertical)187* @param dst Output image188* @param k Contrast factor parameter189* @note For more information check the following paper: J. Weickert190* Applications of nonlinear diffusion in image processing and computer vision,191* Proceedings of Algorithmy 2000192*/193void charbonnier_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {194_dst.create(_Lx.size(), _Lx.type());195Mat Lx = _Lx.getMat();196Mat Ly = _Ly.getMat();197Mat dst = _dst.getMat();198199Size sz = Lx.size();200float inv_k = 1.0f / (k*k);201for (int y = 0; y < sz.height; y++) {202203const float* Lx_row = Lx.ptr<float>(y);204const float* Ly_row = Ly.ptr<float>(y);205float* dst_row = dst.ptr<float>(y);206207for (int x = 0; x < sz.width; x++) {208float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));209dst_row[x] = 1.0f / den;210}211}212}213214215/* ************************************************************************* */216/**217* @brief This function computes a good empirical value for the k contrast factor218* given an input image, the percentile (0-1), the gradient scale and the number of219* bins in the histogram220* @param img Input image221* @param perc Percentile of the image gradient histogram (0-1)222* @param gscale Scale for computing the image gradient histogram223* @param nbins Number of histogram bins224* @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel225* @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel226* @return k contrast factor227*/228float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) {229CV_INSTRUMENT_REGION();230231int nbin = 0, nelements = 0, nthreshold = 0, k = 0;232float kperc = 0.0, modg = 0.0;233float npoints = 0.0;234float hmax = 0.0;235236// Create the array for the histogram237std::vector<int> hist(nbins, 0);238239// Create the matrices240Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F);241Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F);242Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F);243244// Perform the Gaussian convolution245gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale);246247// Compute the Gaussian derivatives Lx and Ly248Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT);249Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT);250251// Skip the borders for computing the histogram252for (int i = 1; i < gaussian.rows - 1; i++) {253const float *lx = Lx.ptr<float>(i);254const float *ly = Ly.ptr<float>(i);255for (int j = 1; j < gaussian.cols - 1; j++) {256modg = lx[j]*lx[j] + ly[j]*ly[j];257258// Get the maximum259if (modg > hmax) {260hmax = modg;261}262}263}264hmax = sqrt(hmax);265// Skip the borders for computing the histogram266for (int i = 1; i < gaussian.rows - 1; i++) {267const float *lx = Lx.ptr<float>(i);268const float *ly = Ly.ptr<float>(i);269for (int j = 1; j < gaussian.cols - 1; j++) {270modg = lx[j]*lx[j] + ly[j]*ly[j];271272// Find the correspondent bin273if (modg != 0.0) {274nbin = (int)floor(nbins*(sqrt(modg) / hmax));275276if (nbin == nbins) {277nbin--;278}279280hist[nbin]++;281npoints++;282}283}284}285286// Now find the perc of the histogram percentile287nthreshold = (int)(npoints*perc);288289for (k = 0; nelements < nthreshold && k < nbins; k++) {290nelements = nelements + hist[k];291}292293if (nelements < nthreshold) {294kperc = 0.03f;295}296else {297kperc = hmax*((float)(k) / (float)nbins);298}299300return kperc;301}302303/* ************************************************************************* */304/**305* @brief This function computes Scharr image derivatives306* @param src Input image307* @param dst Output image308* @param xorder Derivative order in X-direction (horizontal)309* @param yorder Derivative order in Y-direction (vertical)310* @param scale Scale factor for the derivative size311*/312void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) {313Mat kx, ky;314compute_derivative_kernels(kx, ky, xorder, yorder, scale);315sepFilter2D(src, dst, CV_32F, kx, ky);316}317318/* ************************************************************************* */319/**320* @brief Compute derivative kernels for sizes different than 3321* @param _kx Horizontal kernel ues322* @param _ky Vertical kernel values323* @param dx Derivative order in X-direction (horizontal)324* @param dy Derivative order in Y-direction (vertical)325* @param scale_ Scale factor or derivative size326*/327void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) {328CV_INSTRUMENT_REGION();329330int ksize = 3 + 2 * (scale - 1);331332// The standard Scharr kernel333if (scale == 1) {334getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F);335return;336}337338_kx.create(ksize, 1, CV_32F, -1, true);339_ky.create(ksize, 1, CV_32F, -1, true);340Mat kx = _kx.getMat();341Mat ky = _ky.getMat();342std::vector<float> kerI;343344float w = 10.0f / 3.0f;345float norm = 1.0f / (2.0f*scale*(w + 2.0f));346347for (int k = 0; k < 2; k++) {348Mat* kernel = k == 0 ? &kx : &ky;349int order = k == 0 ? dx : dy;350kerI.assign(ksize, 0.0f);351352if (order == 0) {353kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm;354}355else if (order == 1) {356kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1;357}358359Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]);360temp.copyTo(*kernel);361}362}363364class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody365{366public:367Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize)368: _Ld(&Ld)369, _c(&c)370, _Lstep(&Lstep)371, stepsize(_stepsize)372{373}374375virtual ~Nld_Step_Scalar_Invoker()376{377378}379380void operator()(const cv::Range& range) const CV_OVERRIDE381{382cv::Mat& Ld = *_Ld;383const cv::Mat& c = *_c;384cv::Mat& Lstep = *_Lstep;385386for (int i = range.start; i < range.end; i++)387{388const float *c_prev = c.ptr<float>(i - 1);389const float *c_curr = c.ptr<float>(i);390const float *c_next = c.ptr<float>(i + 1);391const float *ld_prev = Ld.ptr<float>(i - 1);392const float *ld_curr = Ld.ptr<float>(i);393const float *ld_next = Ld.ptr<float>(i + 1);394395float *dst = Lstep.ptr<float>(i);396397for (int j = 1; j < Lstep.cols - 1; j++)398{399float xpos = (c_curr[j] + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]);400float xneg = (c_curr[j-1] + c_curr[j]) *(ld_curr[j] - ld_curr[j-1]);401float ypos = (c_curr[j] + c_next[j]) *(ld_next[j] - ld_curr[j]);402float yneg = (c_prev[j] + c_curr[j]) *(ld_curr[j] - ld_prev[j]);403dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg);404}405}406}407private:408cv::Mat * _Ld;409const cv::Mat * _c;410cv::Mat * _Lstep;411float stepsize;412};413414/* ************************************************************************* */415/**416* @brief This function performs a scalar non-linear diffusion step417* @param Ld2 Output image in the evolution418* @param c Conductivity image419* @param Lstep Previous image in the evolution420* @param stepsize The step size in time units421* @note Forward Euler Scheme 3x3 stencil422* The function c is a scalar value that depends on the gradient norm423* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy424*/425void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) {426CV_INSTRUMENT_REGION();427428cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16));429430float xneg, xpos, yneg, ypos;431float* dst = Lstep.ptr<float>(0);432const float* cprv = NULL;433const float* ccur = c.ptr<float>(0);434const float* cnxt = c.ptr<float>(1);435const float* ldprv = NULL;436const float* ldcur = Ld.ptr<float>(0);437const float* ldnxt = Ld.ptr<float>(1);438for (int j = 1; j < Lstep.cols - 1; j++) {439xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);440xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);441ypos = (ccur[j] + cnxt[j]) * (ldnxt[j] - ldcur[j]);442dst[j] = 0.5f*stepsize*(xpos - xneg + ypos);443}444445dst = Lstep.ptr<float>(Lstep.rows - 1);446ccur = c.ptr<float>(Lstep.rows - 1);447cprv = c.ptr<float>(Lstep.rows - 2);448ldcur = Ld.ptr<float>(Lstep.rows - 1);449ldprv = Ld.ptr<float>(Lstep.rows - 2);450451for (int j = 1; j < Lstep.cols - 1; j++) {452xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);453xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);454yneg = (cprv[j] + ccur[j]) * (ldcur[j] - ldprv[j]);455dst[j] = 0.5f*stepsize*(xpos - xneg - yneg);456}457458ccur = c.ptr<float>(1);459ldcur = Ld.ptr<float>(1);460cprv = c.ptr<float>(0);461ldprv = Ld.ptr<float>(0);462463int r0 = Lstep.cols - 1;464int r1 = Lstep.cols - 2;465466for (int i = 1; i < Lstep.rows - 1; i++) {467cnxt = c.ptr<float>(i + 1);468ldnxt = Ld.ptr<float>(i + 1);469dst = Lstep.ptr<float>(i);470471xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]);472ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]);473yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]);474dst[0] = 0.5f*stepsize*(xpos + ypos - yneg);475476xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]);477ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]);478yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]);479dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg);480481cprv = ccur;482ccur = cnxt;483ldprv = ldcur;484ldcur = ldnxt;485}486Ld += Lstep;487}488489/* ************************************************************************* */490/**491* @brief This function downsamples the input image using OpenCV resize492* @param img Input image to be downsampled493* @param dst Output image with half of the resolution of the input image494*/495void halfsample_image(const cv::Mat& src, cv::Mat& dst) {496// Make sure the destination image is of the right size497CV_Assert(src.cols / 2 == dst.cols);498CV_Assert(src.rows / 2 == dst.rows);499resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA);500}501502/* ************************************************************************* */503/**504* @brief This function checks if a given pixel is a maximum in a local neighbourhood505* @param img Input image where we will perform the maximum search506* @param dsize Half size of the neighbourhood507* @param value Response value at (x,y) position508* @param row Image row coordinate509* @param col Image column coordinate510* @param same_img Flag to indicate if the image value at (x,y) is in the input image511* @return 1->is maximum, 0->otherwise512*/513bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) {514515bool response = true;516517for (int i = row - dsize; i <= row + dsize; i++) {518for (int j = col - dsize; j <= col + dsize; j++) {519if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) {520if (same_img == true) {521if (i != row || j != col) {522if ((*(img.ptr<float>(i)+j)) > value) {523response = false;524return response;525}526}527}528else {529if ((*(img.ptr<float>(i)+j)) > value) {530response = false;531return response;532}533}534}535}536}537538return response;539}540541}542543544