PTdecode/CImg-1.3.0/plugins/greycstoration.h

changeset 5
1204ebf9340d
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/PTdecode/CImg-1.3.0/plugins/greycstoration.h	Mon Aug 03 14:09:20 2009 +0100
     1.3 @@ -0,0 +1,481 @@
     1.4 +/*
     1.5 +  #
     1.6 +  #  File        : greycstoration.h
     1.7 +  #                ( C++ header file - CImg plug-in )
     1.8 +  #
     1.9 +  #  Description : GREYCstoration plug-in allowing easy integration in
    1.10 +  #                third parties softwares.
    1.11 +  #                ( http://www.greyc.ensicaen.fr/~dtschump/greycstoration/ )
    1.12 +  #                This file is a part of the CImg Library project.
    1.13 +  #                ( http://cimg.sourceforge.net )
    1.14 +  #
    1.15 +  #  THIS PLUG-IN IS INTENDED FOR DEVELOPERS ONLY. IT EASES THE INTEGRATION ALGORITHM IN
    1.16 +  #  THIRD PARTIES SOFTWARES. IF YOU ARE A USER OF GREYCSTORATION, PLEASE LOOK
    1.17 +  #  AT THE FILE 'greycstoration.cpp' WHICH IS THE SOURCE OF THE COMPLETE
    1.18 +  #  COMMAND LINE GREYCSTORATION TOOL.
    1.19 +  #
    1.20 +  #  Copyright   : David Tschumperle
    1.21 +  #                ( http://www.greyc.ensicaen.fr/~dtschump/ )
    1.22 +  #
    1.23 +  #  License     : CeCILL v2.0
    1.24 +  #                ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
    1.25 +  #
    1.26 +  #  This software is governed by the CeCILL  license under French law and
    1.27 +  #  abiding by the rules of distribution of free software.  You can  use,
    1.28 +  #  modify and/ or redistribute the software under the terms of the CeCILL
    1.29 +  #  license as circulated by CEA, CNRS and INRIA at the following URL
    1.30 +  #  "http://www.cecill.info".
    1.31 +  #
    1.32 +  #  As a counterpart to the access to the source code and  rights to copy,
    1.33 +  #  modify and redistribute granted by the license, users are provided only
    1.34 +  #  with a limited warranty  and the software's author,  the holder of the
    1.35 +  #  economic rights,  and the successive licensors  have only  limited
    1.36 +  #  liability.
    1.37 +  #
    1.38 +  #  In this respect, the user's attention is drawn to the risks associated
    1.39 +  #  with loading,  using,  modifying and/or developing or reproducing the
    1.40 +  #  software by the user in light of its specific status of free software,
    1.41 +  #  that may mean  that it is complicated to manipulate,  and  that  also
    1.42 +  #  therefore means  that it is reserved for developers  and  experienced
    1.43 +  #  professionals having in-depth computer knowledge. Users are therefore
    1.44 +  #  encouraged to load and test the software's suitability as regards their
    1.45 +  #  requirements in conditions enabling the security of their systems and/or
    1.46 +  #  data to be ensured and,  more generally, to use and operate it in the
    1.47 +  #  same conditions as regards security.
    1.48 +  #
    1.49 +  #  The fact that you are presently reading this means that you have had
    1.50 +  #  knowledge of the CeCILL license and that you accept its terms.
    1.51 +  #
    1.52 +*/
    1.53 +
    1.54 +#ifndef cimg_plugin_greycstoration
    1.55 +#define cimg_plugin_greycstoration
    1.56 +
    1.57 +//------------------------------------------------------------------------------
    1.58 +// GREYCstoration parameter structure, storing important informations about
    1.59 +// algorithm parameters and computing threads.
    1.60 +// ** This structure has not to be manipulated by the API user, so please just
    1.61 +// ignore it if you want to **
    1.62 +//-------------------------------------------------------------------------------
    1.63 +struct _greycstoration_params {
    1.64 +
    1.65 +  // Tell if the patch-based algorithm is selected
    1.66 +  bool patch_based;
    1.67 +
    1.68 +  // Parameters specific to the non-patch regularization algorithm
    1.69 +  float amplitude;
    1.70 +  float sharpness;
    1.71 +  float anisotropy;
    1.72 +  float alpha;
    1.73 +  float sigma;
    1.74 +  float gfact;
    1.75 +  float dl;
    1.76 +  float da;
    1.77 +  float gauss_prec;
    1.78 +  unsigned int interpolation;
    1.79 +
    1.80 +  // Parameters specific to the patch-based regularization algorithm
    1.81 +  unsigned int patch_size;
    1.82 +  float sigma_s;
    1.83 +  float sigma_p;
    1.84 +  unsigned int lookup_size;
    1.85 +
    1.86 +  // Non-specific parameters of the algorithms.
    1.87 +  CImg<T> *source;
    1.88 +  const CImg<unsigned char> *mask;
    1.89 +  CImg<T> *temporary;
    1.90 +  unsigned long *counter;
    1.91 +  unsigned int tile;
    1.92 +  unsigned int tile_border;
    1.93 +  unsigned int thread;
    1.94 +  unsigned int nb_threads;
    1.95 +  bool fast_approx;
    1.96 +  bool is_running;
    1.97 +  bool *stop_request;
    1.98 +#if cimg_OS==1 && defined(_PTHREAD_H)
    1.99 +  pthread_mutex_t
   1.100 +  *mutex;
   1.101 +#elif cimg_OS==2
   1.102 +  HANDLE mutex;
   1.103 +#else
   1.104 +  void *mutex;
   1.105 +#endif
   1.106 +
   1.107 +  // Default constructor
   1.108 +  _greycstoration_params():patch_based(false),amplitude(0),sharpness(0),anisotropy(0),alpha(0),sigma(0),gfact(1),
   1.109 +       dl(0),da(0),gauss_prec(0),interpolation(0),patch_size(0),
   1.110 +       sigma_s(0),sigma_p(0),lookup_size(0),source(0),mask(0),temporary(0),counter(0),tile(0),
   1.111 +       tile_border(0),thread(0),nb_threads(0),fast_approx(false),is_running(false), stop_request(0), mutex(0) {}
   1.112 +};
   1.113 +
   1.114 +_greycstoration_params greycstoration_params[16];
   1.115 +
   1.116 +//----------------------------------------------------------
   1.117 +// Public functions of the GREYCstoration API.
   1.118 +// Use the functions below for integrating GREYCstoration
   1.119 +// in your own C++ code.
   1.120 +//----------------------------------------------------------
   1.121 +
   1.122 +//! Test if GREYCstoration threads are still running.
   1.123 +bool greycstoration_is_running() const {
   1.124 +  return greycstoration_params->is_running;
   1.125 +}
   1.126 +
   1.127 +//! Force the GREYCstoration threads to stop.
   1.128 +CImg& greycstoration_stop() {
   1.129 +  if (greycstoration_is_running()) {
   1.130 +    *(greycstoration_params->stop_request) = true;
   1.131 +    while (greycstoration_params->is_running) cimg::wait(50);
   1.132 +  }
   1.133 +  return *this;
   1.134 +}
   1.135 +
   1.136 +//! Return the GREYCstoration progress bar indice (between 0 and 100).
   1.137 +float greycstoration_progress() const {
   1.138 +  if (!greycstoration_is_running()) return 0.0f;
   1.139 +  const unsigned long counter = greycstoration_params->counter?*(greycstoration_params->counter):0;
   1.140 +  const float
   1.141 +    da = greycstoration_params->da,
   1.142 +    factor = greycstoration_params->patch_based?1:(1+360/da);
   1.143 +  float maxcounter = 0;
   1.144 +  if (greycstoration_params->tile==0) maxcounter = width*height*depth*factor;
   1.145 +  else {
   1.146 +    const unsigned int
   1.147 +      t = greycstoration_params->tile,
   1.148 +      b = greycstoration_params->tile_border,
   1.149 +      n = (1+(width-1)/t)*(1+(height-1)/t)*(1+(depth-1)/t);
   1.150 +    maxcounter = (width*height*depth + n*4*b*(b + t))*factor;
   1.151 +  }
   1.152 +  return cimg::min(counter*99.9f/maxcounter,99.9f);
   1.153 +}
   1.154 +
   1.155 +//! Run the non-patch version of the GREYCstoration algorithm on the instance image, using a mask.
   1.156 +CImg& greycstoration_run(const CImg<unsigned char>& mask,
   1.157 +                         const float amplitude=60, const float sharpness=0.7f, const float anisotropy=0.3f,
   1.158 +                         const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f,
   1.159 +                         const float dl=0.8f, const float da=30.0f,
   1.160 +                         const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true,
   1.161 +                         const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) {
   1.162 +
   1.163 +  if (greycstoration_is_running())
   1.164 +    throw CImgInstanceException("CImg<T>::greycstoration_run() : A GREYCstoration thread is already running on"
   1.165 +                                " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data);
   1.166 +
   1.167 +  else {
   1.168 +    if (!mask.is_empty() && !mask.is_sameXY(*this))
   1.169 +      throw CImgArgumentException("CImg<%s>::greycstoration_run() : Given mask (%u,%u,%u,%u,%p) and instance image "
   1.170 +                                  "(%u,%u,%u,%u,%p) have different dimensions.",
   1.171 +                                  pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data,width,height,depth,dim,data);
   1.172 +    if (nb_threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max.");
   1.173 +    const unsigned int
   1.174 +      ntile = (tile && (tile<width || tile<height || (depth>1 && tile<depth)))?tile:0,
   1.175 +#if cimg_OS==1 && !defined(_PTHREAD_H)
   1.176 +      nthreads = 0;
   1.177 +#else
   1.178 +    nthreads = ntile?cimg::min(nb_threads,16U):cimg::min(nb_threads,1U);
   1.179 +#endif
   1.180 +
   1.181 +    CImg<T> *const temporary = ntile?new CImg<T>(*this):0;
   1.182 +    unsigned long *const counter = new unsigned long;
   1.183 +    *counter = 0;
   1.184 +    bool *const stop_request = new bool;
   1.185 +    *stop_request = false;
   1.186 +
   1.187 +    for (unsigned int k=0; k<(nthreads?nthreads:1); k++) {
   1.188 +      greycstoration_params[k].patch_based = false;
   1.189 +      greycstoration_params[k].amplitude = amplitude;
   1.190 +      greycstoration_params[k].sharpness = sharpness;
   1.191 +      greycstoration_params[k].anisotropy = anisotropy;
   1.192 +      greycstoration_params[k].alpha = alpha;
   1.193 +      greycstoration_params[k].sigma = sigma;
   1.194 +      greycstoration_params[k].gfact = gfact;
   1.195 +      greycstoration_params[k].dl = dl;
   1.196 +      greycstoration_params[k].da = da;
   1.197 +      greycstoration_params[k].gauss_prec = gauss_prec;
   1.198 +      greycstoration_params[k].interpolation = interpolation;
   1.199 +      greycstoration_params[k].fast_approx = fast_approx;
   1.200 +      greycstoration_params[k].source = this;
   1.201 +      greycstoration_params[k].mask = &mask;
   1.202 +      greycstoration_params[k].temporary = temporary;
   1.203 +      greycstoration_params[k].counter = counter;
   1.204 +      greycstoration_params[k].tile = ntile;
   1.205 +      greycstoration_params[k].tile_border = tile_border;
   1.206 +      greycstoration_params[k].thread = k;
   1.207 +      greycstoration_params[k].nb_threads = nthreads;
   1.208 +      greycstoration_params[k].is_running = true;
   1.209 +      greycstoration_params[k].stop_request = stop_request;
   1.210 +      if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex;
   1.211 +      else greycstoration_mutex_create(greycstoration_params[0]);
   1.212 +    }
   1.213 +    if (nthreads) {  // Threaded version
   1.214 +#if cimg_OS==1
   1.215 +#ifdef _PTHREAD_H
   1.216 +      pthread_attr_t attr;
   1.217 +      pthread_attr_init(&attr);
   1.218 +      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   1.219 +      for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
   1.220 +        pthread_t thread;
   1.221 +        const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k));
   1.222 +        if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d",
   1.223 +                                     pixel_type(), err);
   1.224 +      }
   1.225 +#endif
   1.226 +#elif cimg_OS==2
   1.227 +      for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
   1.228 +        unsigned long ThreadID = 0;
   1.229 +        CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID);
   1.230 +      }
   1.231 +#else
   1.232 +      throw CImgInstanceException("CImg<T>::greycstoration_run() : Threads are not supported, please define cimg_OS first.");
   1.233 +#endif
   1.234 +    } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version
   1.235 +  }
   1.236 +  return *this;
   1.237 +}
   1.238 +
   1.239 +//! Run the non-patch version of the GREYCstoration algorithm on the instance image.
   1.240 +CImg& greycstoration_run(const float amplitude=50, const float sharpness=0.7f, const float anisotropy=0.3f,
   1.241 +                         const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f,
   1.242 +                         const float dl=0.8f, const float da=30.0f,
   1.243 +                         const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true,
   1.244 +                         const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) {
   1.245 +  static const CImg<unsigned char> empty_mask;
   1.246 +  return greycstoration_run(empty_mask,amplitude,sharpness,anisotropy,alpha,sigma,gfact,dl,da,gauss_prec,
   1.247 +                            interpolation,fast_approx,tile,tile_border,nb_threads);
   1.248 +}
   1.249 +
   1.250 +//! Run the patch-based version of the GREYCstoration algorithm on the instance image.
   1.251 +CImg& greycstoration_patch_run(const unsigned int patch_size=5, const float sigma_p=10, const float sigma_s=100,
   1.252 +                               const unsigned int lookup_size=20, const bool fast_approx=true,
   1.253 +                               const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int nb_threads=1) {
   1.254 +
   1.255 +  static const CImg<unsigned char> empty_mask;
   1.256 +  if (greycstoration_is_running())
   1.257 +    throw CImgInstanceException("CImg<T>::greycstoration_run() : A GREYCstoration thread is already running on"
   1.258 +                                " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data);
   1.259 +
   1.260 +  else {
   1.261 +    if (nb_threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max.");
   1.262 +    const unsigned int
   1.263 +      ntile = (tile && (tile<width || tile<height || (depth>1 && tile<depth)))?tile:0,
   1.264 +#if cimg_OS==1 && !defined(_PTHREAD_H)
   1.265 +      nthreads = 0;
   1.266 +#else
   1.267 +    nthreads = ntile?cimg::min(nb_threads,16U):cimg::min(nb_threads,1U);
   1.268 +#endif
   1.269 +
   1.270 +    CImg<T> *const temporary = ntile?new CImg<T>(*this):0;
   1.271 +    unsigned long *const counter = new unsigned long;
   1.272 +    *counter = 0;
   1.273 +    bool *const stop_request = new bool;
   1.274 +    *stop_request = false;
   1.275 +
   1.276 +    for (unsigned int k=0; k<(nthreads?nthreads:1); k++) {
   1.277 +      greycstoration_params[k].patch_based = true;
   1.278 +      greycstoration_params[k].patch_size = patch_size;
   1.279 +      greycstoration_params[k].sigma_s = sigma_s;
   1.280 +      greycstoration_params[k].sigma_p = sigma_p;
   1.281 +      greycstoration_params[k].lookup_size = lookup_size;
   1.282 +      greycstoration_params[k].source = this;
   1.283 +      greycstoration_params[k].mask = &empty_mask;
   1.284 +      greycstoration_params[k].temporary = temporary;
   1.285 +      greycstoration_params[k].counter = counter;
   1.286 +      greycstoration_params[k].tile = ntile;
   1.287 +      greycstoration_params[k].tile_border = tile_border;
   1.288 +      greycstoration_params[k].thread = k;
   1.289 +      greycstoration_params[k].nb_threads = nthreads;
   1.290 +      greycstoration_params[k].fast_approx = fast_approx;
   1.291 +      greycstoration_params[k].is_running = true;
   1.292 +      greycstoration_params[k].stop_request = stop_request;
   1.293 +      if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex;
   1.294 +      else greycstoration_mutex_create(greycstoration_params[0]);
   1.295 +    }
   1.296 +    if (nthreads) {  // Threaded version
   1.297 +#if cimg_OS==1
   1.298 +#ifdef _PTHREAD_H
   1.299 +      pthread_attr_t attr;
   1.300 +      pthread_attr_init(&attr);
   1.301 +      pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
   1.302 +      for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
   1.303 +        pthread_t thread;
   1.304 +        const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k));
   1.305 +        if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d",
   1.306 +                                     pixel_type(), err);
   1.307 +      }
   1.308 +#endif
   1.309 +#elif cimg_OS==2
   1.310 +      for (unsigned int k=0; k<greycstoration_params->nb_threads; k++) {
   1.311 +        unsigned long ThreadID = 0;
   1.312 +        CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID);
   1.313 +      }
   1.314 +#else
   1.315 +      throw CImgInstanceException("CImg<T>::greycstoration_run() : Threads support have not been enabled in this version of GREYCstoration.");
   1.316 +#endif
   1.317 +    } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version
   1.318 +  }
   1.319 +  return *this;
   1.320 +}
   1.321 +
   1.322 +//------------------------------------------------------------------------------
   1.323 +// GREYCstoration private functions.
   1.324 +// Should not be used directly by the API user.
   1.325 +//-------------------------------------------------------------------------------
   1.326 +
   1.327 +static void greycstoration_mutex_create(_greycstoration_params &p) {
   1.328 +  if (p.nb_threads>1) {
   1.329 +#if cimg_OS==1 && defined(_PTHREAD_H)
   1.330 +    p.mutex = new pthread_mutex_t;
   1.331 +    pthread_mutex_init(p.mutex,0);
   1.332 +#elif cimg_OS==2
   1.333 +    p.mutex = CreateMutex(0,FALSE,0);
   1.334 +#endif
   1.335 +  }
   1.336 +}
   1.337 +
   1.338 +static void greycstoration_mutex_lock(_greycstoration_params &p) {
   1.339 +  if (p.nb_threads>1) {
   1.340 +#if cimg_OS==1 && defined(_PTHREAD_H)
   1.341 +    if (p.mutex) pthread_mutex_lock(p.mutex);
   1.342 +#elif cimg_OS==2
   1.343 +    WaitForSingleObject(p.mutex,INFINITE);
   1.344 +#endif
   1.345 +  }
   1.346 +}
   1.347 +
   1.348 +static void greycstoration_mutex_unlock(_greycstoration_params &p) {
   1.349 +  if (p.nb_threads>1) {
   1.350 +#if cimg_OS==1 && defined(_PTHREAD_H)
   1.351 +    if (p.mutex) pthread_mutex_unlock(p.mutex);
   1.352 +#elif cimg_OS==2
   1.353 +    ReleaseMutex(p.mutex);
   1.354 +#endif
   1.355 +  }
   1.356 +}
   1.357 +
   1.358 +static void greycstoration_mutex_destroy(_greycstoration_params &p) {
   1.359 +  if (p.nb_threads>1) {
   1.360 +#if cimg_OS==1 && defined(_PTHREAD_H)
   1.361 +    if (p.mutex) pthread_mutex_destroy(p.mutex);
   1.362 +#elif cimg_OS==2
   1.363 +    CloseHandle(p.mutex);
   1.364 +#endif
   1.365 +    p.mutex = 0;
   1.366 +  }
   1.367 +}
   1.368 +
   1.369 +#if cimg_OS==1
   1.370 +static void* greycstoration_thread(void *arg) {
   1.371 +#elif cimg_OS==2
   1.372 +  static DWORD WINAPI greycstoration_thread(void *arg) {
   1.373 +#endif
   1.374 +    _greycstoration_params &p = *(_greycstoration_params*)arg;
   1.375 +    greycstoration_mutex_lock(p);
   1.376 +    const CImg<unsigned char> &mask = *(p.mask);
   1.377 +    CImg<T> &source = *(p.source);
   1.378 +
   1.379 +    if (!p.tile) {
   1.380 +
   1.381 +      // Non-tiled version
   1.382 +      //------------------
   1.383 +      if (p.patch_based) source.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx);
   1.384 +      else source.blur_anisotropic(mask,p.amplitude,p.sharpness,p.anisotropy,p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,
   1.385 +                                   p.interpolation,p.fast_approx,p.gfact);
   1.386 +
   1.387 +    } else {
   1.388 +
   1.389 +      // Tiled version
   1.390 +      //---------------
   1.391 +      CImg<T> &temporary = *(p.temporary);
   1.392 +      const bool threed = (source.depth>1);
   1.393 +      const unsigned int b = p.tile_border;
   1.394 +      unsigned int ctile = 0;
   1.395 +      if (threed) {
   1.396 +        for (unsigned int z=0; z<source.depth && !*(p.stop_request); z+=p.tile)
   1.397 +          for (unsigned int y=0; y<source.height && !*(p.stop_request); y+=p.tile)
   1.398 +            for (unsigned int x=0; x<source.width && !*(p.stop_request); x+=p.tile)
   1.399 +              if (!p.nb_threads || ((ctile++)%p.nb_threads)==p.thread) {
   1.400 +                const unsigned int
   1.401 +                  x1 = x+p.tile-1,
   1.402 +                  y1 = y+p.tile-1,
   1.403 +                  z1 = z+p.tile-1,
   1.404 +                  xe = x1<source.width?x1:source.width-1,
   1.405 +                  ye = y1<source.height?y1:source.height-1,
   1.406 +                  ze = z1<source.depth?z1:source.depth-1;
   1.407 +                CImg<T> img = source.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true);
   1.408 +                CImg<unsigned char> mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true);
   1.409 +                img.greycstoration_params[0] = p;
   1.410 +                greycstoration_mutex_unlock(p);
   1.411 +                if (p.patch_based) img.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx);
   1.412 +                else img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy,
   1.413 +                                          p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact);
   1.414 +                greycstoration_mutex_lock(p);
   1.415 +                temporary.draw_image(x,y,z,img.crop(b,b,b,img.width-b,img.height-b,img.depth-b));
   1.416 +              }
   1.417 +      } else {
   1.418 +        for (unsigned int y=0; y<source.height && !*(p.stop_request); y+=p.tile)
   1.419 +          for (unsigned int x=0; x<source.width && !*(p.stop_request); x+=p.tile)
   1.420 +            if (!p.nb_threads || ((ctile++)%p.nb_threads)==p.thread) {
   1.421 +              const unsigned int
   1.422 +                x1 = x+p.tile-1,
   1.423 +                y1 = y+p.tile-1,
   1.424 +                xe = x1<source.width?x1:source.width-1,
   1.425 +                ye = y1<source.height?y1:source.height-1;
   1.426 +              CImg<T> img = source.get_crop(x-b,y-b,xe+b,ye+b,true);
   1.427 +              CImg<unsigned char> mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,xe+b,ye+b,true);
   1.428 +              img.greycstoration_params[0] = p;
   1.429 +              greycstoration_mutex_unlock(p);
   1.430 +              if (p.patch_based) img.blur_patch(p.patch_size,p.sigma_p,p.sigma_s,p.lookup_size,p.fast_approx);
   1.431 +              else img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy,
   1.432 +                                        p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact);
   1.433 +              temporary.draw_image(x,y,img.crop(b,b,img.width-b,img.height-b));
   1.434 +              greycstoration_mutex_lock(p);
   1.435 +            }
   1.436 +      }
   1.437 +    }
   1.438 +    greycstoration_mutex_unlock(p);
   1.439 +
   1.440 +    if (!p.thread) {
   1.441 +      if (p.nb_threads>1) {
   1.442 +        bool stopflag = true;
   1.443 +        do {
   1.444 +          stopflag = true;
   1.445 +          for (unsigned int k=1; k<p.nb_threads; k++) if (source.greycstoration_params[k].is_running) stopflag = false;
   1.446 +          if (!stopflag) cimg::wait(50);
   1.447 +        } while (!stopflag);
   1.448 +      }
   1.449 +      if (p.counter) delete p.counter;
   1.450 +      if (p.temporary) { source = *(p.temporary); delete p.temporary; }
   1.451 +      if (p.stop_request) delete p.stop_request;
   1.452 +      p.mask = 0;
   1.453 +      p.amplitude = p.sharpness = p.anisotropy = p.alpha = p.sigma = p.gfact = p.dl = p.da = p.gauss_prec = p.sigma_s = p.sigma_p = 0;
   1.454 +      p.patch_size = p.interpolation = p.lookup_size = 0;
   1.455 +      p.fast_approx = false;
   1.456 +      p.source = 0;
   1.457 +      p.temporary = 0;
   1.458 +      p.counter = 0;
   1.459 +      p.tile = p.tile_border = p.thread = p.nb_threads = 0;
   1.460 +      p.stop_request = false;
   1.461 +      greycstoration_mutex_destroy(p);
   1.462 +    }
   1.463 +    p.is_running = false;
   1.464 +
   1.465 +    if (p.nb_threads) {
   1.466 +#if cimg_OS==1 && defined(_PTHREAD_H)
   1.467 +      pthread_exit(arg);
   1.468 +      return arg;
   1.469 +#elif cimg_OS==2
   1.470 +      ExitThread(0);
   1.471 +#endif
   1.472 +    }
   1.473 +    return 0;
   1.474 +  }
   1.475 +
   1.476 +
   1.477 +#define cimg_plugin_greycstoration_count \
   1.478 +  if (!*(greycstoration_params->stop_request)) ++(*greycstoration_params->counter); else return *this;
   1.479 +#define cimg_plugin_greycstoration_lock \
   1.480 +  greycstoration_mutex_lock(greycstoration_params[0]);
   1.481 +#define cimg_plugin_greycstoration_unlock \
   1.482 +  greycstoration_mutex_unlock(greycstoration_params[0]);
   1.483 +
   1.484 +#endif