Wed, 05 Aug 2009 15:00:54 +0100
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 : greycstoration.cpp |
philpem@5 | 4 | # ( C++ source file ) |
philpem@5 | 5 | # |
philpem@5 | 6 | # Description : GREYCstoration - A tool to denoise, inpaint and resize images. |
philpem@5 | 7 | # This file is a part of the CImg Library project. |
philpem@5 | 8 | # ( http://cimg.sourceforge.net ) |
philpem@5 | 9 | # See also the GREYCstoration web page |
philpem@5 | 10 | # ( http://www.greyc.ensicaen.fr/~dtschump/greycstoration ) |
philpem@5 | 11 | # |
philpem@5 | 12 | # The GREYCstoration algorithm is an implementation of tensor-directed and |
philpem@5 | 13 | # patch-based diffusion PDE's for image regularization and interpolation, |
philpem@5 | 14 | # published in |
philpem@5 | 15 | # |
philpem@5 | 16 | # "Defining Some Variational Methods on the Space of Patches : |
philpem@5 | 17 | # Application to Multi-Valued Image Denoising and Registration" |
philpem@5 | 18 | # (D. Tschumperle, L. Brun) |
philpem@5 | 19 | # Rapport de recherche : Les cahiers du GREYC No 08-01, Mars 2008. |
philpem@5 | 20 | # |
philpem@5 | 21 | # "Fast Anisotropic Smoothing of Multi-Valued Images |
philpem@5 | 22 | # using Curvature-Preserving PDE's" |
philpem@5 | 23 | # (D. Tschumperle) |
philpem@5 | 24 | # International Journal of Computer Vision, May 2006. |
philpem@5 | 25 | # |
philpem@5 | 26 | # "Vector-Valued Image Regularization with PDE's : A Common Framework |
philpem@5 | 27 | # for Different Applications" |
philpem@5 | 28 | # (D. Tschumperle, R. Deriche). |
philpem@5 | 29 | # IEEE Transactions on Pattern Analysis and Machine Intelligence, |
philpem@5 | 30 | # Vol 27, No 4, pp 506-517, April 2005. |
philpem@5 | 31 | # |
philpem@5 | 32 | # Copyright : David Tschumperle |
philpem@5 | 33 | # ( http://www.greyc.ensicaen.fr/~dtschump/ ) |
philpem@5 | 34 | # |
philpem@5 | 35 | # License : CeCILL v2.0 |
philpem@5 | 36 | # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html ) |
philpem@5 | 37 | # |
philpem@5 | 38 | # This software is governed by the CeCILL license under French law and |
philpem@5 | 39 | # abiding by the rules of distribution of free software. You can use, |
philpem@5 | 40 | # modify and/ or redistribute the software under the terms of the CeCILL |
philpem@5 | 41 | # license as circulated by CEA, CNRS and INRIA at the following URL |
philpem@5 | 42 | # "http://www.cecill.info". |
philpem@5 | 43 | # |
philpem@5 | 44 | # As a counterpart to the access to the source code and rights to copy, |
philpem@5 | 45 | # modify and redistribute granted by the license, users are provided only |
philpem@5 | 46 | # with a limited warranty and the software's author, the holder of the |
philpem@5 | 47 | # economic rights, and the successive licensors have only limited |
philpem@5 | 48 | # liability. |
philpem@5 | 49 | # |
philpem@5 | 50 | # In this respect, the user's attention is drawn to the risks associated |
philpem@5 | 51 | # with loading, using, modifying and/or developing or reproducing the |
philpem@5 | 52 | # software by the user in light of its specific status of free software, |
philpem@5 | 53 | # that may mean that it is complicated to manipulate, and that also |
philpem@5 | 54 | # therefore means that it is reserved for developers and experienced |
philpem@5 | 55 | # professionals having in-depth computer knowledge. Users are therefore |
philpem@5 | 56 | # encouraged to load and test the software's suitability as regards their |
philpem@5 | 57 | # requirements in conditions enabling the security of their systems and/or |
philpem@5 | 58 | # data to be ensured and, more generally, to use and operate it in the |
philpem@5 | 59 | # same conditions as regards security. |
philpem@5 | 60 | # |
philpem@5 | 61 | # The fact that you are presently reading this means that you have had |
philpem@5 | 62 | # knowledge of the CeCILL license and that you accept its terms. |
philpem@5 | 63 | # |
philpem@5 | 64 | */ |
philpem@5 | 65 | |
philpem@5 | 66 | #define cimg_plugin "plugins/greycstoration.h" |
philpem@5 | 67 | #ifndef cimg_debug |
philpem@5 | 68 | #if defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ |
philpem@5 | 69 | || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) |
philpem@5 | 70 | #define cimg_debug 2 |
philpem@5 | 71 | #else |
philpem@5 | 72 | #define cimg_debug 1 |
philpem@5 | 73 | #endif |
philpem@5 | 74 | #endif |
philpem@5 | 75 | #include "CImg.h" |
philpem@5 | 76 | #if cimg_OS!=2 |
philpem@5 | 77 | #include <pthread.h> |
philpem@5 | 78 | #endif |
philpem@5 | 79 | #define gprintf if (verbose) std::fprintf |
philpem@5 | 80 | using namespace cimg_library; |
philpem@5 | 81 | |
philpem@5 | 82 | // The lines below are necessary when using a non-standard compiler as visualcpp6. |
philpem@5 | 83 | #ifdef cimg_use_visualcpp6 |
philpem@5 | 84 | #define std |
philpem@5 | 85 | #endif |
philpem@5 | 86 | #ifdef min |
philpem@5 | 87 | #undef min |
philpem@5 | 88 | #undef max |
philpem@5 | 89 | #endif |
philpem@5 | 90 | |
philpem@5 | 91 | //----------- |
philpem@5 | 92 | // get_geom() : read geometry from a string (for instance '320x256' or '200%x200%'). |
philpem@5 | 93 | //----------- |
philpem@5 | 94 | void get_geom(const char *geom, int &geom_w, int &geom_h) { |
philpem@5 | 95 | char tmp[16]; |
philpem@5 | 96 | std::sscanf(geom,"%d%7[^0-9]%d%7[^0-9]",&geom_w,tmp,&geom_h,tmp+1); |
philpem@5 | 97 | if (tmp[0]=='%') geom_w=-geom_w; |
philpem@5 | 98 | if (tmp[1]=='%') geom_h=-geom_h; |
philpem@5 | 99 | } |
philpem@5 | 100 | |
philpem@5 | 101 | //-------------------------- |
philpem@5 | 102 | // GREYCstoration main code |
philpem@5 | 103 | //-------------------------- |
philpem@5 | 104 | template<typename T> void greycstoration(int argc, char **argv, T pixel_type) { |
philpem@5 | 105 | pixel_type = (T)0; |
philpem@5 | 106 | cimg_usage(" Open Source Algorithms for Image Denoising and Interpolation."); |
philpem@5 | 107 | cimg_help("-------------------------------------------------------------------------\n" |
philpem@5 | 108 | " GREYCstoration v3.0, by David Tschumperle. \n" |
philpem@5 | 109 | " ------------------------------------------------------------------------\n" |
philpem@5 | 110 | " This program allows to denoise, inpaint and resize 2D color images. \n" |
philpem@5 | 111 | " It is the result of the research work done in the IMAGE group of the \n" |
philpem@5 | 112 | " GREYC Lab (CNRS, UMR 6072) (http://www.greyc.ensicaen.fr/EquipeImage/) \n" |
philpem@5 | 113 | " by David Tschumperle (http://www.greyc.ensicaen.fr/~dtschump/). This \n" |
philpem@5 | 114 | " program has been primarily released to help people processing image data\n" |
philpem@5 | 115 | " and to allow comparisons between regularization algorithms. This is an \n" |
philpem@5 | 116 | " open source software, distributed within the CImg Library package \n" |
philpem@5 | 117 | " (http://cimg.sourceforge.net), and submitted to the CeCILL License. If \n" |
philpem@5 | 118 | " you are interested to distribute it in a closed-source product, please \n" |
philpem@5 | 119 | " read the licence file carefully. If you are using 'GREYCstored' images \n" |
philpem@5 | 120 | " in your own publications, be kind to reference the GREYCstoration web \n" |
philpem@5 | 121 | " site or the related scientific papers. More informations available at : \n" |
philpem@5 | 122 | " ** http://cimg.sourceforge.net/greycstoration/ ** \n" |
philpem@5 | 123 | "-------------------------------------------------------------------------\n"); |
philpem@5 | 124 | |
philpem@5 | 125 | // Read Global parameters |
philpem@5 | 126 | //------------------------ |
philpem@5 | 127 | cimg_help(" + Global parameters\n -----------------------"); |
philpem@5 | 128 | const char *restore_mode = cimg_option("-restore",(char*)0,"Restore image specified after '-restore'"); |
philpem@5 | 129 | const char *inpaint_mode = cimg_option("-inpaint",(char*)0,"Inpaint image specified after '-inpaint'"); |
philpem@5 | 130 | const char *resize_mode = cimg_option("-resize",(char*)0,"Resize image specified after '-resize'"); |
philpem@5 | 131 | const char *clean_mode = cimg_option("-clean",(char*)0,"Clean image specified after '-clean'"); |
philpem@5 | 132 | const char *reference_image = cimg_option("-ref",(char*)0,"Reference image to compare with"); |
philpem@5 | 133 | const char nb_bits = cimg_option("-bits",8,"Define input value type (8='uchar', 16='ushort', 32='float')"); |
philpem@5 | 134 | const unsigned int value_factor = cimg_option("-fact",0,"Define input value factor (0=auto)"); |
philpem@5 | 135 | const float noise_g = cimg_option("-ng",0.0f,"Add Gaussian noise before denoising"); |
philpem@5 | 136 | const float noise_u = cimg_option("-nu",0.0f,"Add Uniform noise before denoising"); |
philpem@5 | 137 | const float noise_s = cimg_option("-ns",0.0f,"Add Salt&Pepper noise before denoising"); |
philpem@5 | 138 | const unsigned int color_base = cimg_option("-cbase",0,"Processed color base (0=RGB, 1=YCbCr)"); |
philpem@5 | 139 | const char *channel_range = cimg_option("-crange",(char*)0,"Processed channels (ex: '0-2')"); |
philpem@5 | 140 | const unsigned int saving_step = cimg_option("-save",0,"Iteration saving step (>=0)"); |
philpem@5 | 141 | const bool visu = cimg_option("-visu",cimg_display?true:false,"Enable/Disable visualization windows (0 or 1)"); |
philpem@5 | 142 | const char *file_o = cimg_option("-o",(char*)0,"Filename of output image"); |
philpem@5 | 143 | const bool append_result = cimg_option("-append",false,"Append images in output file"); |
philpem@5 | 144 | const bool verbose = cimg_option("-verbose",true,"Verbose mode"); |
philpem@5 | 145 | const unsigned int jpg_quality = cimg_option("-quality",100,"Output compression quality (if JPEG format)"); |
philpem@5 | 146 | unsigned int nb_iterations = cimg_option("-iter",(restore_mode||clean_mode)?1:(inpaint_mode?1000:3), |
philpem@5 | 147 | "Number of iterations (>0)"); |
philpem@5 | 148 | const float sdt = cimg_option("-sharp",(restore_mode||clean_mode)?0.0f:(inpaint_mode?0.0f:10.0f), |
philpem@5 | 149 | "Sharpening strength (activate sharpening filter, >=0)"); |
philpem@5 | 150 | const float sp = cimg_option("-se",(restore_mode||clean_mode)?0.5f:(inpaint_mode?0.5f:3.0f), |
philpem@5 | 151 | "Sharpening edge threshold (>=0)"); |
philpem@5 | 152 | const unsigned int tile_size = cimg_option("-tile",512,"Activate tiled mode and set tile size (>=0)"); |
philpem@5 | 153 | const unsigned int tile_border = cimg_option("-btile",4,"Size of tile borders (>=0)"); |
philpem@5 | 154 | const unsigned int nb_threads = cimg_option("-threads",1,"Number of threads used (*experimental*, tile mode only, >0)"); |
philpem@5 | 155 | const bool fast_approx = cimg_option("-fast",true,"Try faster algorithm (true or false)"); |
philpem@5 | 156 | |
philpem@5 | 157 | // Declare specific algorithm parameters |
philpem@5 | 158 | //-------------------------------------- |
philpem@5 | 159 | float amplitude = 0, sharpness = 0, anisotropy = 0, alpha = 0, sigma = 0, gauss_prec = 0, dl = 0, da = 0, sigma_s = 0, sigma_p = 0; |
philpem@5 | 160 | unsigned int interpolation = 0, patch_size = 0, lookup_size = 0; |
philpem@5 | 161 | |
philpem@5 | 162 | if (argc==1 || |
philpem@5 | 163 | (!restore_mode && !inpaint_mode && !resize_mode && !clean_mode) || |
philpem@5 | 164 | (restore_mode && inpaint_mode) || (restore_mode && resize_mode) || (restore_mode && clean_mode) || |
philpem@5 | 165 | (inpaint_mode && resize_mode) || (inpaint_mode && clean_mode)) { |
philpem@5 | 166 | std::fprintf(stderr,"\n%s : You must specify (only) one of the '-restore', '-inpaint', '-resize' or '-clean' options.\n" |
philpem@5 | 167 | "(try option '-h', '-h -restore','-h -inpaint', '-h -resize' or '-h -clean' to get options relative to specific actions\n\n", |
philpem@5 | 168 | cimg::basename(argv[0])); |
philpem@5 | 169 | std::exit(0); |
philpem@5 | 170 | } |
philpem@5 | 171 | |
philpem@5 | 172 | // Init variables |
philpem@5 | 173 | //---------------- |
philpem@5 | 174 | CImg<T> img0, img, imgr; |
philpem@5 | 175 | CImg<unsigned char> mask; |
philpem@5 | 176 | CImgDisplay disp; |
philpem@5 | 177 | |
philpem@5 | 178 | // Read specific parameters for image restoration |
philpem@5 | 179 | //------------------------------------------------ |
philpem@5 | 180 | if (restore_mode) { |
philpem@5 | 181 | cimg_help("\n + Restoration mode parameters\n ---------------------------"); |
philpem@5 | 182 | amplitude = cimg_option("-dt",40.0f,"Regularization strength per iteration (>=0)"); |
philpem@5 | 183 | sharpness = cimg_option("-p",0.9f,"Contour preservation (>=0)"); |
philpem@5 | 184 | anisotropy = cimg_option("-a",0.15f,"Smoothing anisotropy (0<=a<=1)"); |
philpem@5 | 185 | alpha = cimg_option("-alpha",0.6f,"Noise scale (>=0)"); |
philpem@5 | 186 | sigma = cimg_option("-sigma",1.1f,"Geometry regularity (>=0)"); |
philpem@5 | 187 | gauss_prec = cimg_option("-prec",2.0f,"Computation precision (>0)"); |
philpem@5 | 188 | dl = cimg_option("-dl",0.8f,"Spatial integration step (0<=dl<=1)"); |
philpem@5 | 189 | da = cimg_option("-da",30.0f,"Angular integration step (0<=da<=90)"); |
philpem@5 | 190 | interpolation = cimg_option("-interp",0,"Interpolation type (0=Nearest-neighbor, 1=Linear, 2=Runge-Kutta)"); |
philpem@5 | 191 | |
philpem@5 | 192 | gprintf(stderr,"- Image Restoration mode\n"); |
philpem@5 | 193 | if (!cimg::strcmp("-restore",restore_mode)) { |
philpem@5 | 194 | std::fprintf(stderr,"%s : You must specify a valid input image filename after the '-restore' flag.\n\n",cimg::basename(argv[0])); |
philpem@5 | 195 | std::exit(0); |
philpem@5 | 196 | } |
philpem@5 | 197 | gprintf(stderr,"- Load input image '%s'...",cimg::basename(restore_mode)); |
philpem@5 | 198 | img.load(restore_mode); |
philpem@5 | 199 | gprintf(stderr,"\r- Input image : '%s' (size %dx%d, value range [%g,%g])\n", |
philpem@5 | 200 | cimg::basename(restore_mode),img.dimx(),img.dimy(),(double)img.min(),(double)img.max()); |
philpem@5 | 201 | if (noise_g || noise_u || noise_s) { |
philpem@5 | 202 | img0 = img; |
philpem@5 | 203 | img.noise(noise_g,0).noise(noise_u,1).noise(noise_s,2); |
philpem@5 | 204 | gprintf(stderr,"\r- Noisy image : value range [%g,%g], PSNR Noisy / Original : %g\n", |
philpem@5 | 205 | (double)img.min(),(double)img.max(),img.PSNR(img0)); |
philpem@5 | 206 | } |
philpem@5 | 207 | } |
philpem@5 | 208 | |
philpem@5 | 209 | // Specific parameters for image inpainting |
philpem@5 | 210 | //----------------------------------------- |
philpem@5 | 211 | if (inpaint_mode) { |
philpem@5 | 212 | cimg_help("\n + Inpainting mode parameters\n --------------------------"); |
philpem@5 | 213 | const char *file_m = cimg_option("-m",(char*)0,"Input inpainting mask"); |
philpem@5 | 214 | const unsigned int dilate = cimg_option("-dilate",0,"Inpainting mask dilatation (>=0)"); |
philpem@5 | 215 | const unsigned int init = cimg_option("-init",4,"Inpainting init (0=black, 1=white, 2=noise, 3=unchanged, 4=smart)"); |
philpem@5 | 216 | amplitude = cimg_option("-dt",20.0f,"Regularization strength per iteration (>=0)"); |
philpem@5 | 217 | sharpness = cimg_option("-p",0.3f,"Contour preservation (>=0)"); |
philpem@5 | 218 | anisotropy = cimg_option("-a",1.0f,"Smoothing anisotropy (0<=a<=1)"); |
philpem@5 | 219 | alpha = cimg_option("-alpha",0.8f,"Noise scale (>=0)"); |
philpem@5 | 220 | sigma = cimg_option("-sigma",2.0f,"Geometry regularity (>=0)"); |
philpem@5 | 221 | gauss_prec = cimg_option("-prec",2.0f,"Computation precision (>0)"); |
philpem@5 | 222 | dl = cimg_option("-dl",0.8f,"Spatial integration step (0<=dl<=1)"); |
philpem@5 | 223 | da = cimg_option("-da",30.0f,"Angular integration step (0<=da<=90)"); |
philpem@5 | 224 | interpolation = cimg_option("-interp",0,"Interpolation type (0=Nearest-neighbor, 1=Linear, 2=Runge-Kutta)"); |
philpem@5 | 225 | |
philpem@5 | 226 | gprintf(stderr,"- Image Inpainting mode\n"); |
philpem@5 | 227 | if (!cimg::strcmp("-inpaint",inpaint_mode)) { |
philpem@5 | 228 | std::fprintf(stderr,"%s : You must specify a valid input image filename after the '-inpaint' flag.\n\n", |
philpem@5 | 229 | cimg::basename(argv[0])); |
philpem@5 | 230 | std::exit(0); |
philpem@5 | 231 | } |
philpem@5 | 232 | gprintf(stderr,"- Load input image '%s'...",cimg::basename(inpaint_mode)); |
philpem@5 | 233 | img.load(inpaint_mode); |
philpem@5 | 234 | gprintf(stderr,"\r- Input image : '%s' (size %dx%d, value range [%g,%g])\n", |
philpem@5 | 235 | cimg::basename(inpaint_mode),img.dimx(),img.dimy(),(double)img.min(),(double)img.max()); |
philpem@5 | 236 | if (noise_g || noise_u || noise_s) { |
philpem@5 | 237 | img0 = img; |
philpem@5 | 238 | img.noise(noise_g,0).noise(noise_u,1).noise(noise_s,2); |
philpem@5 | 239 | gprintf(stderr,"\r- Noisy image : value range [%g,%g], PSNR Noisy / Original : %g\n", |
philpem@5 | 240 | (double)img.min(),(double)img.max(),img.PSNR(img0)); |
philpem@5 | 241 | } |
philpem@5 | 242 | if (!file_m) { |
philpem@5 | 243 | std::fprintf(stderr,"%s : You need to specify a valid inpainting mask filename after the '-m' flag.\n\n", |
philpem@5 | 244 | cimg::basename(argv[0])); |
philpem@5 | 245 | std::exit(0); |
philpem@5 | 246 | } |
philpem@5 | 247 | if (cimg::strncasecmp("block",file_m,5)) { |
philpem@5 | 248 | gprintf(stderr,"- Load inpainting mask '%s'...",cimg::basename(file_m)); |
philpem@5 | 249 | mask.load(file_m); |
philpem@5 | 250 | gprintf(stderr,"\r- Inpainting mask : '%s' (size %dx%d)\n", |
philpem@5 | 251 | cimg::basename(file_m),mask.dimx(),mask.dimy()); |
philpem@5 | 252 | } |
philpem@5 | 253 | else { |
philpem@5 | 254 | unsigned int l = 16; std::sscanf(file_m,"block%u",&l); |
philpem@5 | 255 | mask.assign(img.dimx()/l,img.dimy()/l); |
philpem@5 | 256 | cimg_forXY(mask,x,y) mask(x,y) = (x+y)%2; |
philpem@5 | 257 | img0 = img; |
philpem@5 | 258 | } |
philpem@5 | 259 | mask.resize(img.dimx(),img.dimy(),1,1); |
philpem@5 | 260 | if (dilate) mask.dilate(dilate); |
philpem@5 | 261 | switch (init) { |
philpem@5 | 262 | case 0 : { cimg_forXYV(img,x,y,k) if (mask(x,y)) img(x,y,k) = 0; } break; |
philpem@5 | 263 | case 1 : { cimg_forXYV(img,x,y,k) if (mask(x,y)) img(x,y,k) = 255; } break; |
philpem@5 | 264 | case 2 : { cimg_forXYV(img,x,y,k) if (mask(x,y)) img(x,y,k) = (T)(255*cimg::rand()); } break; |
philpem@5 | 265 | case 3 : break; |
philpem@5 | 266 | default: { |
philpem@5 | 267 | typedef unsigned char uchar; |
philpem@5 | 268 | CImg<unsigned char> tmask(mask), ntmask(tmask); |
philpem@5 | 269 | CImg_3x3(M,uchar); Mpp = Mnp = Mpn = Mnn = 0; |
philpem@5 | 270 | CImg_3x3(I,T); Ipp = Inp = Icc = Ipn = Inn = 0; |
philpem@5 | 271 | while (ntmask.max()>0) { |
philpem@5 | 272 | cimg_for3x3(tmask,x,y,0,0,M) if (Mcc && (!Mpc || !Mnc || !Mcp || !Mcn)) { |
philpem@5 | 273 | const float |
philpem@5 | 274 | ccp = Mcp?0.0f:1.0f, cpc = Mpc?0.0f:1.0f, |
philpem@5 | 275 | cnc = Mnc?0.0f:1.0f, ccn = Mcn?0.0f:1.0f, |
philpem@5 | 276 | csum = ccp + cpc + cnc + ccn; |
philpem@5 | 277 | cimg_forV(img,k) { |
philpem@5 | 278 | cimg_get3x3(img,x,y,0,k,I); |
philpem@5 | 279 | img(x,y,k) = (T)((ccp*Icp + cpc*Ipc + cnc*Inc + ccn*Icn)/csum); |
philpem@5 | 280 | } |
philpem@5 | 281 | ntmask(x,y) = 0; |
philpem@5 | 282 | } |
philpem@5 | 283 | tmask = ntmask; |
philpem@5 | 284 | } |
philpem@5 | 285 | } break; |
philpem@5 | 286 | } |
philpem@5 | 287 | } |
philpem@5 | 288 | |
philpem@5 | 289 | // Specific parameters for image resizing |
philpem@5 | 290 | //---------------------------------------- |
philpem@5 | 291 | if (resize_mode) { |
philpem@5 | 292 | cimg_help("\n + Resizing mode parameters\n ------------------------"); |
philpem@5 | 293 | const char *geom0 = cimg_option("-g0",(char*)0,"Input image geometry"); |
philpem@5 | 294 | const char *geom = cimg_option("-g",(char*)0,"Output image geometry"); |
philpem@5 | 295 | const bool anchor = cimg_option("-anchor",true,"Anchor original pixels (keep their original values)"); |
philpem@5 | 296 | const unsigned int init = cimg_option("-init",3,"Initial estimate (1=block, 3=linear, 4=Moving average, 5=bicubic)"); |
philpem@5 | 297 | amplitude = cimg_option("-dt",20.0f,"Regularization strength per iteration (>=0)"); |
philpem@5 | 298 | sharpness = cimg_option("-p",0.2f,"Contour preservation (>=0)"); |
philpem@5 | 299 | anisotropy = cimg_option("-a",0.9f,"Smoothing anisotropy (0<=a<=1)"); |
philpem@5 | 300 | alpha = cimg_option("-alpha",0.1f,"Noise scale (>=0)"); |
philpem@5 | 301 | sigma = cimg_option("-sigma",1.5f,"Geometry regularity (>=0)"); |
philpem@5 | 302 | gauss_prec = cimg_option("-prec",2.0f,"Computation precision (>0)"); |
philpem@5 | 303 | dl = cimg_option("-dl",0.8f,"Spatial integration step (0<=dl<=1)"); |
philpem@5 | 304 | da = cimg_option("-da",30.0f,"Angular integration step (0<=da<=90)"); |
philpem@5 | 305 | interpolation = cimg_option("-interp",0,"Interpolation type (0=Nearest-neighbor, 1=Linear, 2=Runge-Kutta)"); |
philpem@5 | 306 | |
philpem@5 | 307 | gprintf(stderr,"- Image Resizing mode\n"); |
philpem@5 | 308 | if (!geom && !geom0) { |
philpem@5 | 309 | std::fprintf(stderr,"%s : You need to specify an output geometry or an input geometry (option -g or -g0).\n\n", |
philpem@5 | 310 | cimg::basename(argv[0])); |
philpem@5 | 311 | std::exit(0); |
philpem@5 | 312 | } |
philpem@5 | 313 | if (!cimg::strcmp("-resize",resize_mode)) { |
philpem@5 | 314 | std::fprintf(stderr,"%s : You must specify a valid input image filename after the '-resize' flag.\n\n", |
philpem@5 | 315 | cimg::basename(argv[0])); |
philpem@5 | 316 | std::exit(0); |
philpem@5 | 317 | } |
philpem@5 | 318 | gprintf(stderr,"- Load input image '%s'...",cimg::basename(resize_mode)); |
philpem@5 | 319 | img.load(resize_mode); |
philpem@5 | 320 | gprintf(stderr,"\r- Input image : '%s' (size %dx%d, value range [%g,%g])\n", |
philpem@5 | 321 | cimg::basename(resize_mode),img.dimx(),img.dimy(),(double)img.min(),(double)img.max()); |
philpem@5 | 322 | if (noise_g || noise_u || noise_s) { |
philpem@5 | 323 | img0 = img; |
philpem@5 | 324 | img.noise(noise_g,0).noise(noise_u,1).noise(noise_s,2); |
philpem@5 | 325 | gprintf(stderr,"\r- Noisy image : value range [%g,%g], PSNR Noisy / Original : %g\n", |
philpem@5 | 326 | (double)img.min(),(double)img.max(),img.PSNR(img0)); |
philpem@5 | 327 | } |
philpem@5 | 328 | int w, h; |
philpem@5 | 329 | if (geom0) { |
philpem@5 | 330 | int w0, h0; |
philpem@5 | 331 | get_geom(geom0,w0,h0); |
philpem@5 | 332 | w0 = w0>0?w0:-w0*img.dimx()/100; |
philpem@5 | 333 | h0 = h0>0?h0:-h0*img.dimy()/100; |
philpem@5 | 334 | gprintf(stderr,"- Reducing geometry to %dx%d using %s interpolation.\n",w0,h0, |
philpem@5 | 335 | init==1?"bloc":(init==3?"linear":(init==5?"bicubic":"moving average"))); |
philpem@5 | 336 | img0.assign(img); |
philpem@5 | 337 | w = img.dimx(); |
philpem@5 | 338 | h = img.dimy(); |
philpem@5 | 339 | img.resize(w0,h0,-100,-100,5); |
philpem@5 | 340 | } |
philpem@5 | 341 | if (geom) { |
philpem@5 | 342 | get_geom(geom,w,h); |
philpem@5 | 343 | w = w>0?w:-w*img.dimx()/100; |
philpem@5 | 344 | h = h>0?h:-h*img.dimy()/100; |
philpem@5 | 345 | } |
philpem@5 | 346 | mask.assign(img.dimx(),img.dimy(),1,1,255); |
philpem@5 | 347 | if (!anchor) mask.resize(w,h,1,1,1); else mask = !mask.resize(w,h,1,1,4); |
philpem@5 | 348 | img.resize(w,h,1,-100,init); |
philpem@5 | 349 | if (img0) { gprintf(stderr,"\r- PSNR Original / Thumbnail : %g\n",img.PSNR(img0)); } |
philpem@5 | 350 | } |
philpem@5 | 351 | |
philpem@5 | 352 | // Specific parameters for image cleaning |
philpem@5 | 353 | //---------------------------------------- |
philpem@5 | 354 | if (clean_mode) { |
philpem@5 | 355 | cimg_help("\n + Cleaning mode parameters\n ------------------------"); |
philpem@5 | 356 | patch_size = cimg_option("-p",4,"Patch size (>0)"); |
philpem@5 | 357 | sigma_s = cimg_option("-ss",15.0f,"Spatial sigma (>0)"); |
philpem@5 | 358 | sigma_p = cimg_option("-sp",10.0f,"Patch sigma (>0)"); |
philpem@5 | 359 | lookup_size = cimg_option("-r",7,"Size of the lookup region (>0)"); |
philpem@5 | 360 | |
philpem@5 | 361 | gprintf(stderr,"- Image Cleaning mode\n"); |
philpem@5 | 362 | if (!cimg::strcmp("-clean",clean_mode)) { |
philpem@5 | 363 | std::fprintf(stderr,"%s : You must specify a valid input image filename after the '-clean' flag.\n\n", |
philpem@5 | 364 | cimg::basename(argv[0])); |
philpem@5 | 365 | std::exit(0); |
philpem@5 | 366 | } |
philpem@5 | 367 | gprintf(stderr,"- Load input image '%s'...",cimg::basename(clean_mode)); |
philpem@5 | 368 | img.load(clean_mode); |
philpem@5 | 369 | gprintf(stderr,"\r- Input image : '%s' (size %dx%d, value range [%g,%g])\n", |
philpem@5 | 370 | cimg::basename(clean_mode),img.dimx(),img.dimy(),(double)img.min(),(double)img.max()); |
philpem@5 | 371 | if (noise_g || noise_u || noise_s) { |
philpem@5 | 372 | img0 = img; |
philpem@5 | 373 | img.noise(noise_g,0).noise(noise_u,1).noise(noise_s,2); |
philpem@5 | 374 | gprintf(stderr,"\r- Noisy image : value range [%g,%g], PSNR Noisy / Original : %g\n", |
philpem@5 | 375 | (double)img.min(),(double)img.max(),img.PSNR(img0)); |
philpem@5 | 376 | } |
philpem@5 | 377 | } |
philpem@5 | 378 | |
philpem@5 | 379 | // Load reference image if any specified |
philpem@5 | 380 | //-------------------------------------- |
philpem@5 | 381 | if (reference_image) { |
philpem@5 | 382 | gprintf(stderr,"- Load reference image '%s'...",cimg::basename(reference_image)); |
philpem@5 | 383 | imgr.load(reference_image); |
philpem@5 | 384 | gprintf(stderr,"\r- Reference image : '%s' (size %dx%d, value range [%g,%g])", |
philpem@5 | 385 | cimg::basename(reference_image),imgr.dimx(),imgr.dimy(),(double)imgr.min(),(double)imgr.max()); |
philpem@5 | 386 | if (img0) { imgr.resize(img0); gprintf(stderr,", PSNR Reference / Original : %g dB\n",imgr.PSNR(img0)); } |
philpem@5 | 387 | else { imgr.resize(img); gprintf(stderr,"\n"); } |
philpem@5 | 388 | } |
philpem@5 | 389 | |
philpem@5 | 390 | // Init images and display |
philpem@5 | 391 | //------------------------- |
philpem@5 | 392 | CImg<T> dest(img); |
philpem@5 | 393 | unsigned int crange_beg = 0, crange_end = dest.dimv()-1U; |
philpem@5 | 394 | if (color_base) { |
philpem@5 | 395 | switch (nb_bits) { |
philpem@5 | 396 | case 8: dest.RGBtoYCbCr(); break; |
philpem@5 | 397 | case 16: (dest/=256).RGBtoYCbCr(); break; |
philpem@5 | 398 | default: std::fprintf(stderr,"\n%s : YCbCr color base is not authorized for 32bits float-valued images.\n\n", |
philpem@5 | 399 | cimg::basename(argv[0])); std::exit(0); |
philpem@5 | 400 | } |
philpem@5 | 401 | } |
philpem@5 | 402 | if (channel_range) std::sscanf(channel_range,"%u%*c%u",&crange_beg,&crange_end); |
philpem@5 | 403 | gprintf(stderr,"- Color base : %s, Channels range : %u-%u\n",color_base?"YCbCr":"RGB",crange_beg,crange_end); |
philpem@5 | 404 | if (!visu && !append_result) img.assign(); |
philpem@5 | 405 | if (visu) { |
philpem@5 | 406 | const int sx = 2*CImgDisplay::screen_dimx()/3, sy = 2*CImgDisplay::screen_dimy()/3; |
philpem@5 | 407 | int nwidth = dest.dimx(), nheight = dest.dimy(); |
philpem@5 | 408 | if (nwidth>sx) { nheight = nheight*sx/nwidth; nwidth = sx; } |
philpem@5 | 409 | if (nheight>sy) { nwidth = nwidth*sy/nheight; nheight = sy; } |
philpem@5 | 410 | disp.assign(nwidth,nheight,"GREYCstoration"); |
philpem@5 | 411 | if (color_base) { |
philpem@5 | 412 | if (nb_bits==16) (dest.get_YCbCrtoRGB()*=256).display(disp); |
philpem@5 | 413 | else dest.get_YCbCrtoRGB().display(disp); |
philpem@5 | 414 | } |
philpem@5 | 415 | else dest.display(disp); |
philpem@5 | 416 | } |
philpem@5 | 417 | const float gfact = (value_factor>0)?value_factor:((sizeof(T)==2)?1.0f/256:1.0f); |
philpem@5 | 418 | |
philpem@5 | 419 | //--------------------------------- |
philpem@5 | 420 | // Begin GREYCstoration iterations |
philpem@5 | 421 | //--------------------------------- |
philpem@5 | 422 | bool stop_all = false; |
philpem@5 | 423 | for (unsigned int iter=0; iter<nb_iterations && !stop_all; iter++) { |
philpem@5 | 424 | bool stop_iteration = false; |
philpem@5 | 425 | |
philpem@5 | 426 | // Run one iteration of the GREYCstoration filter |
philpem@5 | 427 | //------------------------------------------------ |
philpem@5 | 428 | CImg<T> dest_range = dest.get_shared_channels(crange_beg,crange_end); |
philpem@5 | 429 | if (restore_mode) |
philpem@5 | 430 | dest_range.greycstoration_run(amplitude,sharpness,anisotropy,alpha,sigma,gfact,dl,da,gauss_prec, |
philpem@5 | 431 | interpolation,fast_approx,tile_size,tile_border,nb_threads); |
philpem@5 | 432 | if (clean_mode) |
philpem@5 | 433 | dest_range.greycstoration_patch_run(patch_size,sigma_s,sigma_p,lookup_size, |
philpem@5 | 434 | tile_size,tile_border,nb_threads,fast_approx); |
philpem@5 | 435 | if (inpaint_mode || resize_mode) |
philpem@5 | 436 | dest_range.greycstoration_run(mask,amplitude,sharpness,anisotropy,alpha,sigma,gfact,dl,da,gauss_prec, |
philpem@5 | 437 | interpolation,fast_approx,tile_size,tile_border,nb_threads); |
philpem@5 | 438 | do { |
philpem@5 | 439 | const unsigned int progress = (unsigned int)dest_range.greycstoration_progress(); |
philpem@5 | 440 | gprintf(stderr,"\r- Processing : Iteration %u/%u (%u%%)\t\t",1+iter,nb_iterations,progress); |
philpem@5 | 441 | if (disp) { |
philpem@5 | 442 | if (disp.is_resized) disp.resize(); |
philpem@5 | 443 | disp.set_title("Processing : Iteration %u/%u (%u%%)",1+iter,nb_iterations,progress); |
philpem@5 | 444 | if (disp.is_closed || disp.is_keyQ || disp.is_keyESC) { |
philpem@5 | 445 | dest_range.greycstoration_stop(); |
philpem@5 | 446 | stop_iteration = true; |
philpem@5 | 447 | iter = nb_iterations-1; |
philpem@5 | 448 | } |
philpem@5 | 449 | } |
philpem@5 | 450 | cimg::wait(200); |
philpem@5 | 451 | } while (dest_range.greycstoration_is_running()); |
philpem@5 | 452 | if (!stop_iteration && sdt>0) dest_range.sharpen(sdt,sp,alpha/3,sigma/3); |
philpem@5 | 453 | dest_range.cut(cimg::type<T>::min(),cimg::type<T>::max()); |
philpem@5 | 454 | |
philpem@5 | 455 | // Prepare for next iteration |
philpem@5 | 456 | //--------------------------- |
philpem@5 | 457 | CImg<T> tmp_rgb = color_base?(nb_bits==16?dest.get_YCbCrtoRGB()*=256:dest.get_YCbCrtoRGB()):CImg<T>(), |
philpem@5 | 458 | &dest_rgb = color_base?tmp_rgb:dest; |
philpem@5 | 459 | if (disp && visu) dest_rgb.display(disp); |
philpem@5 | 460 | if (file_o && saving_step && !(iter%saving_step)) dest_rgb.save(file_o,iter); |
philpem@5 | 461 | |
philpem@5 | 462 | // Display result and allows user interaction if needed. |
philpem@5 | 463 | //------------------------------------------------------- |
philpem@5 | 464 | if (iter==nb_iterations-1) { |
philpem@5 | 465 | gprintf(stderr,"\r- Processing : Done ! \n"); |
philpem@5 | 466 | if (img0) { gprintf(stderr,"- PSNR Restored / Original : %g dB\n",dest_rgb.PSNR(img0)); } |
philpem@5 | 467 | if (disp) { |
philpem@5 | 468 | static bool first_time = true; |
philpem@5 | 469 | if (first_time) { |
philpem@5 | 470 | first_time = false; |
philpem@5 | 471 | gprintf(stderr, |
philpem@5 | 472 | "- GREYCstoration interface :\n" |
philpem@5 | 473 | " > You can now zoom to a particular rectangular region,\n" |
philpem@5 | 474 | " or press one of the following key on the display window :\n" |
philpem@5 | 475 | " SPACE : Swap views.\n" |
philpem@5 | 476 | " S : Save a snapshot of the current image.\n" |
philpem@5 | 477 | " I : Run another iteration.\n" |
philpem@5 | 478 | " Q : Quit GREYCstoration.\n"); |
philpem@5 | 479 | } |
philpem@5 | 480 | |
philpem@5 | 481 | CImgList<T> visu; |
philpem@5 | 482 | visu.insert(img0,~0,true).insert(img,~0,true).insert(dest_rgb,~0,true).insert(imgr,~0U,true); |
philpem@5 | 483 | const char *titles[4] = { "original", "noisy", "restored", "reference"}; |
philpem@5 | 484 | unsigned int visupos = 2; |
philpem@5 | 485 | CImgDisplay dispz; |
philpem@5 | 486 | CImg<T> zoom; |
philpem@5 | 487 | int snb = 0; |
philpem@5 | 488 | bool stop_interact = false; |
philpem@5 | 489 | while (!stop_interact) { |
philpem@5 | 490 | disp.show().set_title("GREYCstoration (%s)",titles[visupos]); |
philpem@5 | 491 | const CImg<int> s = visu(visupos).get_select(disp,2); |
philpem@5 | 492 | if (disp.is_closed) stop_interact = true; |
philpem@5 | 493 | switch (disp.key) { |
philpem@5 | 494 | case cimg::keySPACE: do { visupos = (visupos+1)%visu.size; } while (!visu(visupos)); break; |
philpem@5 | 495 | case cimg::keyBACKSPACE: do { visupos = (visupos-1+visu.size)%visu.size; } while (!visu(visupos)); break; |
philpem@5 | 496 | case cimg::keyQ: stop_interact = stop_all = true; break; |
philpem@5 | 497 | case cimg::keyI: |
philpem@5 | 498 | stop_interact = true; |
philpem@5 | 499 | gprintf(stderr,"- Perform iteration %u...\n",++nb_iterations); |
philpem@5 | 500 | dest_rgb.display(disp); |
philpem@5 | 501 | break; |
philpem@5 | 502 | case cimg::keyS: |
philpem@5 | 503 | if (!snb) { |
philpem@5 | 504 | if (!append_result) dest_rgb.save(file_o?file_o:"GREYCstoration.bmp"); |
philpem@5 | 505 | else CImgList<T>(img,dest_rgb).get_append('x').save(file_o?file_o:"GREYCstoration.bmp"); |
philpem@5 | 506 | } |
philpem@5 | 507 | if (zoom) zoom.save(file_o?file_o:"GREYCstoration.bmp",snb); |
philpem@5 | 508 | gprintf(stderr,"- Snapshot %u : '%s' saved\n",snb++,file_o?file_o:"GREYCstoration.bmp"); |
philpem@5 | 509 | break; |
philpem@5 | 510 | } |
philpem@5 | 511 | disp.key = 0; |
philpem@5 | 512 | if (disp.is_resized) disp.resize().display(visu(visupos)); |
philpem@5 | 513 | if (dispz && dispz.is_resized) dispz.resize().display(zoom); |
philpem@5 | 514 | if (dispz && dispz.is_closed) dispz.assign(); |
philpem@5 | 515 | |
philpem@5 | 516 | if (s[0]>=0 && s[1]>=0 && s[3]>=0 && s[4]>=0) { |
philpem@5 | 517 | const int x0 = s[0], y0 = s[1], x1 = s[3], y1 = s[4]; |
philpem@5 | 518 | if (cimg::abs(x0-x1)>4 && cimg::abs(y0-y1)>4) { |
philpem@5 | 519 | CImgList<T> tmp(img.get_crop(x0,y0,x1,y1), dest_rgb.get_crop(x0,y0,x1,y1)); |
philpem@5 | 520 | if (img0) tmp.insert(img0.get_crop(x0,y0,x1,y1),0); |
philpem@5 | 521 | if (imgr) tmp.insert(imgr.get_crop(x0,y0,x1,y1)); |
philpem@5 | 522 | zoom = tmp.get_append('x','c'); |
philpem@5 | 523 | if (!dispz) { |
philpem@5 | 524 | const int sx = 5*CImgDisplay::screen_dimx()/6, sy = 5*CImgDisplay::screen_dimy()/6; |
philpem@5 | 525 | int nwidth = zoom.dimx(), nheight = zoom.dimy(); |
philpem@5 | 526 | if (nwidth>nheight) { nheight = nheight*sx/nwidth; nwidth = sx; } |
philpem@5 | 527 | else { nwidth = nwidth*sy/nheight; nheight = sy; } |
philpem@5 | 528 | dispz.assign(zoom.get_resize(nwidth,nheight)); |
philpem@5 | 529 | dispz.set_title("GREYCstoration (zoom) : - %s %s %s %s", |
philpem@5 | 530 | img0?"original -":"", |
philpem@5 | 531 | img?"noisy -":"", |
philpem@5 | 532 | dest?"restored -":"", |
philpem@5 | 533 | imgr?"reference -":""); |
philpem@5 | 534 | } else dispz.resize(dispz.dimx(),dispz.dimx()*zoom.dimy()/zoom.dimx(),false); |
philpem@5 | 535 | dispz.display(zoom).show(); |
philpem@5 | 536 | } |
philpem@5 | 537 | } |
philpem@5 | 538 | } |
philpem@5 | 539 | } |
philpem@5 | 540 | } |
philpem@5 | 541 | } |
philpem@5 | 542 | |
philpem@5 | 543 | // Save result and exit |
philpem@5 | 544 | //---------------------- |
philpem@5 | 545 | if (file_o) { |
philpem@5 | 546 | CImg<T> tmp_rgb = color_base?(nb_bits==16?dest.get_YCbCrtoRGB()*=256:dest.get_YCbCrtoRGB()):CImg<T>(), |
philpem@5 | 547 | &dest_rgb = color_base?tmp_rgb:dest; |
philpem@5 | 548 | if (jpg_quality) { |
philpem@5 | 549 | gprintf(stderr,"\n- Saving output image '%s' (JPEG quality = %u%%)\n",file_o,jpg_quality); |
philpem@5 | 550 | if (!append_result) dest_rgb.save_jpeg(file_o,jpg_quality); |
philpem@5 | 551 | else CImgList<T>(img,dest_rgb).get_append('x').save_jpeg(file_o,jpg_quality); |
philpem@5 | 552 | } else { |
philpem@5 | 553 | gprintf(stderr,"\n- Saving output image '%s'\n",file_o); |
philpem@5 | 554 | if (!append_result) dest_rgb.save(file_o); |
philpem@5 | 555 | else CImgList<T>(img,dest_rgb).get_append('x').save(file_o); |
philpem@5 | 556 | } |
philpem@5 | 557 | } |
philpem@5 | 558 | gprintf(stderr,"\n- Quit\n\n"); |
philpem@5 | 559 | } |
philpem@5 | 560 | |
philpem@5 | 561 | |
philpem@5 | 562 | /*----------------- |
philpem@5 | 563 | Main procedure |
philpem@5 | 564 | ----------------*/ |
philpem@5 | 565 | int main(int argc,char **argv) { |
philpem@5 | 566 | const unsigned int color_base = cimg_option("-cbase",0,0); |
philpem@5 | 567 | switch (cimg_option("-bits",8,0)) { |
philpem@5 | 568 | case 32: { float pixel_type = 0; greycstoration(argc,argv,pixel_type); } break; |
philpem@5 | 569 | case 16: { |
philpem@5 | 570 | if (!color_base) { float pixel_type = 0; greycstoration(argc,argv,pixel_type); } |
philpem@5 | 571 | else { unsigned short pixel_type = 0; greycstoration(argc,argv,pixel_type); } |
philpem@5 | 572 | } break; |
philpem@5 | 573 | default: { unsigned char pixel_type = 0; greycstoration(argc,argv,pixel_type); } break; |
philpem@5 | 574 | } |
philpem@5 | 575 | return 0; |
philpem@5 | 576 | } |