Wed, 05 Aug 2009 17:32:05 +0100
updated README
philpem@5 | 1 | /* |
philpem@5 | 2 | # |
philpem@5 | 3 | # File : nlmeans.h |
philpem@5 | 4 | # ( C++ header file - CImg plug-in ) |
philpem@5 | 5 | # |
philpem@5 | 6 | # Description : CImg plugin that implements the non-local mean filter. |
philpem@5 | 7 | # This file is a part of the CImg Library project. |
philpem@5 | 8 | # ( http://cimg.sourceforge.net ) |
philpem@5 | 9 | # |
philpem@5 | 10 | # [1] Buades, A.; Coll, B.; Morel, J.-M.: A non-local algorithm for image denoising |
philpem@5 | 11 | # IEEE Computer Society Conference on Computer Vision and Pattern Recognition, 2005. CVPR 2005. |
philpem@5 | 12 | # Volume 2, 20-25 June 2005 Page(s):60 - 65 vol. 2 |
philpem@5 | 13 | # |
philpem@5 | 14 | # [2] Buades, A. Coll, B. and Morel, J.: A review of image denoising algorithms, with a new one. |
philpem@5 | 15 | # Multiscale Modeling and Simulation: A SIAM Interdisciplinary Journal 4 (2004) 490-530 |
philpem@5 | 16 | # |
philpem@5 | 17 | # [3] Gasser, T. Sroka,L. Jennen Steinmetz,C. Residual variance and residual pattern nonlinear regression. |
philpem@5 | 18 | # Biometrika 73 (1986) 625-659 |
philpem@5 | 19 | # |
philpem@5 | 20 | # Copyright : Jerome Boulanger |
philpem@5 | 21 | # ( http://www.irisa.fr/vista/Equipe/People/Jerome.Boulanger.html ) |
philpem@5 | 22 | # |
philpem@5 | 23 | # License : CeCILL v2.0 |
philpem@5 | 24 | # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) |
philpem@5 | 25 | # |
philpem@5 | 26 | # This software is governed by the CeCILL license under French law and |
philpem@5 | 27 | # abiding by the rules of distribution of free software. You can use, |
philpem@5 | 28 | # modify and/ or redistribute the software under the terms of the CeCILL |
philpem@5 | 29 | # license as circulated by CEA, CNRS and INRIA at the following URL |
philpem@5 | 30 | # "http://www.cecill.info". |
philpem@5 | 31 | # |
philpem@5 | 32 | # As a counterpart to the access to the source code and rights to copy, |
philpem@5 | 33 | # modify and redistribute granted by the license, users are provided only |
philpem@5 | 34 | # with a limited warranty and the software's author, the holder of the |
philpem@5 | 35 | # economic rights, and the successive licensors have only limited |
philpem@5 | 36 | # liability. |
philpem@5 | 37 | # |
philpem@5 | 38 | # In this respect, the user's attention is drawn to the risks associated |
philpem@5 | 39 | # with loading, using, modifying and/or developing or reproducing the |
philpem@5 | 40 | # software by the user in light of its specific status of free software, |
philpem@5 | 41 | # that may mean that it is complicated to manipulate, and that also |
philpem@5 | 42 | # therefore means that it is reserved for developers and experienced |
philpem@5 | 43 | # professionals having in-depth computer knowledge. Users are therefore |
philpem@5 | 44 | # encouraged to load and test the software's suitability as regards their |
philpem@5 | 45 | # requirements in conditions enabling the security of their systems and/or |
philpem@5 | 46 | # data to be ensured and, more generally, to use and operate it in the |
philpem@5 | 47 | # same conditions as regards security. |
philpem@5 | 48 | # |
philpem@5 | 49 | # The fact that you are presently reading this means that you have had |
philpem@5 | 50 | # knowledge of the CeCILL license and that you accept its terms. |
philpem@5 | 51 | # |
philpem@5 | 52 | */ |
philpem@5 | 53 | |
philpem@5 | 54 | #ifndef cimg_plugin_nlmeans |
philpem@5 | 55 | #define cimg_plugin_nlmeans |
philpem@5 | 56 | |
philpem@5 | 57 | #include "noise_analysis.h" |
philpem@5 | 58 | |
philpem@5 | 59 | //! NL-Means denoising algorithm. |
philpem@5 | 60 | /** |
philpem@5 | 61 | This is the in-place version of get_nlmean(). |
philpem@5 | 62 | **/ |
philpem@5 | 63 | CImg<T>& nlmeans(int patch_size=1, double lambda=-1, double alpha=3, double sigma=-1, int sampling=1){ |
philpem@5 | 64 | if (!is_empty()){ |
philpem@5 | 65 | if (sigma<0) sigma=std::sqrt(noise_variance()); // noise variance estimation |
philpem@5 | 66 | double np=(2*patch_size+1)*(2*patch_size+1)*dimv()/(double)sampling; |
philpem@5 | 67 | if (lambda<0) {// Bandwidth estimation |
philpem@5 | 68 | if (np<100) |
philpem@5 | 69 | lambda =(((((( 1.1785e-12*np -5.1827e-10)*np+ 9.5946e-08)*np -9.7798e-06)*np+ 6.0756e-04)*np -0.0248)*np+ 1.9203)*np +7.9599; |
philpem@5 | 70 | else |
philpem@5 | 71 | lambda = (-7.2611e-04*np+ 1.3213)*np+ 15.2726; |
philpem@5 | 72 | } |
philpem@5 | 73 | #if cimg_debug>=1 |
philpem@5 | 74 | std::fprintf(stderr,"Size of the patch : %dx%d \n", |
philpem@5 | 75 | 2*patch_size+1,2*patch_size+1); |
philpem@5 | 76 | std::fprintf(stderr,"Size of window where similar patch are looked for : %dx%d \n", |
philpem@5 | 77 | (int)(alpha*(2*patch_size+1)),(int)(alpha*(2*patch_size+1))); |
philpem@5 | 78 | std::fprintf(stderr,"Bandwidth of the kernel : %fx%f^2 \n", |
philpem@5 | 79 | lambda,sigma); |
philpem@5 | 80 | std::fprintf(stderr,"Noise standard deviation estimated to : %f \n", |
philpem@5 | 81 | sigma); |
philpem@5 | 82 | #endif |
philpem@5 | 83 | |
philpem@5 | 84 | CImg<T> dest(dimx(),dimy(),dimz(),dimv(),0); |
philpem@5 | 85 | double * uhat = new double[dimv()]; |
philpem@5 | 86 | double h2=-.5/(lambda*sigma*sigma); // [Kervrann] notations |
philpem@5 | 87 | if (dimz()!=1){// 3D case |
philpem@5 | 88 | CImg<> P=(*this).get_blur(1); // inspired from Mahmoudi&Sapiro SPletter dec 05 |
philpem@5 | 89 | int n_simu=64; |
philpem@5 | 90 | CImg<> tmp(n_simu,n_simu,n_simu); |
philpem@5 | 91 | double sig = std::sqrt(tmp.fill(0.f).noise(sigma).blur(1).pow(2.).sum()/(n_simu*n_simu*n_simu)); |
philpem@5 | 92 | int patch_size_z=0; |
philpem@5 | 93 | int pxi=(int)(alpha*patch_size), |
philpem@5 | 94 | pyi=(int)(alpha*patch_size), |
philpem@5 | 95 | pzi=2;//Define the size of the neighborhood in z |
philpem@5 | 96 | for (int zi=0;zi<dimz();zi++){ |
philpem@5 | 97 | #if cimg_debug>=1 |
philpem@5 | 98 | std::fprintf(stderr,"\rProcessing : %3d %%",(int)((float)zi/(float)dimz()*100.));fflush(stdout); |
philpem@5 | 99 | #endif |
philpem@5 | 100 | for (int yi=0;yi<dimy();yi++) |
philpem@5 | 101 | for (int xi=0;xi<dimx();xi++){ |
philpem@5 | 102 | for (int v=0;v<dimv();v++) uhat[v] = 0; |
philpem@5 | 103 | float sw=0,wmax=-1; |
philpem@5 | 104 | for (int zj=cimg::max(0,zi-pzi);zj<cimg::min(dimz(),zi+pzi+1);zj++) |
philpem@5 | 105 | for (int yj=cimg::max(0,yi-pyi);yj<cimg::min(dimy(),yi+pyi+1);yj++) |
philpem@5 | 106 | for (int xj=cimg::max(0,xi-pxi);xj<cimg::min(dimx(),xi+pxi+1);xj++) |
philpem@5 | 107 | if( cimg::abs(P(xi,yi,zi)-P(xj,yj,zj))/sig < 3){ |
philpem@5 | 108 | double d = 0; |
philpem@5 | 109 | int n = 0; |
philpem@5 | 110 | if (xi!=xj && yi!=yj && zi!=zj){ |
philpem@5 | 111 | for (int kz=-patch_size_z;kz<patch_size_z+1;kz+=sampling){ |
philpem@5 | 112 | int zj_ = zj+kz; |
philpem@5 | 113 | int zi_ = zi+kz; |
philpem@5 | 114 | if (zj_>=0 && zj_<dimz() && zi_>=0 && zi_<dimz()) |
philpem@5 | 115 | for (int ky=-patch_size;ky<=patch_size;ky+=sampling){ |
philpem@5 | 116 | int yj_ = yj+ky; |
philpem@5 | 117 | int yi_ = yi+ky; |
philpem@5 | 118 | if (yj_>=0 && yj_<dimy() && yi_>=0 && yi_<dimy()) |
philpem@5 | 119 | for (int kx=-patch_size;kx<=patch_size;kx+=sampling){ |
philpem@5 | 120 | int xj_ = xj+kx; |
philpem@5 | 121 | int xi_ = xi+kx; |
philpem@5 | 122 | if (xj_>=0 && xj_<dimx() && xi_>=0 && xi_<dimx()) |
philpem@5 | 123 | for (int v=0;v<dimv();v++){ |
philpem@5 | 124 | double d1 = (*this)(xj_,yj_,zj_,v)-(*this)(xi_,yi_,zi_,v); |
philpem@5 | 125 | d += d1*d1; |
philpem@5 | 126 | n++; |
philpem@5 | 127 | } |
philpem@5 | 128 | } |
philpem@5 | 129 | } |
philpem@5 | 130 | } |
philpem@5 | 131 | float w = (float)std::exp(d*h2); |
philpem@5 | 132 | wmax = w>wmax?w:wmax; |
philpem@5 | 133 | for (int v=0;v<dimv();v++) uhat[v] += w*(*this)(xj,yj,zj,v); |
philpem@5 | 134 | sw += w; |
philpem@5 | 135 | } |
philpem@5 | 136 | } |
philpem@5 | 137 | // add the central pixel |
philpem@5 | 138 | { for (int v=0;v<dimv();v++) uhat[v] += wmax*(*this)(xi,yi,zi,v); } |
philpem@5 | 139 | sw += wmax; |
philpem@5 | 140 | { for (int v=0;v<dimv();v++) dest(xi,yi,zi,v)= (T) (uhat[v] /= sw); } |
philpem@5 | 141 | } |
philpem@5 | 142 | } |
philpem@5 | 143 | } |
philpem@5 | 144 | else{ // 2D case |
philpem@5 | 145 | CImg<> P=(*this).get_blur(1); // inspired from Mahmoudi&Sapiro SPletter dec 05 |
philpem@5 | 146 | int n_simu=512; |
philpem@5 | 147 | CImg<> tmp(n_simu,n_simu); |
philpem@5 | 148 | double sig = std::sqrt(tmp.fill(0.f).noise(sigma).blur(1).pow(2.).sum()/(n_simu*n_simu)); |
philpem@5 | 149 | int pxi=(int)(alpha*patch_size),pyi=(int)(alpha*patch_size);//Define the size of the neighborhood |
philpem@5 | 150 | for (int yi=0;yi<dimy();yi++){ |
philpem@5 | 151 | #if cimg_debug>=1 |
philpem@5 | 152 | std::fprintf(stderr,"\rProcessing : %3d %%",(int)((float)yi/(float)dimy()*100.));fflush(stdout); |
philpem@5 | 153 | #endif |
philpem@5 | 154 | for (int xi=0;xi<dimx();xi++){ |
philpem@5 | 155 | for (int v=0;v<dimv();v++) uhat[v] = 0; |
philpem@5 | 156 | float sw=0,wmax=-1; |
philpem@5 | 157 | for (int yj=cimg::max(0,yi-pyi);yj<cimg::min(dimy(),yi+pyi+1);yj++) |
philpem@5 | 158 | for (int xj=cimg::max(0,xi-pxi);xj<cimg::min(dimx(),xi+pxi+1);xj++) |
philpem@5 | 159 | if( cimg::abs(P(xi,yi)-P(xj,yj))/sig < 3.){ |
philpem@5 | 160 | double d = 0; |
philpem@5 | 161 | int n = 0; |
philpem@5 | 162 | if (!(xi==xj && yi==yj)) |
philpem@5 | 163 | for (int ky=-patch_size;ky<patch_size+1;ky+=sampling){ |
philpem@5 | 164 | int yj_ = yj+ky; |
philpem@5 | 165 | int yi_ = yi+ky; |
philpem@5 | 166 | if (yj_>=0 && yj_<dimy() && yi_>=0 && yi_<dimy()) |
philpem@5 | 167 | for (int kx=-patch_size;kx<patch_size+1;kx+=sampling){ |
philpem@5 | 168 | int xj_ = xj+kx; |
philpem@5 | 169 | int xi_ = xi+kx; |
philpem@5 | 170 | if (xj_>=0 && xj_<dimx() && xi_>=0 && xi_<dimx()) |
philpem@5 | 171 | for (int v=0;v<dimv();v++){ |
philpem@5 | 172 | double d1 = (*this)(xj_,yj_,v)-(*this)(xi_,yi_,v); |
philpem@5 | 173 | d += d1*d1; |
philpem@5 | 174 | n++; |
philpem@5 | 175 | } |
philpem@5 | 176 | } |
philpem@5 | 177 | } |
philpem@5 | 178 | float w=(float)std::exp(d*h2); |
philpem@5 | 179 | for (int v=0;v<dimv();v++) uhat[v] += w*(*this)(xj,yj,v); |
philpem@5 | 180 | wmax = w>wmax?w:wmax; // Store the maximum of the weights |
philpem@5 | 181 | sw += w; // Compute the sum of the weights |
philpem@5 | 182 | } |
philpem@5 | 183 | // add the central pixel with the maximum weight |
philpem@5 | 184 | { for (int v=0;v<dimv();v++) uhat[v] += wmax*(*this)(xi,yi,v); } |
philpem@5 | 185 | sw += wmax; |
philpem@5 | 186 | // Compute the estimate for the current pixel |
philpem@5 | 187 | { for (int v=0;v<dimv();v++) dest(xi,yi,v)= (T) (uhat[v] /= sw); } |
philpem@5 | 188 | } |
philpem@5 | 189 | }// main loop |
philpem@5 | 190 | }// 2d |
philpem@5 | 191 | delete [] uhat; |
philpem@5 | 192 | *this=dest; |
philpem@5 | 193 | #if cimg_debug>=1 |
philpem@5 | 194 | std::fprintf(stderr,"\n"); // make a new line |
philpem@5 | 195 | #endif |
philpem@5 | 196 | }// is empty |
philpem@5 | 197 | return *this; |
philpem@5 | 198 | } |
philpem@5 | 199 | |
philpem@5 | 200 | //! Get the result of the NL-Means denoising algorithm. |
philpem@5 | 201 | /** |
philpem@5 | 202 | \param patch_size = radius of the patch (1=3x3 by default) |
philpem@5 | 203 | \param lambda = bandwidth ( -1 by default : automatic selection) |
philpem@5 | 204 | \param alpha = size of the region where similar patch are searched (3 x patch_size = 9x9 by default) |
philpem@5 | 205 | \param sigma = noise standard deviation (-1 = estimation) |
philpem@5 | 206 | \param sampling = sampling of the patch (1 = uses all point, 2 = uses one point on 4, etc) |
philpem@5 | 207 | If the image has three dimensions then the patch is only in 2D and the neighborhood extent in time is only 5. |
philpem@5 | 208 | If the image has several channel (color images), the distance between the two patch is computed using |
philpem@5 | 209 | all the channels. |
philpem@5 | 210 | The greater the patch is the best is the result. |
philpem@5 | 211 | Lambda parameter is function of the size of the patch size. The automatic Lambda parameter is taken |
philpem@5 | 212 | in the Chi2 table at a significiance level of 0.01. This diffear from the original paper [1]. The weighted average becomes then: |
philpem@5 | 213 | \f$$ \hat{f}(x,y) = \sum_{x',y'} \frac{1}{Z} exp(\frac{P(x,y)-P(x',y')}{2 \lambda \sigma^2}) f(x',y') $$\f |
philpem@5 | 214 | where \f$ P(x,y) $\f denotes the patch in (x,y) location. |
philpem@5 | 215 | |
philpem@5 | 216 | An a priori is also used to increase the speed of the algorithm in the spirit of Sapiro et al. SPletter dec 05 |
philpem@5 | 217 | |
philpem@5 | 218 | This very basic version of the Non-Local Means algorithm provides an output image which contains |
philpem@5 | 219 | some residual noise with a relatively small variance (\f$\sigma<5$\f). |
philpem@5 | 220 | |
philpem@5 | 221 | [1] A non-local algorithm for image denoising |
philpem@5 | 222 | Buades, A.; Coll, B.; Morel, J.-M.; |
philpem@5 | 223 | Computer Vision and Pattern Recognition, 2005. CVPR 2005. IEEE Computer Society Conference on |
philpem@5 | 224 | Volume 2, 20-25 June 2005 Page(s):60 - 65 vol. 2 |
philpem@5 | 225 | **/ |
philpem@5 | 226 | CImg<T> get_nlmeans( int patch_size=1, double lambda=-1, double alpha=3 ,double sigma=-1, int sampling=1) { |
philpem@5 | 227 | return CImg<T>(*this).nlmeans(patch_size,lambda,alpha,sigma,sampling); |
philpem@5 | 228 | } |
philpem@5 | 229 | |
philpem@5 | 230 | #endif |