1 /**
2  * @file fastbilateral.cpp
3  * @brief Fast bilateral filtering
4  *
5  *
6  * This file is a part of PFSTMO package.
7  * ----------------------------------------------------------------------
8  * Copyright (C) 2003,2004 Grzegorz Krawczyk
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  * ----------------------------------------------------------------------
24  *
25  * @author Grzegorz Krawczyk, <krawczyk@mpi-sb.mpg.de>
26  *
27  * $Id: fastbilateral.cpp,v 1.5 2008/09/09 18:10:49 rafm Exp $
28  */
29 
30 #include <config.h>
31 
32 #include <iostream>
33 
34 #include <fftw3.h>
35 #include <math.h>
36 
37 #include "pfstmo.h"
38 
39 
40 using namespace std;
41 
42 
max(float a,float b)43 inline float max( float a, float b )
44 {
45   return a > b ? a : b;
46 }
47 
min(float a,float b)48 inline float min( float a, float b )
49 {
50   return a < b ? a : b;
51 }
52 
53 
54 // TODO: use spatial convolution rather than FFT, should be much
55 // faster except for a very large kernels
56 class GaussianBlur
57 {
58   float* source;
59   fftwf_complex* freq;
60   fftwf_plan fplan_fw;
61   fftwf_plan fplan_in;
62 
63   float sigma;
64 
65 public:
GaussianBlur(int nx,int ny,float sigma)66   GaussianBlur( int nx, int ny, float sigma ) : sigma( sigma )
67   {
68     int ox = nx;
69     int oy = ny/2 + 1;            // saves half of the data
70     const int osize = ox * oy;
71     source =  (float*)fftwf_malloc(sizeof(float) * nx * 2 * (ny/2+1) );
72     freq = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex) * osize);
73 //    if( source == NULL || freq == NULL )
74     //TODO: throw exception
75     fplan_fw = fftwf_plan_dft_r2c_2d(nx, ny, source, freq, FFTW_ESTIMATE);
76     fplan_in = fftwf_plan_dft_c2r_2d(nx, ny, freq, source, FFTW_ESTIMATE);
77   }
78 
79 
blur(const pfstmo::Array2D * I,pfstmo::Array2D * J)80   void blur( const pfstmo::Array2D *I, pfstmo::Array2D *J )
81   {
82     int i,x,y;
83 
84     int nx = I->getCols();
85     int ny = I->getRows();
86     int nsize = nx * ny;
87 
88     int ox = nx;
89     int oy = ny/2 + 1;            // saves half of the data
90     int osize = ox * oy;
91 
92     for( y=0 ; y<ny ; y++ )
93       for( x=0 ; x<nx ; x++ )
94         source[x*ny+y] = (*I)(x,y);
95 
96     fftwf_execute(fplan_fw);
97 
98     // filter
99     float sig = nx/(2.0f*sigma);
100     float sig2 = 2.0f*sig*sig;
101     for( x=0 ; x<ox/2 ; x++ )
102       for( y=0 ; y<oy ; y++ )
103       {
104         float d2 = x*x + y*y;
105         float kernel = exp( -d2 / sig2 );
106 
107         freq[x*oy+y][0] *= kernel;
108         freq[x*oy+y][1] *= kernel;
109         freq[(ox-x-1)*oy+y][0] *= kernel;
110         freq[(ox-x-1)*oy+y][1] *= kernel;
111       }
112 
113     fftwf_execute(fplan_in);
114 
115     for( x=0 ; x<nx ; x++ )
116       for( y=0 ; y<ny ; y++ )
117         (*J)(x,y) = source[x*ny+y] / nsize;
118   }
119 
~GaussianBlur()120   ~GaussianBlur()
121   {
122     fftwf_free(source);
123     fftwf_free(freq);
124     fftwf_destroy_plan(fplan_fw);
125     fftwf_destroy_plan(fplan_in);
126   }
127 
128 
129 };
130 
131 
132 // According to the original paper, downsampling can be used to speed
133 // up computation. However, downsampling cannot be mathematically
134 // justified and introduces large errors (mostly excessive bluring) in
135 // the result of the bilateral filter. Therefore, in this
136 // implementation, downsampling is disabled.
137 
138 #if 0
139 /**
140  * @brief upsampling and downsampling
141  *
142  * original code from pfssize
143  */
144 void upsampleArray( const pfstmo::Array2D *in, pfstmo::Array2D *out )
145 {
146   float dx = (float)in->getCols() / (float)out->getCols();
147   float dy = (float)in->getRows() / (float)out->getRows();
148 
149   float pad;
150 
151   float filterSamplingX = max( modff( dx, &pad ), 0.01f );
152   float filterSamplingY = max( modff( dy, &pad ), 0.01f );
153 
154   const int outRows = out->getRows();
155   const int outCols = out->getCols();
156 
157   const float inRows = in->getRows();
158   const float inCols = in->getCols();
159 
160   const float filterSize = 1;
161 
162   float sx, sy;
163   int x, y;
164   for( y = 0, sy = -0.5 + dy/2; y < outRows; y++, sy += dy )
165     for( x = 0, sx = -0.5 + dx/2; x < outCols; x++, sx += dx ) {
166 
167       float pixVal = 0;
168       float weight = 0;
169 
170       for( float ix = max( 0, ceilf( sx-filterSize ) ); ix <= min( floorf(sx+filterSize), inCols-1 ); ix++ )
171         for( float iy = max( 0, ceilf( sy-filterSize ) ); iy <= min( floorf( sy+filterSize), inRows-1 ); iy++ ) {
172           float fx = fabs( sx - ix );
173           float fy = fabs( sy - iy );
174 
175           const float fval = (1.0f-fx)*(1.0f-fy);
176 
177           pixVal += (*in)( (int)ix, (int)iy ) * fval;
178           weight += fval;
179         }
180 
181       if( weight == 0 ) {
182         fprintf( stderr, "%g %g %g %g\n", sx, sy, dx, dy );
183       }
184 //      assert( weight != 0 );
185       (*out)(x,y) = pixVal / weight;
186 
187     }
188 }
189 
190 void downsampleArray( const pfstmo::Array2D *in, pfstmo::Array2D *out )
191 {
192   const float inRows = in->getRows();
193   const float inCols = in->getCols();
194 
195   const int outRows = out->getRows();
196   const int outCols = out->getCols();
197 
198   const float dx = (float)in->getCols() / (float)out->getCols();
199   const float dy = (float)in->getRows() / (float)out->getRows();
200 
201   const float filterSize = 0.5;
202 
203   float sx, sy;
204   int x, y;
205 
206   for( y = 0, sy = dy/2-0.5; y < outRows; y++, sy += dy )
207     for( x = 0, sx = dx/2-0.5; x < outCols; x++, sx += dx ) {
208 
209       float pixVal = 0;
210       float w = 0;
211       for( float ix = max( 0, ceilf( sx-dx*filterSize ) ); ix <= min( floorf( sx+dx*filterSize ), inCols-1 ); ix++ )
212         for( float iy = max( 0, ceilf( sy-dx*filterSize ) ); iy <= min( floorf( sy+dx*filterSize), inRows-1 ); iy++ ) {
213           pixVal += (*in)( (int)ix, (int)iy );
214           w += 1;
215         }
216       (*out)(x,y) = pixVal/w;
217     }
218 }
219 
220 #endif
221 
222 /*
223 Pseudocode from the paper:
224 
225 PiecewiseBilateral (Image I, spatial kernel fs , intensity influence gr )
226   J=0 // set the output to zero
227   for j=0..NB SEGMENTS
228     ij= minI+j.*(max(I)-min(I))/NB SEGMENTS
229     Gj=gr (I - ij ) // evaluate gr at each pixel
230     Kj=Gj x fs // normalization factor
231     Hj=Gj .* I // compute H for each pixel
232     Hj=Hj x fs
233     Jj=Hj ./ Kj // normalize
234     J=J+Jj .*  InterpolationWeight(I, ij )
235 */
236 
fastBilateralFilter(const pfstmo::Array2D * I,pfstmo::Array2D * J,float sigma_s,float sigma_r,int downsample,pfstmo_progress_callback progress_cb)237 void fastBilateralFilter( const pfstmo::Array2D *I,
238   pfstmo::Array2D *J, float sigma_s, float sigma_r, int downsample,
239   pfstmo_progress_callback progress_cb )
240 {
241   int i;
242   int w = I->getCols();
243   int h = I->getRows();
244   int size = w * h;
245 
246   // find range of values in the input array
247   float maxI = (*I)(0);
248   float minI = (*I)(0);
249   for(i=0 ; i<size ; i++)
250   {
251     float v = (*I)(i);
252     if( unlikely( v>maxI ) ) maxI = v;
253     if( unlikely( v<minI ) ) minI = v;
254     (*J)(i) = 0.0f;             // zero output
255   }
256 
257   pfstmo::Array2D* JJ;
258 //  if( downsample != 1 )
259 //    JJ = new pfstmo::Array2D(w,h);
260 
261 //  w /= downsample;
262 //  h /= downsample;
263 
264   int sizeZ = w*h;
265 //  pfstmo::Array2D* Iz = new pfstmo::Array2D(w,h);
266 //  downsampleArray(I,Iz);
267   const pfstmo::Array2D* Iz = I;
268 //  sigma_s /= downsample;
269 
270   pfstmo::Array2D* jJ = new pfstmo::Array2D(w,h);
271   pfstmo::Array2D* jG = new pfstmo::Array2D(w,h);
272   pfstmo::Array2D* jK = new pfstmo::Array2D(w,h);
273   pfstmo::Array2D* jH = new pfstmo::Array2D(w,h);
274 
275   const int NB_SEGMENTS = (int)ceil((maxI-minI)/sigma_r);
276   float stepI = (maxI-minI)/NB_SEGMENTS;
277 
278   GaussianBlur gaussian_blur( w, h, sigma_s );
279 
280   // piecewise bilateral
281   for( int j=0 ; j<NB_SEGMENTS ; j++ )
282   {
283     progress_cb( j * 100 / NB_SEGMENTS );
284     float jI = minI + j*stepI;        // current intensity value
285 
286     for( i=0 ; i<sizeZ ; i++ )
287     {
288       float dI = (*Iz)(i)-jI;
289       (*jG)(i) = exp( -(dI*dI) / (sigma_r*sigma_r) );
290       (*jH)(i) = (*jG)(i) * (*I)(i);
291     }
292 
293     gaussian_blur.blur( jG, jK );
294     gaussian_blur.blur( jH, jH );
295 
296 //    convolveArray(jG, sigma_s, jK);
297 //    convolveArray(jH, sigma_s, jH);
298 
299     for( i=0 ; i<sizeZ ; i++ )
300       if( likely((*jK)(i)!=0.0f) )
301         (*jJ)(i) = (*jH)(i) / (*jK)(i);
302       else
303         (*jJ)(i) = 0.0f;
304 
305     //  if( downsample == 1 )
306       JJ = jJ;                  // No upsampling is necessary
307 //    else
308 //      upsampleArray(jJ,JJ);
309 
310     if( j == 0 ) {
311       // if the first segment - to account for the range boundary
312       for( i=0 ; i<size ; i++ )
313       {
314         if( likely( (*I)(i) > jI + stepI ) )
315           continue;             // wi = 0;
316         if( likely( (*I)(i) > jI ) ) {
317           float wi = (stepI - ((*I)(i)-jI)) / stepI;
318           (*J)(i) += (*JJ)(i)*wi;
319         } else
320           (*J)(i) += (*JJ)(i);
321       }
322     } else if( j == NB_SEGMENTS-1 ) {
323       // if the last segment - to account for the range boundary
324       for( i=0 ; i<size ; i++ )
325       {
326         if( likely( (*I)(i) < jI - stepI ) )
327           continue;             // wi = 0;
328         if( likely( (*I)(i) < jI ) ) {
329           float wi = (stepI - (jI-(*I)(i))) / stepI;
330           (*J)(i) += (*JJ)(i)*wi;
331         } else
332           (*J)(i) += (*JJ)(i);
333       }
334     } else {
335       for( i=0 ; i<size ; i++ )
336       {
337         float wi = (stepI - fabs( (*I)(i)-jI )) / stepI;
338         if( unlikely( wi>0.0f ) )
339           (*J)(i) += (*JJ)(i)*wi;
340       }
341     }
342   }
343 
344 //  delete Iz;
345 //  if( downsample != 1 )
346 //    delete JJ;
347   delete jJ;
348   delete jG;
349   delete jK;
350   delete jH;
351 }
352