PTdecode/CImg-1.3.0/plugins/nlmeans.h

Wed, 05 Aug 2009 15:00:54 +0100

author
Philip Pemberton <philpem@philpem.me.uk>
date
Wed, 05 Aug 2009 15:00:54 +0100
changeset 12
96e1df9bd27c
parent 5
1204ebf9340d
permissions
-rwxr-xr-x

small changes to hexdump code to stop a gcc warning on platforms where sizeof(int) != sizeof(int*) e.g. x86_64

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