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@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  */
gaussian_2D_convolution(const cv::Mat & src,cv::Mat & dst,int ksize_x,int ksize_y,float sigma)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  */
image_derivatives_scharr(const cv::Mat & src,cv::Mat & dst,int xorder,int yorder)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  */
pm_g1(const cv::Mat & Lx,const cv::Mat & Ly,cv::Mat & dst,float k)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  */
pm_g2(const cv::Mat & Lx,const cv::Mat & Ly,cv::Mat & dst,float k)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  */
weickert_diffusivity(const cv::Mat & Lx,const cv::Mat & Ly,cv::Mat & dst,float k)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 */
charbonnier_diffusivity(const cv::Mat & Lx,const cv::Mat & Ly,cv::Mat & dst,float k)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  */
compute_k_percentile(const cv::Mat & img,float perc,float gscale,int nbins,int ksize_x,int ksize_y)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  */
compute_scharr_derivatives(const cv::Mat & src,cv::Mat & dst,int xorder,int yorder,int scale)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  */
compute_derivative_kernels(cv::OutputArray _kx,cv::OutputArray _ky,int dx,int dy,int scale)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:
Nld_Step_Scalar_Invoker(cv::Mat & Ld,const cv::Mat & c,cv::Mat & Lstep,float _stepsize)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 
~Nld_Step_Scalar_Invoker()355     virtual ~Nld_Step_Scalar_Invoker()
356     {
357 
358     }
359 
operator ()(const cv::Range & range) const360     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 */
nld_step_scalar(cv::Mat & Ld,const cv::Mat & c,cv::Mat & Lstep,float stepsize)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 */
halfsample_image(const cv::Mat & src,cv::Mat & dst)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  */
check_maximum_neighbourhood(const cv::Mat & img,int dsize,float value,int row,int col,bool same_img)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