targeter  1
software for EUCALL WP6 deliverable - pre-investigation of XFEL targets
openCVtrilateralFilter.cpp
1 #include "openCVtrilateralFilter.h"
2 #include <math.h>
3 #include "opencv2/opencv.hpp"
4 #include "opencv/highgui.h"
5 #include <vector>
6 using std::vector;
7 
8 bool OpenCVtrilateralFilter::trilateralFilter(
9  cv::Mat& inputImg,
10  cv::Mat& outputImg,
11  const float sigmaC,
12  const float epsilon,
13  const int filterType
14  )
15 {
16 
17  // Check sizes
18  if (inputImg.cols != outputImg.cols
19  || inputImg.rows != outputImg.rows
20  || inputImg.depth() != outputImg.depth()
21  || inputImg.channels() != outputImg.channels()
22  || inputImg.channels() != 1)
23  {
24  return false;
25  }
26 
27  if (inputImg.depth() != IPL_DEPTH_32F)
28  return false;
29 
30  // Adaptive Neighbourhood pixelwise image
31  cv::Mat adaptiveNeighbourhood = cv::Mat(inputImg.rows, inputImg.cols, inputImg.type(),cv::Scalar(1));
32 
33  // x and y gradients
34  cv::Mat xGradient = cv::Mat(inputImg.rows, inputImg.cols, inputImg.type(),cv::Scalar(1));
35  cv::Mat yGradient = cv::Mat(inputImg.rows, inputImg.cols, inputImg.type(),cv::Scalar(1));
36 
37  // Smoothed x and y gradients
38  cv::Mat xGradientSmooth = cv::Mat(inputImg.rows, inputImg.cols, inputImg.type(),cv::Scalar(1));
39  cv::Mat yGradientSmooth = cv::Mat(inputImg.rows, inputImg.cols, inputImg.type(),cv::Scalar(1));
40 
41  // Gradient magnitude
42  cv::Mat gradientMagnitude = cv::Mat(inputImg.rows, inputImg.cols, inputImg.type(),cv::Scalar(1));
43 
44  // domain variance for the two filters: sigmaC
45  // range variance of the two filters: sigmaR
46  // beta = emperical value
47  float sigmaR;
48  const float beta = 0.15f; //Always set between 0.1 and 0.2
49 
50  //Computes X and Y gradients of the input image
51  computeGradients(inputImg, xGradient, yGradient );
52 
53  // Computes the gradient magnitude
54  computeMagnitude(xGradient, yGradient, gradientMagnitude);
55 
56  // Get min and max gradient for sigmaR
57  double minGrad, maxGrad;
58  minMaxLoc(gradientMagnitude, &minGrad, &maxGrad);
59  sigmaR = beta * ( (float) maxGrad - (float) minGrad );
60 
61  // Level Max and Maximum LUT values
62  int levelMax, maxLUT;
63 
64  // If using LUT
65  if (filterType == TYPE_LUT)
66  {
67  // maximum level = log2(xsize) or log2(ysize)
68  levelMax = log2( __min( inputImg.cols, inputImg.rows ) );
69  maxLUT = pow( 2.f, levelMax-1) + 1;
70  }
71  else
72  {
73  // Using fast-trilateral
74  // Find threshold for truncation
75  maxLUT = int( sigmaC * sqrt( fabs( log(epsilon) ) ) ) + 1;
76  // Calculate maximum level
77  levelMax = log2(2 * maxLUT + 1, true);
78  }
79 
80  // Calculate Adaptive Neighbourhood
81  setAdaptiveNeighbourHood(gradientMagnitude, sigmaR, levelMax, adaptiveNeighbourhood);
82 
83  // Bilaterally filter the X and Y gradients of the input image
84  // to produce xGradientSmooth and yGradientSmooth.
85 
86  if (filterType == TYPE_LUT)
87  BilateralGradientFilterLUT(
88  xGradient,yGradient, gradientMagnitude, sigmaC, sigmaR,
89  xGradientSmooth, yGradientSmooth);
90  else
91  BilateralGradientFilter(
92  xGradient, yGradient, gradientMagnitude, sigmaC, sigmaR, epsilon,
93  xGradientSmooth, yGradientSmooth );
94 
95  // Performs bilateral filter on the detail signal
96 
97  if (filterType == TYPE_LUT)
98  DetailBilateralFilterLUT(
99  inputImg, adaptiveNeighbourhood, xGradientSmooth, yGradientSmooth,
100  sigmaC, sigmaR, maxLUT, outputImg);
101  else
102  DetailBilateralFilter(
103  inputImg, adaptiveNeighbourhood, xGradientSmooth, yGradientSmooth,
104  sigmaC, sigmaR, maxLUT, epsilon, outputImg);
105 
106  return true;
107 }
108 
109 // Computes X and Y gradients of the input image
110 void OpenCVtrilateralFilter::computeGradients(
111  cv::Mat& inputImg, cv::Mat& xGradient, cv::Mat& yGradient )
112 {
113 
114  // Set up convolution kernels for forward differences
115  float kernel[] = { -1, 1 };
116  cv::Mat xKernel = cv::Mat(1,2,CV_32FC1,kernel);
117  cv::Mat yKernel = cv::Mat(2,1,CV_32FC1,kernel);
118  cv::Point anchorPt = cv::Point(0,0);
119 
120  cv::filter2D(inputImg,xGradient, CV_32FC1, xKernel, anchorPt); // x gradient
121  cv::filter2D(inputImg,yGradient,CV_32FC1, yKernel, anchorPt); // y gradient
122 }
123 
124 // Computes the magnitude of the gradients
125 void OpenCVtrilateralFilter::computeMagnitude(
126  cv::Mat& xGradient, cv::Mat& yGradient,
127  cv::Mat& gradientMagnitude)
128 {
129  gradientMagnitude = cv::norm(xGradient-yGradient);
130  /*
131  float* xGrad = (float*) xGradient.data;
132  float* yGrad = (float*) yGradient.data;
133  float* gradMag = (float*) gradientMagnitude.data;
134 
135  for (int x = 0; x < xGradient.cols * xGradient.rows; x++)
136  gradMag[x] = cv::norm(xGrad[x], yGrad[x]);
137  */
138 }
139 
140 // Find the adaptive neighbourhood for image
141 void OpenCVtrilateralFilter::setAdaptiveNeighbourHood(
142  cv::Mat& gradientMagnitude, const float sigmaR, const int maxLevel,
143  cv::Mat& adaptiveNeighbourhood )
144 {
145  // Image stacks for max and min neighbourhoods
146  vector<cv::Mat> minGradientStack, maxGradientStack;
147  cv::Size imgSize = gradientMagnitude.size();
148 
149  // Create image stack
150  for(int i = 0 ; i < maxLevel ; i++)
151  minGradientStack.push_back( cv::Mat(imgSize,gradientMagnitude.depth(),1) );
152  for(int i = 0 ; i < maxLevel ; i++)
153  maxGradientStack.push_back( cv::Mat(imgSize,gradientMagnitude.depth(),1) );
154 
155  // Build the min-max stack
156  buildMinMaxImageStack(gradientMagnitude, minGradientStack, maxGradientStack );
157 
158  // Set up image data references
159  cv::Mat minImg, maxImg;
160  cv::Mat magImg = cv::Mat(gradientMagnitude);
161  cv::Mat adpImg = cv::Mat(adaptiveNeighbourhood);
162 
163  for(int y = 0; y < imgSize.width; y++)
164  {
165  for(int x = 0; x < imgSize.height; x++)
166  {
167  int lev;
168  const float upperThreshold = magImg.ptr(y)[x] + sigmaR;
169  const float lowerThreshold = magImg.ptr(y)[x] - sigmaR;
170 
171  // Compute the adaptive neighbourhood based on the similarity of
172  // the neighborhood gradients
173  for(lev = 0; lev < maxLevel; lev++)
174  {
175  minImg = minGradientStack[lev];
176  maxImg = maxGradientStack[lev];
177  if ( maxImg.ptr(y)[x] > upperThreshold || minImg.ptr(y)[x] < lowerThreshold )
178  break;
179  }
180 
181  // Sets the (half) size of the adaptive region
182  // i.e., floor( ( pow(2.f, lev) + 1 ) / 2.f )
183  adpImg.ptr(y)[x] = pow(2.f, lev-1);
184  }
185  }
186 }
187 
188 
189 // Building the Min-Max Stack of Image Gradients
190 void OpenCVtrilateralFilter::buildMinMaxImageStack(
191  cv::Mat& gradientMagnitude,
192  vector<cv::Mat> minStack, vector<cv::Mat> maxStack )
193 {
194  const int imgWidth = gradientMagnitude.cols;
195  const int imgHeight = gradientMagnitude.rows;
196 
197  // Set up image data references
198  cv::Mat minImg1, maxImg1, minImg2, maxImg2;
199  cv::Mat magImg = cv::Mat(gradientMagnitude);
200 
201  // Set up the bottom level of the pyramid
202  minImg1 = minStack[0];
203  maxImg1 = maxStack[0];
204 
205  // Loop through image setting up bottom stack
206  for(int y = 0; y < imgHeight ; y++)
207  {
208  for (int x = 0; x < imgWidth; x++)
209  {
210  float outMin = 1e12;
211  float outMax = -1e12;
212 
213  // Loop through local neighbourhood
214  // To find maximum and minimum values
215  for(int n = __max(y-1,0) ; n < __min(y+2, imgHeight); n++)
216  {
217  for(int m=__max(x-1,0); m < __min(x+2, imgWidth) ; m++)
218  {
219  outMin = __min(magImg.ptr(n)[m], outMin);
220  outMax = __max(magImg.ptr(n)[m], outMax);
221  }
222  }
223  minImg1.ptr(y)[x] = outMin;
224  maxImg1.ptr(y)[x] = outMax;
225  }
226  }
227 
228  // Loop through image stack
229  for (int i = 1 ; i < minStack.size(); i++)
230  {
231  // Lower level
232  minImg1 = minStack[i-1];
233  maxImg1 = maxStack[i-1];
234 
235  // Current level
236  minImg2 = minStack[i];
237  maxImg2 = maxStack[i];
238 
239  for(int y = 0; y < imgHeight ; y++)
240  {
241  for (int x = 0; x < imgWidth; x++)
242  {
243  float outMin = 1e12;
244  float outMax = -1e12;
245 
246  // Loop through local neighbourhood
247  // To find maximum and minimum values
248  for(int n = __max(y-1,0) ; n < __min(y+2, imgHeight); n++)
249  {
250  for(int m =__max(x-1,0); m < __min(x+2, imgWidth) ; m++)
251  {
252  outMin = __min(minImg1.ptr(n)[m], outMin);
253  outMax = __max(maxImg1.ptr(n)[m], outMax);
254  }
255  }
256  minImg2.ptr(y)[x] = outMin;
257  maxImg2.ptr(y)[x] = outMax;
258  }
259  }
260  }
261 }
262 
263 // =======================================================================
264 // Specific for fast version of Trilateral Fitler
265 // =======================================================================
266 
267 
268 // Bilaterally filters the X and Y gradients of the input image.
269 // To produce smoothed x and y gradients
270 
271 void OpenCVtrilateralFilter::BilateralGradientFilter(
272  cv::Mat& xGradient, cv::Mat& yGradient,
273  cv::Mat& gradientMagnitude,
274  const float sigmaC, const float sigmaR, const float epsilon,
275  cv::Mat& xGradientSmooth, cv::Mat& yGradientSmooth )
276 {
277  // Get image size
278  const int imgWidth = xGradient.cols;
279  const int imgHeight = xGradient.rows;
280 
281  // Constants used for domain / range calculations
282  const float domainConst = -2.f * sigmaC * sigmaC;
283  const float rangeConst = -2.f * sigmaR * sigmaR;
284 
285  // Compute the weight for the domain filter (domainWeight).
286  // The domain filter is a Gaussian lowpass filter
287  const int halfSize = int(sigmaC / 2.f);
288  cv::Mat domainWeightLUT =
289  cv::Mat(cvSize(halfSize+1,halfSize+1),xGradient.depth(),1);
290 
291  // Memory reference
292  cv::Mat domainWeight = cv::Mat(domainWeightLUT);
293 
294  for (int y = 0; y < domainWeightLUT.rows ; y++)
295  {
296  for (int x = 0; x < domainWeightLUT.cols ; x++)
297  {
298  // weight for the domain filter (domainWeight)
299  const float diff = (float) (x*x+y*y);
300  domainWeight.ptr(y)[x] = (float) exp( diff / domainConst );
301  }
302  }
303 
304  // Memory referencing
305  cv::Mat xImg = cv::Mat(xGradient);
306  cv::Mat yImg= cv::Mat(yGradient);
307  cv::Mat xSmoothImg = cv::Mat(xGradientSmooth);
308  cv::Mat ySmoothImg= cv::Mat(yGradientSmooth);
309  cv::Mat magImg= cv::Mat(gradientMagnitude);
310 
311 
312  // Loop through image
313  for(int y = 0; y < imgHeight ; y++)
314  {
315  for(int x = 0; x < imgWidth ; x++)
316  {
317  double normFactor = 0.f;
318  double tmpX = 0.f;
319  double tmpY = 0.f;
320 
321  // Calculate Middle Pixel Normalised-gradient
322  const float g2 = magImg.ptr(y)[x];
323 
324  // Loop through local neighbourhood
325  for (int n = -halfSize; n <= halfSize; n++)
326  {
327  for(int m = -halfSize; m <= halfSize; m++)
328  {
329  //Compute the weight for the domain filter (domainWeight).
330  const float dWeight = domainWeight.ptr(abs(n))[abs(m)];
331 
332  // Only perform calculation if weight above zero
333  if( dWeight < epsilon ) continue;
334 
335  // Only perform calculationg if within bounds
336  const int localX = x + m;
337  if (localX < 0) continue;
338  if (localX >= imgWidth) continue;
339 
340  const int localY = y + n;
341  if (localY < 0) continue;
342  if (localY >= imgHeight) continue;
343 
344  // Calculate Local Normalised Gradient
345  const float g1 = magImg.ptr(localY)[localX];
346 
347  //Compute the gradient difference between a pixel and its neighborhood pixel
348  const float gradDiffSq = (float) pow(g1 - g2, 2);
349 
350  // Compute the weight for the range filter (rangeWeight). The range filter
351  // is a Gaussian filter defined by the difference in gradient magnitude.
352  const float rangeWeight = (float) exp( gradDiffSq / rangeConst );
353 
354  // Only compute if less than epsilon
355  if (rangeWeight < epsilon) continue;
356 
357  tmpX += xImg.ptr(localY)[localX] * dWeight * rangeWeight;
358  tmpY += yImg.ptr(localY)[localX] * dWeight * rangeWeight;
359 
360  // Bilateral filter normalized by normFactor
361  normFactor += dWeight * rangeWeight;
362  }
363  }
364 
365  // Set smoothed image to normalised value
366  xSmoothImg.ptr(y)[x] = tmpX / normFactor;
367  ySmoothImg.ptr(y)[x] = tmpY / normFactor;
368  }
369  }
370 }
371 
372 
373 // Filters the detail signal and computes the output (2nd filtering pass for trilateral filter).
374 void OpenCVtrilateralFilter::DetailBilateralFilter(
375  cv::Mat& inputImage, cv::Mat& adaptiveRegion,
376  cv::Mat& xGradientSmooth, cv::Mat& yGradientSmooth,
377  const float sigmaC, const float sigmaR,
378  const int maxDomainSize, const float epsilon,
379  cv::Mat& outputImg)
380 {
381 
382  // Get image size
383  const int imgWidth = inputImage.cols;
384  const int imgHeight = inputImage.rows;
385 
386  // Create constants used throughout code
387  const double domainConst = -2.f * sigmaC * sigmaC;
388  const double rangeConst = -2.f * sigmaR * sigmaR;
389 
390  // Memory referencing
391  cv::Mat xImg = cv::Mat(xGradientSmooth);
392  cv::Mat yImg = cv::Mat(yGradientSmooth);
393  cv::Mat srcImg = cv::Mat(inputImage);
394  cv::Mat outImg = cv::Mat(outputImg);
395  cv::Mat adpImg = cv::Mat(adaptiveRegion);
396 
397  // Define Look Up Table for weightings
398 
399  // Compute the weight for the domain filter (domainWeight). The domain filter
400  // is a Gaussian lowpass filter
401  cv::Mat domainWeightLUT =
402  cv::Mat( cvSize(maxDomainSize+1,maxDomainSize+1), inputImage.depth(), 1);
403 
404  // Memory reference
405  cv::Mat domainWeight = cv::Mat(domainWeightLUT);
406 
407  for (int y = 0; y < domainWeightLUT.rows ; y++)
408  {
409  for (int x = 0; x < domainWeightLUT.cols ; x++)
410  {
411  // weight for the domain filter (domainWeight)
412  const float diff = (float) (x*x+y*y);
413  domainWeight.ptr(y)[x] = (float) exp( diff / domainConst );
414  }
415  }
416 
417  for(int y = 0; y < imgHeight; y++)
418  {
419  for(int x = 0; x < imgWidth; x++)
420  {
421  double normFactor = 0.f;
422  double tmp = 0.f;
423 
424  // filter window width is calculated from adaptive neighbourhood
425  // halfsize is half of the filter window width (or maximum LUT value)
426  const int halfSize = (int) __min( adpImg.ptr(y)[x], maxDomainSize );
427 
428  // Coefficients defining the centerplane is calculated
429  // from the smoothed image gradients
430  // Plane Equation is z = coeffA.x + coeffB.y + coeffC
431  // coeffA = dI/dx, coeffB = dI/dy, coeffC = I at center pixel of the filter kernel
432  const float coeffA = xImg.ptr(y)[x];
433  const float coeffB = yImg.ptr(y)[x];
434  const float coeffC = srcImg.ptr(y)[x];
435 
436  for (int n = -halfSize; n <= halfSize; n++) // y scan line
437  {
438  for(int m = -halfSize; m <= halfSize; m++) // x scan line
439  {
440  // Get domain weight from LUT
441  const float dWeight = domainWeight.ptr(abs(n))[abs(m)];
442 
443  // Only perform calculation if weight above zero
444  if( dWeight < epsilon ) continue;
445 
446  // Only perform calculationg if within bounds
447  const int localX = x + m;
448  if (localX < 0) continue;
449  if (localX >= imgWidth) continue;
450 
451  const int localY = y + n;
452  if (localY < 0) continue;
453  if (localY >= imgHeight) continue;
454 
455  // Compute the detail signal (detail) based on the difference between a
456  // neighborhood pixel and the centerplane passing through the center-pixel
457  // of the filter window.
458  const float detail =
459  srcImg.ptr(localY)[localX] - coeffA * float(m) - coeffB * float(n) - coeffC;
460 
461  // Compute the weight for the range filter (rangeWeight). The range filter
462  // is a Gaussian filter defined by the detail signal.
463  const float rangeWeight = exp( pow(detail,2) / rangeConst );
464 
465  if ( dWeight < epsilon ) continue;
466 
467  tmp += detail * dWeight * rangeWeight;
468 
469  //Detail Bilateral filter normalized by normFactor (eq. 9, Section 3.1)
470  normFactor += dWeight * rangeWeight;
471  }
472  }
473 
474  // Write result to output image
475  outImg.ptr(y)[x] = tmp / normFactor + coeffC;
476  }
477  }
478 }
479 
480 // =======================================================================
481 // Specific for LUT version of Trilateral Fitler
482 // =======================================================================
483 
484 // Bilaterally filters the X and Y gradients of the input image.
485 // To produce smoothed x and y gradients
486 
487 void OpenCVtrilateralFilter::BilateralGradientFilterLUT(
488  cv::Mat& xGradient, cv::Mat& yGradient,
489  cv::Mat& gradientMagnitude,
490  const float sigmaC, const float sigmaR,
491  cv::Mat& xGradientSmooth, cv::Mat& yGradientSmooth )
492 {
493  // Get image size
494  const int imgWidth = xGradient.cols;
495  const int imgHeight = xGradient.rows;
496 
497  // Constants used for domain / range calculations
498  const float domainConst = -2.f * sigmaC * sigmaC;
499  const float rangeConst = -2.f * sigmaR * sigmaR;
500 
501  // Compute the weight for the domain filter (domainWeight).
502  // The domain filter is a Gaussian lowpass filter
503  const int halfSize = int(sigmaC - 1.f / 2.f);
504  cv::Mat domainWeightLUT = cv::Mat(cv::Size(halfSize+1,halfSize+1), xGradient.depth(),1);
505 
506  // Memory reference
507  cv::Mat domainWeight = cv::Mat(domainWeightLUT);
508 
509  for (int y = 0; y < domainWeightLUT.rows ; y++)
510  {
511  for (int x = 0; x < domainWeightLUT.cols ; x++)
512  {
513  // weight for the domain filter (domainWeight)
514  const float diff = (float) (x*x+y*y);
515  domainWeight.ptr(y)[x] = (float) exp( diff / domainConst );
516  }
517  }
518 
519  // Memory referencing
520  cv::Mat xImg = cv::Mat(xGradient);
521  cv::Mat yImg = cv::Mat(yGradient);
522  cv::Mat xSmoothImg = cv::Mat(xGradientSmooth);
523  cv::Mat ySmoothImg = cv::Mat(yGradientSmooth);
524  cv::Mat magImg = cv::Mat(gradientMagnitude);
525 
526 
527  // Loop through image
528  for(int y = 0; y < imgHeight ; y++)
529  {
530  for(int x = 0; x < imgWidth ; x++)
531  {
532  double normFactor = 0.f;
533  double tmpX = 0.f;
534  double tmpY = 0.f;
535 
536  // Calculate Middle Pixel Normalised-gradient
537  const float g2 = magImg.ptr(y)[x];
538 
539  // Loop through local neighbourhood
540  for (int n = -halfSize; n <= halfSize; n++)
541  {
542  for(int m = -halfSize; m <= halfSize; m++)
543  {
544  //Compute the weight for the domain filter (domainWeight).
545  const float dWeight = domainWeight.ptr(abs(n))[abs(m)];
546 
547  // Only perform calculationg if within bounds
548  const int localX = x + m;
549  if (localX < 0) continue;
550  if (localX >= imgWidth) continue;
551 
552  const int localY = y + n;
553  if (localY < 0) continue;
554  if (localY >= imgHeight) continue;
555 
556  // Calculate Local Normalised Gradient
557  const float g1 = magImg.ptr(localY)[localX];
558 
559  // Compute the gradient difference between a pixel and its neighborhood pixel
560  const float gradDiffSq = (float) pow(g1 - g2, 2);
561 
562  // Compute the weight for the range filter (rangeWeight). The range filter
563  // is a Gaussian filter defined by the difference in gradient magnitude.
564  const float rangeWeight = (float) exp( gradDiffSq / rangeConst );
565 
566  tmpX += xImg.ptr(localY)[localX] * dWeight * rangeWeight;
567  tmpY += yImg.ptr(localY)[localX] * dWeight * rangeWeight;
568 
569  // Bilateral filter normalized by normFactor
570  normFactor += dWeight * rangeWeight;
571  }
572  }
573 
574  // Set smoothed image to normalised value
575  xSmoothImg.ptr(y)[x] = tmpX / normFactor;
576  ySmoothImg.ptr(y)[x] = tmpY / normFactor;
577  }
578  }
579 }
580 
581 
582 // Filters the detail signal and computes the output (2nd filtering pass for trilateral filter).
583 void OpenCVtrilateralFilter::DetailBilateralFilterLUT(
584  cv::Mat& inputImage, cv::Mat& adaptiveRegion,
585  cv::Mat& xGradientSmooth, cv::Mat& yGradientSmooth,
586  const float sigmaC, const float sigmaR,
587  const int maxDomainSize, cv::Mat& outputImg)
588 {
589 
590  // Get image size
591  const int imgWidth = inputImage.cols;
592  const int imgHeight = inputImage.rows;
593 
594  // Create constants used throughout code
595  const double domainConst = -2.f * sigmaC * sigmaC;
596  const double rangeConst = -2.f * sigmaR * sigmaR;
597 
598  // Memory referencing
599  cv::Mat xImg = cv::Mat(xGradientSmooth);
600  cv::Mat yImg = cv::Mat(yGradientSmooth);
601  cv::Mat srcImg = cv::Mat(inputImage);
602  cv::Mat outImg = cv::Mat(outputImg);
603  cv::Mat adpImg = cv::Mat(adaptiveRegion);
604 
605  // Define Look Up Table for weightings
606 
607  // Compute the weight for the domain filter (domainWeight). The domain filter
608  // is a Gaussian lowpass filter
609  cv::Mat domainWeightLUT = cv::Mat( cv::Size(maxDomainSize,maxDomainSize), inputImage.depth(), 1);
610 
611  // Memory reference
612  cv::Mat domainWeight = cv::Mat(domainWeightLUT);
613 
614  for (int y = 0; y < domainWeightLUT.rows ; y++)
615  {
616  for (int x = 0; x < domainWeightLUT.cols ; x++)
617  {
618  // weight for the domain filter (domainWeight)
619  const float diff = (float) (x*x+y*y);
620  domainWeight.ptr(y)[x] = (float) exp( diff / domainConst );
621  }
622  }
623 
624 
625  for(int y = 0; y < imgHeight; y++)
626  {
627  for(int x = 0; x < imgWidth; x++)
628  {
629  double normFactor = 0.f;
630  double tmp = 0.f;
631 
632  // filter window width is calculated from adaptive neighbourhood
633  // halfsize is half of the filter window width (or maximum LUT value)
634  const int halfSize = (int) adpImg.ptr(y)[x];
635 
636  // Coefficients defining the centerplane is calculated
637  // from the smoothed image gradients
638  // Plane Equation is z = coeffA.x + coeffB.y + coeffC
639  // coeffA = dI/dx, coeffB = dI/dy, coeffC = I at center pixel of the filter kernel
640  const float coeffA = xImg.ptr(y)[x];
641  const float coeffB = yImg.ptr(y)[x];
642  const float coeffC = srcImg.ptr(y)[x];
643 
644  for (int n = -halfSize; n <= halfSize; n++) // y scan line
645  {
646  for(int m = -halfSize; m <= halfSize; m++) // x scan line
647  {
648  // Get domain weight from LUT
649  const float dWeight = domainWeight.ptr(abs(n))[abs(m)];
650 
651  // Only perform calculationg if within bounds
652  const int localX = x + m;
653  if (localX < 0) continue;
654  if (localX >= imgWidth) continue;
655 
656  const int localY = y + n;
657  if (localY < 0) continue;
658  if (localY >= imgHeight) continue;
659 
660  // Compute the detail signal (detail) based on the difference between a
661  // neighborhood pixel and the centerplane passing through the center-pixel
662  // of the filter window.
663  const float detail =
664  srcImg.ptr(localY)[localX] - coeffA * float(m) - coeffB * float(n) - coeffC;
665 
666  // Compute the weight for the range filter (rangeWeight). The range filter
667  // is a Gaussian filter defined by the detail signal.
668  const float rangeWeight = exp( pow(detail,2) / rangeConst );
669 
670  // Add to function
671  tmp += detail * dWeight * rangeWeight;
672 
673  //Detail Bilateral filter normalized by normFactor
674  normFactor += dWeight * rangeWeight;
675  }
676  }
677 
678  // Normalise according to weight
679  outImg.ptr(y)[x] = tmp / normFactor + coeffC;
680  }
681  }
682 
683 
684 }
685