Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/features2d/src/kaze/nldiffusion_functions.cpp
16337 views
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: [email protected]
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_ = cvCeil(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(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
95
_dst.create(_Lx.size(), _Lx.type());
96
Mat Lx = _Lx.getMat();
97
Mat Ly = _Ly.getMat();
98
Mat dst = _dst.getMat();
99
100
Size sz = Lx.size();
101
float inv_k = 1.0f / (k*k);
102
for (int y = 0; y < sz.height; y++) {
103
104
const float* Lx_row = Lx.ptr<float>(y);
105
const float* Ly_row = Ly.ptr<float>(y);
106
float* dst_row = dst.ptr<float>(y);
107
108
for (int x = 0; x < sz.width; x++) {
109
dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
110
}
111
}
112
113
exp(dst, dst);
114
}
115
116
/* ************************************************************************* */
117
/**
118
* @brief This function computes the Perona and Malik conductivity coefficient g2
119
* g2 = 1 / (1 + dL^2 / k^2)
120
* @param Lx First order image derivative in X-direction (horizontal)
121
* @param Ly First order image derivative in Y-direction (vertical)
122
* @param dst Output image
123
* @param k Contrast factor parameter
124
*/
125
void pm_g2(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
126
CV_INSTRUMENT_REGION();
127
128
_dst.create(_Lx.size(), _Lx.type());
129
Mat Lx = _Lx.getMat();
130
Mat Ly = _Ly.getMat();
131
Mat dst = _dst.getMat();
132
133
Size sz = Lx.size();
134
dst.create(sz, Lx.type());
135
float k2inv = 1.0f / (k * k);
136
137
for(int y = 0; y < sz.height; y++) {
138
const float *Lx_row = Lx.ptr<float>(y);
139
const float *Ly_row = Ly.ptr<float>(y);
140
float* dst_row = dst.ptr<float>(y);
141
for(int x = 0; x < sz.width; x++) {
142
dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv));
143
}
144
}
145
}
146
/* ************************************************************************* */
147
/**
148
* @brief This function computes Weickert conductivity coefficient gw
149
* @param Lx First order image derivative in X-direction (horizontal)
150
* @param Ly First order image derivative in Y-direction (vertical)
151
* @param dst Output image
152
* @param k Contrast factor parameter
153
* @note For more information check the following paper: J. Weickert
154
* Applications of nonlinear diffusion in image processing and computer vision,
155
* Proceedings of Algorithmy 2000
156
*/
157
void weickert_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
158
_dst.create(_Lx.size(), _Lx.type());
159
Mat Lx = _Lx.getMat();
160
Mat Ly = _Ly.getMat();
161
Mat dst = _dst.getMat();
162
163
Size sz = Lx.size();
164
float inv_k = 1.0f / (k*k);
165
for (int y = 0; y < sz.height; y++) {
166
167
const float* Lx_row = Lx.ptr<float>(y);
168
const float* Ly_row = Ly.ptr<float>(y);
169
float* dst_row = dst.ptr<float>(y);
170
171
for (int x = 0; x < sz.width; x++) {
172
float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]);
173
dst_row[x] = -3.315f/(dL*dL*dL*dL);
174
}
175
}
176
177
exp(dst, dst);
178
dst = 1.0 - dst;
179
}
180
181
182
/* ************************************************************************* */
183
/**
184
* @brief This function computes Charbonnier conductivity coefficient gc
185
* gc = 1 / sqrt(1 + dL^2 / k^2)
186
* @param Lx First order image derivative in X-direction (horizontal)
187
* @param Ly First order image derivative in Y-direction (vertical)
188
* @param dst Output image
189
* @param k Contrast factor parameter
190
* @note For more information check the following paper: J. Weickert
191
* Applications of nonlinear diffusion in image processing and computer vision,
192
* Proceedings of Algorithmy 2000
193
*/
194
void charbonnier_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
195
_dst.create(_Lx.size(), _Lx.type());
196
Mat Lx = _Lx.getMat();
197
Mat Ly = _Ly.getMat();
198
Mat dst = _dst.getMat();
199
200
Size sz = Lx.size();
201
float inv_k = 1.0f / (k*k);
202
for (int y = 0; y < sz.height; y++) {
203
204
const float* Lx_row = Lx.ptr<float>(y);
205
const float* Ly_row = Ly.ptr<float>(y);
206
float* dst_row = dst.ptr<float>(y);
207
208
for (int x = 0; x < sz.width; x++) {
209
float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
210
dst_row[x] = 1.0f / den;
211
}
212
}
213
}
214
215
216
/* ************************************************************************* */
217
/**
218
* @brief This function computes a good empirical value for the k contrast factor
219
* given an input image, the percentile (0-1), the gradient scale and the number of
220
* bins in the histogram
221
* @param img Input image
222
* @param perc Percentile of the image gradient histogram (0-1)
223
* @param gscale Scale for computing the image gradient histogram
224
* @param nbins Number of histogram bins
225
* @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel
226
* @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel
227
* @return k contrast factor
228
*/
229
float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) {
230
CV_INSTRUMENT_REGION();
231
232
int nbin = 0, nelements = 0, nthreshold = 0, k = 0;
233
float kperc = 0.0, modg = 0.0;
234
float npoints = 0.0;
235
float hmax = 0.0;
236
237
// Create the array for the histogram
238
std::vector<int> hist(nbins, 0);
239
240
// Create the matrices
241
Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F);
242
Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F);
243
Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F);
244
245
// Perform the Gaussian convolution
246
gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale);
247
248
// Compute the Gaussian derivatives Lx and Ly
249
Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT);
250
Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT);
251
252
// Skip the borders for computing the histogram
253
for (int i = 1; i < gaussian.rows - 1; i++) {
254
const float *lx = Lx.ptr<float>(i);
255
const float *ly = Ly.ptr<float>(i);
256
for (int j = 1; j < gaussian.cols - 1; j++) {
257
modg = lx[j]*lx[j] + ly[j]*ly[j];
258
259
// Get the maximum
260
if (modg > hmax) {
261
hmax = modg;
262
}
263
}
264
}
265
hmax = sqrt(hmax);
266
// Skip the borders for computing the histogram
267
for (int i = 1; i < gaussian.rows - 1; i++) {
268
const float *lx = Lx.ptr<float>(i);
269
const float *ly = Ly.ptr<float>(i);
270
for (int j = 1; j < gaussian.cols - 1; j++) {
271
modg = lx[j]*lx[j] + ly[j]*ly[j];
272
273
// Find the correspondent bin
274
if (modg != 0.0) {
275
nbin = (int)floor(nbins*(sqrt(modg) / hmax));
276
277
if (nbin == nbins) {
278
nbin--;
279
}
280
281
hist[nbin]++;
282
npoints++;
283
}
284
}
285
}
286
287
// Now find the perc of the histogram percentile
288
nthreshold = (int)(npoints*perc);
289
290
for (k = 0; nelements < nthreshold && k < nbins; k++) {
291
nelements = nelements + hist[k];
292
}
293
294
if (nelements < nthreshold) {
295
kperc = 0.03f;
296
}
297
else {
298
kperc = hmax*((float)(k) / (float)nbins);
299
}
300
301
return kperc;
302
}
303
304
/* ************************************************************************* */
305
/**
306
* @brief This function computes Scharr image derivatives
307
* @param src Input image
308
* @param dst Output image
309
* @param xorder Derivative order in X-direction (horizontal)
310
* @param yorder Derivative order in Y-direction (vertical)
311
* @param scale Scale factor for the derivative size
312
*/
313
void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) {
314
Mat kx, ky;
315
compute_derivative_kernels(kx, ky, xorder, yorder, scale);
316
sepFilter2D(src, dst, CV_32F, kx, ky);
317
}
318
319
/* ************************************************************************* */
320
/**
321
* @brief Compute derivative kernels for sizes different than 3
322
* @param _kx Horizontal kernel ues
323
* @param _ky Vertical kernel values
324
* @param dx Derivative order in X-direction (horizontal)
325
* @param dy Derivative order in Y-direction (vertical)
326
* @param scale_ Scale factor or derivative size
327
*/
328
void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) {
329
CV_INSTRUMENT_REGION();
330
331
int ksize = 3 + 2 * (scale - 1);
332
333
// The standard Scharr kernel
334
if (scale == 1) {
335
getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F);
336
return;
337
}
338
339
_kx.create(ksize, 1, CV_32F, -1, true);
340
_ky.create(ksize, 1, CV_32F, -1, true);
341
Mat kx = _kx.getMat();
342
Mat ky = _ky.getMat();
343
std::vector<float> kerI;
344
345
float w = 10.0f / 3.0f;
346
float norm = 1.0f / (2.0f*scale*(w + 2.0f));
347
348
for (int k = 0; k < 2; k++) {
349
Mat* kernel = k == 0 ? &kx : &ky;
350
int order = k == 0 ? dx : dy;
351
kerI.assign(ksize, 0.0f);
352
353
if (order == 0) {
354
kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm;
355
}
356
else if (order == 1) {
357
kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1;
358
}
359
360
Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]);
361
temp.copyTo(*kernel);
362
}
363
}
364
365
class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody
366
{
367
public:
368
Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize)
369
: _Ld(&Ld)
370
, _c(&c)
371
, _Lstep(&Lstep)
372
, stepsize(_stepsize)
373
{
374
}
375
376
virtual ~Nld_Step_Scalar_Invoker()
377
{
378
379
}
380
381
void operator()(const cv::Range& range) const CV_OVERRIDE
382
{
383
cv::Mat& Ld = *_Ld;
384
const cv::Mat& c = *_c;
385
cv::Mat& Lstep = *_Lstep;
386
387
for (int i = range.start; i < range.end; i++)
388
{
389
const float *c_prev = c.ptr<float>(i - 1);
390
const float *c_curr = c.ptr<float>(i);
391
const float *c_next = c.ptr<float>(i + 1);
392
const float *ld_prev = Ld.ptr<float>(i - 1);
393
const float *ld_curr = Ld.ptr<float>(i);
394
const float *ld_next = Ld.ptr<float>(i + 1);
395
396
float *dst = Lstep.ptr<float>(i);
397
398
for (int j = 1; j < Lstep.cols - 1; j++)
399
{
400
float xpos = (c_curr[j] + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]);
401
float xneg = (c_curr[j-1] + c_curr[j]) *(ld_curr[j] - ld_curr[j-1]);
402
float ypos = (c_curr[j] + c_next[j]) *(ld_next[j] - ld_curr[j]);
403
float yneg = (c_prev[j] + c_curr[j]) *(ld_curr[j] - ld_prev[j]);
404
dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg);
405
}
406
}
407
}
408
private:
409
cv::Mat * _Ld;
410
const cv::Mat * _c;
411
cv::Mat * _Lstep;
412
float stepsize;
413
};
414
415
/* ************************************************************************* */
416
/**
417
* @brief This function performs a scalar non-linear diffusion step
418
* @param Ld2 Output image in the evolution
419
* @param c Conductivity image
420
* @param Lstep Previous image in the evolution
421
* @param stepsize The step size in time units
422
* @note Forward Euler Scheme 3x3 stencil
423
* The function c is a scalar value that depends on the gradient norm
424
* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy
425
*/
426
void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) {
427
CV_INSTRUMENT_REGION();
428
429
cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16));
430
431
float xneg, xpos, yneg, ypos;
432
float* dst = Lstep.ptr<float>(0);
433
const float* cprv = NULL;
434
const float* ccur = c.ptr<float>(0);
435
const float* cnxt = c.ptr<float>(1);
436
const float* ldprv = NULL;
437
const float* ldcur = Ld.ptr<float>(0);
438
const float* ldnxt = Ld.ptr<float>(1);
439
for (int j = 1; j < Lstep.cols - 1; j++) {
440
xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
441
xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
442
ypos = (ccur[j] + cnxt[j]) * (ldnxt[j] - ldcur[j]);
443
dst[j] = 0.5f*stepsize*(xpos - xneg + ypos);
444
}
445
446
dst = Lstep.ptr<float>(Lstep.rows - 1);
447
ccur = c.ptr<float>(Lstep.rows - 1);
448
cprv = c.ptr<float>(Lstep.rows - 2);
449
ldcur = Ld.ptr<float>(Lstep.rows - 1);
450
ldprv = Ld.ptr<float>(Lstep.rows - 2);
451
452
for (int j = 1; j < Lstep.cols - 1; j++) {
453
xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
454
xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
455
yneg = (cprv[j] + ccur[j]) * (ldcur[j] - ldprv[j]);
456
dst[j] = 0.5f*stepsize*(xpos - xneg - yneg);
457
}
458
459
ccur = c.ptr<float>(1);
460
ldcur = Ld.ptr<float>(1);
461
cprv = c.ptr<float>(0);
462
ldprv = Ld.ptr<float>(0);
463
464
int r0 = Lstep.cols - 1;
465
int r1 = Lstep.cols - 2;
466
467
for (int i = 1; i < Lstep.rows - 1; i++) {
468
cnxt = c.ptr<float>(i + 1);
469
ldnxt = Ld.ptr<float>(i + 1);
470
dst = Lstep.ptr<float>(i);
471
472
xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]);
473
ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]);
474
yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]);
475
dst[0] = 0.5f*stepsize*(xpos + ypos - yneg);
476
477
xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]);
478
ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]);
479
yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]);
480
dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg);
481
482
cprv = ccur;
483
ccur = cnxt;
484
ldprv = ldcur;
485
ldcur = ldnxt;
486
}
487
Ld += Lstep;
488
}
489
490
/* ************************************************************************* */
491
/**
492
* @brief This function downsamples the input image using OpenCV resize
493
* @param img Input image to be downsampled
494
* @param dst Output image with half of the resolution of the input image
495
*/
496
void halfsample_image(const cv::Mat& src, cv::Mat& dst) {
497
// Make sure the destination image is of the right size
498
CV_Assert(src.cols / 2 == dst.cols);
499
CV_Assert(src.rows / 2 == dst.rows);
500
resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA);
501
}
502
503
/* ************************************************************************* */
504
/**
505
* @brief This function checks if a given pixel is a maximum in a local neighbourhood
506
* @param img Input image where we will perform the maximum search
507
* @param dsize Half size of the neighbourhood
508
* @param value Response value at (x,y) position
509
* @param row Image row coordinate
510
* @param col Image column coordinate
511
* @param same_img Flag to indicate if the image value at (x,y) is in the input image
512
* @return 1->is maximum, 0->otherwise
513
*/
514
bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) {
515
516
bool response = true;
517
518
for (int i = row - dsize; i <= row + dsize; i++) {
519
for (int j = col - dsize; j <= col + dsize; j++) {
520
if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) {
521
if (same_img == true) {
522
if (i != row || j != col) {
523
if ((*(img.ptr<float>(i)+j)) > value) {
524
response = false;
525
return response;
526
}
527
}
528
}
529
else {
530
if ((*(img.ptr<float>(i)+j)) > value) {
531
response = false;
532
return response;
533
}
534
}
535
}
536
}
537
}
538
539
return response;
540
}
541
542
}
543
544