Home | History | Annotate | Download | only in kaze
      1 //=============================================================================
      2 //
      3 // nldiffusion_functions.cpp
      4 // Author: Pablo F. Alcantarilla
      5 // Institution: University d'Auvergne
      6 // Address: Clermont Ferrand, France
      7 // Date: 27/12/2011
      8 // Email: pablofdezalc (at) gmail.com
      9 //
     10 // KAZE Features Copyright 2012, Pablo F. Alcantarilla
     11 // All Rights Reserved
     12 // See LICENSE for the license information
     13 //=============================================================================
     14 
     15 /**
     16  * @file nldiffusion_functions.cpp
     17  * @brief Functions for non-linear diffusion applications:
     18  * 2D Gaussian Derivatives
     19  * Perona and Malik conductivity equations
     20  * Perona and Malik evolution
     21  * @date Dec 27, 2011
     22  * @author Pablo F. Alcantarilla
     23  */
     24 
     25 #include "../precomp.hpp"
     26 #include "nldiffusion_functions.h"
     27 #include <iostream>
     28 
     29 // Namespaces
     30 
     31 /* ************************************************************************* */
     32 
     33 namespace cv
     34 {
     35 using namespace std;
     36 
     37 /* ************************************************************************* */
     38 /**
     39  * @brief This function smoothes an image with a Gaussian kernel
     40  * @param src Input image
     41  * @param dst Output image
     42  * @param ksize_x Kernel size in X-direction (horizontal)
     43  * @param ksize_y Kernel size in Y-direction (vertical)
     44  * @param sigma Kernel standard deviation
     45  */
     46 void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma) {
     47 
     48     int ksize_x_ = 0, ksize_y_ = 0;
     49 
     50     // Compute an appropriate kernel size according to the specified sigma
     51     if (sigma > ksize_x || sigma > ksize_y || ksize_x == 0 || ksize_y == 0) {
     52         ksize_x_ = (int)ceil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f)));
     53         ksize_y_ = ksize_x_;
     54     }
     55 
     56     // The kernel size must be and odd number
     57     if ((ksize_x_ % 2) == 0) {
     58         ksize_x_ += 1;
     59     }
     60 
     61     if ((ksize_y_ % 2) == 0) {
     62         ksize_y_ += 1;
     63     }
     64 
     65     // Perform the Gaussian Smoothing with border replication
     66     GaussianBlur(src, dst, Size(ksize_x_, ksize_y_), sigma, sigma, BORDER_REPLICATE);
     67 }
     68 
     69 /* ************************************************************************* */
     70 /**
     71  * @brief This function computes image derivatives with Scharr kernel
     72  * @param src Input image
     73  * @param dst Output image
     74  * @param xorder Derivative order in X-direction (horizontal)
     75  * @param yorder Derivative order in Y-direction (vertical)
     76  * @note Scharr operator approximates better rotation invariance than
     77  * other stencils such as Sobel. See Weickert and Scharr,
     78  * A Scheme for Coherence-Enhancing Diffusion Filtering with Optimized Rotation Invariance,
     79  * Journal of Visual Communication and Image Representation 2002
     80  */
     81 void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder) {
     82     Scharr(src, dst, CV_32F, xorder, yorder, 1.0, 0, BORDER_DEFAULT);
     83 }
     84 
     85 /* ************************************************************************* */
     86 /**
     87  * @brief This function computes the Perona and Malik conductivity coefficient g1
     88  * g1 = exp(-|dL|^2/k^2)
     89  * @param Lx First order image derivative in X-direction (horizontal)
     90  * @param Ly First order image derivative in Y-direction (vertical)
     91  * @param dst Output image
     92  * @param k Contrast factor parameter
     93  */
     94 void pm_g1(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) {
     95 
     96   Size sz = Lx.size();
     97   float inv_k = 1.0f / (k*k);
     98   for (int y = 0; y < sz.height; y++) {
     99 
    100     const float* Lx_row = Lx.ptr<float>(y);
    101     const float* Ly_row = Ly.ptr<float>(y);
    102     float* dst_row = dst.ptr<float>(y);
    103 
    104     for (int x = 0; x < sz.width; x++) {
    105       dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
    106     }
    107   }
    108 
    109   exp(dst, dst);
    110 }
    111 
    112 /* ************************************************************************* */
    113 /**
    114  * @brief This function computes the Perona and Malik conductivity coefficient g2
    115  * g2 = 1 / (1 + dL^2 / k^2)
    116  * @param Lx First order image derivative in X-direction (horizontal)
    117  * @param Ly First order image derivative in Y-direction (vertical)
    118  * @param dst Output image
    119  * @param k Contrast factor parameter
    120  */
    121 void pm_g2(const cv::Mat &Lx, const cv::Mat& Ly, cv::Mat& dst, float k) {
    122 
    123     Size sz = Lx.size();
    124     dst.create(sz, Lx.type());
    125     float k2inv = 1.0f / (k * k);
    126 
    127     for(int y = 0; y < sz.height; y++) {
    128         const float *Lx_row = Lx.ptr<float>(y);
    129         const float *Ly_row = Ly.ptr<float>(y);
    130         float* dst_row = dst.ptr<float>(y);
    131         for(int x = 0; x < sz.width; x++) {
    132             dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv));
    133         }
    134     }
    135 }
    136 /* ************************************************************************* */
    137 /**
    138  * @brief This function computes Weickert conductivity coefficient gw
    139  * @param Lx First order image derivative in X-direction (horizontal)
    140  * @param Ly First order image derivative in Y-direction (vertical)
    141  * @param dst Output image
    142  * @param k Contrast factor parameter
    143  * @note For more information check the following paper: J. Weickert
    144  * Applications of nonlinear diffusion in image processing and computer vision,
    145  * Proceedings of Algorithmy 2000
    146  */
    147 void weickert_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) {
    148 
    149   Size sz = Lx.size();
    150   float inv_k = 1.0f / (k*k);
    151   for (int y = 0; y < sz.height; y++) {
    152 
    153     const float* Lx_row = Lx.ptr<float>(y);
    154     const float* Ly_row = Ly.ptr<float>(y);
    155     float* dst_row = dst.ptr<float>(y);
    156 
    157     for (int x = 0; x < sz.width; x++) {
    158       float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]);
    159       dst_row[x] = -3.315f/(dL*dL*dL*dL);
    160     }
    161   }
    162 
    163   exp(dst, dst);
    164   dst = 1.0 - dst;
    165 }
    166 
    167 
    168 /* ************************************************************************* */
    169 /**
    170 * @brief This function computes Charbonnier conductivity coefficient gc
    171 * gc = 1 / sqrt(1 + dL^2 / k^2)
    172 * @param Lx First order image derivative in X-direction (horizontal)
    173 * @param Ly First order image derivative in Y-direction (vertical)
    174 * @param dst Output image
    175 * @param k Contrast factor parameter
    176 * @note For more information check the following paper: J. Weickert
    177 * Applications of nonlinear diffusion in image processing and computer vision,
    178 * Proceedings of Algorithmy 2000
    179 */
    180 void charbonnier_diffusivity(const cv::Mat& Lx, const cv::Mat& Ly, cv::Mat& dst, float k) {
    181 
    182   Size sz = Lx.size();
    183   float inv_k = 1.0f / (k*k);
    184   for (int y = 0; y < sz.height; y++) {
    185 
    186     const float* Lx_row = Lx.ptr<float>(y);
    187     const float* Ly_row = Ly.ptr<float>(y);
    188     float* dst_row = dst.ptr<float>(y);
    189 
    190     for (int x = 0; x < sz.width; x++) {
    191       float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
    192       dst_row[x] = 1.0f / den;
    193     }
    194   }
    195 }
    196 
    197 
    198 /* ************************************************************************* */
    199 /**
    200  * @brief This function computes a good empirical value for the k contrast factor
    201  * given an input image, the percentile (0-1), the gradient scale and the number of
    202  * bins in the histogram
    203  * @param img Input image
    204  * @param perc Percentile of the image gradient histogram (0-1)
    205  * @param gscale Scale for computing the image gradient histogram
    206  * @param nbins Number of histogram bins
    207  * @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel
    208  * @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel
    209  * @return k contrast factor
    210  */
    211 float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) {
    212 
    213     int nbin = 0, nelements = 0, nthreshold = 0, k = 0;
    214     float kperc = 0.0, modg = 0.0;
    215     float npoints = 0.0;
    216     float hmax = 0.0;
    217 
    218     // Create the array for the histogram
    219     std::vector<int> hist(nbins, 0);
    220 
    221     // Create the matrices
    222     Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F);
    223     Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F);
    224     Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F);
    225 
    226     // Perform the Gaussian convolution
    227     gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale);
    228 
    229     // Compute the Gaussian derivatives Lx and Ly
    230     Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT);
    231     Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT);
    232 
    233     // Skip the borders for computing the histogram
    234     for (int i = 1; i < gaussian.rows - 1; i++) {
    235         const float *lx = Lx.ptr<float>(i);
    236         const float *ly = Ly.ptr<float>(i);
    237         for (int j = 1; j < gaussian.cols - 1; j++) {
    238             modg = lx[j]*lx[j] + ly[j]*ly[j];
    239 
    240             // Get the maximum
    241             if (modg > hmax) {
    242                 hmax = modg;
    243             }
    244         }
    245     }
    246     hmax = sqrt(hmax);
    247     // Skip the borders for computing the histogram
    248     for (int i = 1; i < gaussian.rows - 1; i++) {
    249         const float *lx = Lx.ptr<float>(i);
    250         const float *ly = Ly.ptr<float>(i);
    251         for (int j = 1; j < gaussian.cols - 1; j++) {
    252             modg = lx[j]*lx[j] + ly[j]*ly[j];
    253 
    254             // Find the correspondent bin
    255             if (modg != 0.0) {
    256                 nbin = (int)floor(nbins*(sqrt(modg) / hmax));
    257 
    258                 if (nbin == nbins) {
    259                     nbin--;
    260                 }
    261 
    262                 hist[nbin]++;
    263                 npoints++;
    264             }
    265         }
    266     }
    267 
    268     // Now find the perc of the histogram percentile
    269     nthreshold = (int)(npoints*perc);
    270 
    271     for (k = 0; nelements < nthreshold && k < nbins; k++) {
    272         nelements = nelements + hist[k];
    273     }
    274 
    275     if (nelements < nthreshold)  {
    276         kperc = 0.03f;
    277     }
    278     else {
    279         kperc = hmax*((float)(k) / (float)nbins);
    280     }
    281 
    282     return kperc;
    283 }
    284 
    285 /* ************************************************************************* */
    286 /**
    287  * @brief This function computes Scharr image derivatives
    288  * @param src Input image
    289  * @param dst Output image
    290  * @param xorder Derivative order in X-direction (horizontal)
    291  * @param yorder Derivative order in Y-direction (vertical)
    292  * @param scale Scale factor for the derivative size
    293  */
    294 void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) {
    295     Mat kx, ky;
    296     compute_derivative_kernels(kx, ky, xorder, yorder, scale);
    297     sepFilter2D(src, dst, CV_32F, kx, ky);
    298 }
    299 
    300 /* ************************************************************************* */
    301 /**
    302  * @brief Compute derivative kernels for sizes different than 3
    303  * @param _kx Horizontal kernel ues
    304  * @param _ky Vertical kernel values
    305  * @param dx Derivative order in X-direction (horizontal)
    306  * @param dy Derivative order in Y-direction (vertical)
    307  * @param scale_ Scale factor or derivative size
    308  */
    309 void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) {
    310 
    311     int ksize = 3 + 2 * (scale - 1);
    312 
    313     // The standard Scharr kernel
    314     if (scale == 1) {
    315         getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F);
    316         return;
    317     }
    318 
    319     _kx.create(ksize, 1, CV_32F, -1, true);
    320     _ky.create(ksize, 1, CV_32F, -1, true);
    321     Mat kx = _kx.getMat();
    322     Mat ky = _ky.getMat();
    323 
    324     float w = 10.0f / 3.0f;
    325     float norm = 1.0f / (2.0f*scale*(w + 2.0f));
    326 
    327     for (int k = 0; k < 2; k++) {
    328         Mat* kernel = k == 0 ? &kx : &ky;
    329         int order = k == 0 ? dx : dy;
    330         std::vector<float> kerI(ksize, 0.0f);
    331 
    332         if (order == 0) {
    333             kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm;
    334         }
    335         else if (order == 1) {
    336             kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1;
    337         }
    338 
    339         Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]);
    340         temp.copyTo(*kernel);
    341     }
    342 }
    343 
    344 class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody
    345 {
    346 public:
    347     Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize)
    348         : _Ld(&Ld)
    349         , _c(&c)
    350         , _Lstep(&Lstep)
    351         , stepsize(_stepsize)
    352     {
    353     }
    354 
    355     virtual ~Nld_Step_Scalar_Invoker()
    356     {
    357 
    358     }
    359 
    360     void operator()(const cv::Range& range) const
    361     {
    362         cv::Mat& Ld = *_Ld;
    363         const cv::Mat& c = *_c;
    364         cv::Mat& Lstep = *_Lstep;
    365 
    366         for (int i = range.start; i < range.end; i++)
    367         {
    368             const float *c_prev  = c.ptr<float>(i - 1);
    369             const float *c_curr  = c.ptr<float>(i);
    370             const float *c_next  = c.ptr<float>(i + 1);
    371             const float *ld_prev = Ld.ptr<float>(i - 1);
    372             const float *ld_curr = Ld.ptr<float>(i);
    373             const float *ld_next = Ld.ptr<float>(i + 1);
    374 
    375             float *dst  = Lstep.ptr<float>(i);
    376 
    377             for (int j = 1; j < Lstep.cols - 1; j++)
    378             {
    379                 float xpos = (c_curr[j]   + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]);
    380                 float xneg = (c_curr[j-1] + c_curr[j])  *(ld_curr[j]   - ld_curr[j-1]);
    381                 float ypos = (c_curr[j]   + c_next[j])  *(ld_next[j]   - ld_curr[j]);
    382                 float yneg = (c_prev[j]   + c_curr[j])  *(ld_curr[j]   - ld_prev[j]);
    383                 dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg);
    384             }
    385         }
    386     }
    387 private:
    388     cv::Mat * _Ld;
    389     const cv::Mat * _c;
    390     cv::Mat * _Lstep;
    391     float stepsize;
    392 };
    393 
    394 /* ************************************************************************* */
    395 /**
    396 * @brief This function performs a scalar non-linear diffusion step
    397 * @param Ld2 Output image in the evolution
    398 * @param c Conductivity image
    399 * @param Lstep Previous image in the evolution
    400 * @param stepsize The step size in time units
    401 * @note Forward Euler Scheme 3x3 stencil
    402 * The function c is a scalar value that depends on the gradient norm
    403 * dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy
    404 */
    405 void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) {
    406 
    407     cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16));
    408 
    409     float xneg, xpos, yneg, ypos;
    410     float* dst = Lstep.ptr<float>(0);
    411     const float* cprv = NULL;
    412     const float* ccur  = c.ptr<float>(0);
    413     const float* cnxt  = c.ptr<float>(1);
    414     const float* ldprv = NULL;
    415     const float* ldcur = Ld.ptr<float>(0);
    416     const float* ldnxt = Ld.ptr<float>(1);
    417     for (int j = 1; j < Lstep.cols - 1; j++) {
    418         xpos = (ccur[j]   + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
    419         xneg = (ccur[j-1] + ccur[j])   * (ldcur[j]   - ldcur[j-1]);
    420         ypos = (ccur[j]   + cnxt[j])   * (ldnxt[j]   - ldcur[j]);
    421         dst[j] = 0.5f*stepsize*(xpos - xneg + ypos);
    422     }
    423 
    424     dst = Lstep.ptr<float>(Lstep.rows - 1);
    425     ccur = c.ptr<float>(Lstep.rows - 1);
    426     cprv = c.ptr<float>(Lstep.rows - 2);
    427     ldcur = Ld.ptr<float>(Lstep.rows - 1);
    428     ldprv = Ld.ptr<float>(Lstep.rows - 2);
    429 
    430     for (int j = 1; j < Lstep.cols - 1; j++) {
    431         xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
    432         xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
    433         yneg = (cprv[j] + ccur[j])   * (ldcur[j] - ldprv[j]);
    434         dst[j] = 0.5f*stepsize*(xpos - xneg - yneg);
    435     }
    436 
    437     ccur = c.ptr<float>(1);
    438     ldcur = Ld.ptr<float>(1);
    439     cprv = c.ptr<float>(0);
    440     ldprv = Ld.ptr<float>(0);
    441 
    442     int r0 = Lstep.cols - 1;
    443     int r1 = Lstep.cols - 2;
    444 
    445     for (int i = 1; i < Lstep.rows - 1; i++) {
    446         cnxt = c.ptr<float>(i + 1);
    447         ldnxt = Ld.ptr<float>(i + 1);
    448         dst = Lstep.ptr<float>(i);
    449 
    450         xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]);
    451         ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]);
    452         yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]);
    453         dst[0] = 0.5f*stepsize*(xpos + ypos - yneg);
    454 
    455         xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]);
    456         ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]);
    457         yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]);
    458         dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg);
    459 
    460         cprv = ccur;
    461         ccur = cnxt;
    462         ldprv = ldcur;
    463         ldcur = ldnxt;
    464     }
    465     Ld += Lstep;
    466 }
    467 
    468 /* ************************************************************************* */
    469 /**
    470 * @brief This function downsamples the input image using OpenCV resize
    471 * @param img Input image to be downsampled
    472 * @param dst Output image with half of the resolution of the input image
    473 */
    474 void halfsample_image(const cv::Mat& src, cv::Mat& dst) {
    475 
    476     // Make sure the destination image is of the right size
    477     CV_Assert(src.cols / 2 == dst.cols);
    478     CV_Assert(src.rows / 2 == dst.rows);
    479     resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA);
    480 }
    481 
    482 /* ************************************************************************* */
    483 /**
    484  * @brief This function checks if a given pixel is a maximum in a local neighbourhood
    485  * @param img Input image where we will perform the maximum search
    486  * @param dsize Half size of the neighbourhood
    487  * @param value Response value at (x,y) position
    488  * @param row Image row coordinate
    489  * @param col Image column coordinate
    490  * @param same_img Flag to indicate if the image value at (x,y) is in the input image
    491  * @return 1->is maximum, 0->otherwise
    492  */
    493 bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) {
    494 
    495     bool response = true;
    496 
    497     for (int i = row - dsize; i <= row + dsize; i++) {
    498         for (int j = col - dsize; j <= col + dsize; j++) {
    499             if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) {
    500                 if (same_img == true) {
    501                     if (i != row || j != col) {
    502                         if ((*(img.ptr<float>(i)+j)) > value) {
    503                             response = false;
    504                             return response;
    505                         }
    506                     }
    507                 }
    508                 else {
    509                     if ((*(img.ptr<float>(i)+j)) > value) {
    510                         response = false;
    511                         return response;
    512                     }
    513                 }
    514             }
    515         }
    516     }
    517 
    518     return response;
    519 }
    520 
    521 }
    522