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