1 /* 2 */ 3 4 /* 5 6 Copyright (C) 2014 Ferrero Andrea 7 8 This program is free software: you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation, either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. 20 21 22 */ 23 24 /* 25 26 These files are distributed with PhotoFlow - http://aferrero2707.github.io/PhotoFlow/ 27 28 */ 29 30 /* The Wavelet Decompose algorithm is based on the GIMP's Wavelet Decompose plugin, by Marco Rossini 31 * 32 * http://registry.gimp.org/node/11742 33 * 34 * */ 35 36 37 #ifndef PF_WAVDEC_H 38 #define PF_WAVDEC_H 39 40 #include <string.h> 41 #include "../base/processor.hh" 42 43 namespace PF 44 { 45 46 47 class WavDecPar: public OpParBase 48 { 49 Property<int> numScales, currScale, preview_scale, initial_lev; 50 Property<float> blendFactor; 51 52 ProcessorBase* wavdec_algo; 53 54 public: 55 WavDecPar(); 56 has_intensity()57 bool has_intensity() { return false; } 58 // bool needs_caching() { return true; } 59 get_numScales()60 int get_numScales() { return numScales.get(); } set_numScales(int a)61 void set_numScales(int a) { numScales.set(a); } get_currScale()62 int get_currScale() { return currScale.get(); } set_currScale(int a)63 void set_currScale(int a) { currScale.set(a); } get_initial_lev()64 int get_initial_lev() { return initial_lev.get(); } set_initial_lev(int s)65 void set_initial_lev(int s) { initial_lev.set(s); } get_preview_scale()66 int get_preview_scale() { return preview_scale.get(); } set_preview_scale(int s)67 void set_preview_scale(int s) { preview_scale.set(s); } 68 get_blendFactor()69 float get_blendFactor() { return blendFactor.get(); } set_blendFactor(float a)70 void set_blendFactor(float a) { blendFactor.set(a); } 71 get_padding(int nScales,int initial_lev)72 int get_padding(int nScales, int initial_lev) { return pow(2, nScales+initial_lev); } 73 void compute_padding( VipsImage* full_res, unsigned int id, unsigned int level ); 74 75 VipsImage* build(std::vector<VipsImage*>& in, int first, 76 VipsImage* imap, VipsImage* omap, 77 unsigned int& level); 78 }; 79 80 class WavDecAlgoPar: public OpParBase 81 { 82 int numScales, currScale; 83 float blendFactor; 84 int initial_lev; 85 int preview_scale; 86 int padding; 87 88 public: WavDecAlgoPar()89 WavDecAlgoPar(): OpParBase() 90 { 91 numScales = 0; 92 currScale = 0; 93 blendFactor = .5f; 94 initial_lev = 0; 95 preview_scale = 1; 96 padding = 0; 97 } 98 get_numScales()99 int get_numScales() { return numScales; } set_numScales(int a)100 void set_numScales(int a) { numScales=a; } get_currScale()101 int get_currScale() { return currScale; } set_currScale(int a)102 void set_currScale(int a) { currScale=a; } get_padding()103 int get_padding() { return padding; } set_padding(int p)104 void set_padding(int p) { padding = p; } get_initial_lev()105 int get_initial_lev() { return initial_lev; } set_initial_lev(int p)106 void set_initial_lev(int p) { initial_lev = p; } get_preview_scale()107 int get_preview_scale() { return preview_scale; } set_preview_scale(int p)108 void set_preview_scale(int p) { preview_scale = p; } 109 get_blendFactor()110 float get_blendFactor() { return (float)blendFactor; } set_blendFactor(float a)111 void set_blendFactor(float a) { blendFactor=a; } 112 get_maxScales(const int width,const int height)113 static int get_maxScales(const int width, const int height) 114 { 115 int maxscale = 0; 116 117 // smallest edge must be higher than or equal to 2^scales 118 unsigned int size = MIN(width, height); 119 while (size >>= 1) maxscale++; 120 121 return maxscale; 122 } 123 124 /* Function to derive the output area from the input area 125 */ transform(const VipsRect * rin,VipsRect * rout,int)126 virtual void transform(const VipsRect* rin, VipsRect* rout, int /*id*/) 127 { 128 int pad = get_padding(); 129 rout->left = rin->left+pad; 130 rout->top = rin->top+pad; 131 rout->width = rin->width-pad*2; 132 rout->height = rin->height-pad*2; 133 } 134 135 /* Function to derive the area to be read from input images, 136 based on the requested output area 137 */ transform_inv(const VipsRect * rout,VipsRect * rin,int)138 virtual void transform_inv(const VipsRect* rout, VipsRect* rin, int /*id*/) 139 { 140 int pad = get_padding(); 141 rin->left = rout->left-pad; 142 rin->top = rout->top-pad; 143 rin->width = rout->width+pad*2; 144 rin->height = rout->height+pad*2; 145 } 146 147 148 }; 149 150 151 152 template < OP_TEMPLATE_DEF > 153 class WavDecProc 154 { 155 public: render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,PF::OpParBase * par)156 void render(VipsRegion** ireg, int n, int in_first, 157 VipsRegion* imap, VipsRegion* omap, 158 VipsRegion* oreg, PF::OpParBase* par) 159 { 160 } 161 }; 162 163 template < OP_TEMPLATE_DEF > 164 class WavDecAlgoProc 165 { 166 public: render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)167 void render(VipsRegion** in, int n, int in_first, 168 VipsRegion* imap, VipsRegion* omap, 169 VipsRegion* out, OpParBase* par) 170 { 171 } 172 }; 173 174 175 176 template < OP_TEMPLATE_DEF_TYPE_SPEC > 177 class WavDecAlgoProc< OP_TEMPLATE_IMP_TYPE_SPEC(float) > 178 { 179 float *wd_layers; // buffer for internal use 180 float *wd_image; 181 int wd_ch; 182 int wd_width; 183 int wd_height; 184 int wd_scales; 185 int wd_return_layer; 186 float wd_blend_factor; // .128f 187 int wd_max_scales; 188 int wd_initial_lev; 189 float wd_preview_scale; 190 191 #define INDEX_WT_IMAGE(ch, index) (((index)*ch)+c) 192 193 194 public: render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)195 void render(VipsRegion** ireg, int n, int in_first, 196 VipsRegion* imap, VipsRegion* omap, 197 VipsRegion* oreg, OpParBase* par) 198 { 199 if( ireg[0] == NULL ) { 200 std::cout<<"WavDecAlgoProc::render ireg[0] == NULL"<<std::endl; 201 return; 202 } 203 204 WavDecAlgoPar* opar = dynamic_cast<WavDecAlgoPar*>(par); 205 if( !opar ) { 206 std::cout<<"WavDecAlgoProc::render opar == NULL"<<std::endl; 207 return; 208 } 209 210 VipsRect *r = &oreg->valid; 211 VipsRect *ir = &ireg[0]->valid; 212 213 wd_width = ir->width; 214 wd_height = ir->height; 215 wd_ch = oreg->im->Bands; 216 217 const int i_line_size = wd_width * wd_ch; 218 219 wd_image = (float*)malloc(wd_width * wd_height * wd_ch * sizeof(float)); 220 memset(wd_image, 0, wd_width * wd_height * wd_ch * sizeof(float)); 221 222 for( int y = 0; y < wd_height; y++ ) { 223 float *pin = (float*)VIPS_REGION_ADDR( ireg[0], ir->left, ir->top + y ); 224 float *pwd = wd_image + y * i_line_size; 225 226 memcpy(pwd, pin, i_line_size * sizeof(float)); 227 } 228 229 wd_initial_lev = (int)opar->get_initial_lev(); 230 wd_scales = (int)opar->get_numScales(); 231 wd_return_layer = (int)opar->get_currScale(); 232 wd_blend_factor = (float)opar->get_blendFactor(); 233 wd_preview_scale = (float)opar->get_preview_scale()+1; 234 wd_preview_scale = 1.f / wd_preview_scale; 235 wd_max_scales = opar->get_maxScales(wd_width, wd_height); 236 if (wd_scales > wd_max_scales) 237 { 238 wd_scales = wd_max_scales; 239 std::cout<<"WavDecAlgoProc::render: max scale is "<<wd_max_scales<<" for this image preview size"<<std::endl; 240 } 241 if (wd_return_layer > (wd_scales+1)) wd_return_layer = wd_scales+1; 242 243 // std::cout<<"WavDecAlgoProc::render: max scale is "<<wd_max_scales<<" for this image preview size"<<std::endl; 244 245 if (wd_scales > 0) 246 dwt_decompose(); 247 248 const int o_line_size = r->width*wd_ch; 249 250 for( int y = 0; y < r->height; y++ ) { 251 float *pout = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + y ); 252 float *pwd = wd_image + (y+abs(ir->height-r->height)/2) * i_line_size + (abs(ir->width-r->width)/2) * wd_ch; 253 254 memcpy(pout, pwd, o_line_size * sizeof(float)); 255 } 256 257 if (wd_image) free(wd_image); 258 259 } 260 261 262 private: dwt_decompose()263 void dwt_decompose() 264 { 265 /* this function prepares for decomposing, which is done in the function dwt_wavelet_decompose() */ 266 if (wd_scales > wd_max_scales) wd_scales = wd_max_scales; 267 if (wd_return_layer > (wd_scales+1)) wd_return_layer = wd_scales+1; 268 269 if (wd_return_layer == 0) { 270 wd_layers = (float *)malloc(wd_width * wd_height * wd_ch * sizeof(float)); 271 if (wd_layers == NULL) 272 { 273 std::cout<<"not enough memory for wavelet decomposition"<<std::endl; 274 goto cleanup; 275 } 276 memset(wd_layers, 0, wd_width * wd_height * wd_ch * sizeof(float)); 277 } 278 else { 279 wd_layers = NULL; 280 } 281 282 dwt_wavelet_decompose(); 283 284 285 cleanup: 286 if (wd_layers) 287 { 288 free(wd_layers); 289 wd_layers = NULL; 290 } 291 292 } 293 dwt_wavelet_decompose()294 void dwt_wavelet_decompose() 295 { 296 float *temp = NULL; 297 unsigned int lpass, hpass; 298 float *buffer[2] = {0, 0}; 299 int bcontinue = 1; 300 301 //const float lpass_add = sqrtf(.25f); 302 //const float lpass_mult = (1.f / 16.f); 303 const float lpass_add = 0;//8.f/257.f;//sqrtf(.25f); 304 const float lpass_mult = (1.f / 16.f);//( lpass_add * 2.f );//(1.f / 16.f); 305 const float lpass_sub = wd_blend_factor; //.128f; 306 307 const int size = wd_width * wd_height * wd_ch; 308 const int i_line_size = wd_width * wd_ch; 309 310 /* image buffers */ 311 buffer[0] = wd_image; 312 /* temporary storage */ 313 buffer[1] = (float *)malloc(size * sizeof(float)); 314 if (buffer[1] == NULL) 315 { 316 std::cout<<"not enough memory for wavelet decomposition"<<std::endl; 317 goto cleanup; 318 } 319 memset(buffer[1], 0, size * sizeof(float)); 320 321 temp = (float *)malloc(MAX(wd_width, wd_height) * wd_ch * sizeof(float)); 322 if (temp == NULL) 323 { 324 std::cout<<"not enough memory for wavelet decomposition"<<std::endl; 325 goto cleanup; 326 } 327 memset(temp, 0, MAX(wd_width, wd_height) * wd_ch * sizeof(float)); 328 329 /* iterate over wavelet scales */ 330 lpass = 1; 331 hpass = 0; 332 for (unsigned int lev = 0; lev < wd_scales && bcontinue; lev++) 333 { 334 lpass = (1 - (lev & 1)); 335 336 for (int row = 0; row < wd_height; row++) 337 { 338 dwt_hat_transform(temp, buffer[hpass] + (row * i_line_size), 1, wd_width, 1 << (lev+wd_initial_lev)); 339 memcpy(&(buffer[lpass][row * i_line_size]), temp, i_line_size * sizeof(float)); 340 } 341 342 for (int col = 0; col < wd_width; col++) 343 { 344 dwt_hat_transform(temp, buffer[lpass] + col*wd_ch, wd_width, wd_height, 1 << (lev+wd_initial_lev)); 345 for (int row = 0; row < wd_height; row++) 346 { 347 for (int c = 0; c < wd_ch; c++) 348 buffer[lpass][INDEX_WT_IMAGE(wd_ch, row * wd_width + col)] = temp[INDEX_WT_IMAGE(wd_ch, row)]; 349 } 350 } 351 352 for (int i = 0; i < size; i++) 353 { 354 //buffer[lpass][i] = ( buffer[lpass][i] + lpass_add ) * lpass_mult; 355 buffer[lpass][i] *= lpass_mult; 356 buffer[hpass][i] -= buffer[lpass][i] - lpass_sub; 357 } 358 359 if (wd_return_layer == 0) 360 { 361 dwt_add_layer(buffer[hpass], lev + 1); 362 } 363 else if (wd_return_layer == ((int)(lev + 1))) 364 { 365 dwt_get_image_layer(buffer[hpass]); 366 367 bcontinue = 0; 368 } 369 370 hpass = lpass; 371 } 372 373 // Wavelet residual 374 if (wd_return_layer == (wd_scales+1)) 375 { 376 dwt_get_image_layer(buffer[lpass]); 377 } 378 else if (wd_return_layer == 0) 379 { 380 dwt_add_layer(buffer[hpass], wd_scales+1); 381 382 dwt_get_image_layer(wd_layers); 383 } 384 385 cleanup: 386 if (temp) free(temp); 387 if (buffer[1]) free(buffer[1]); 388 389 } 390 dwt_add_layer(float * const img,const int n_scale)391 void dwt_add_layer(float *const img, const int n_scale) 392 { 393 const float lpass_sub = wd_blend_factor; //.128f; 394 const int buff_size = wd_width * wd_height * wd_ch; 395 396 if (n_scale == wd_scales+1) 397 { 398 for (int i = 0; i < buff_size; i++) 399 wd_layers[i] += img[i]; 400 } 401 else 402 { 403 for (int i = 0; i < buff_size; i++) 404 wd_layers[i] += img[i] - lpass_sub; 405 } 406 407 } 408 dwt_get_image_layer(float * const layer)409 void dwt_get_image_layer(float *const layer) 410 { 411 if (wd_image != layer) { 412 memcpy(wd_image, layer, wd_width * wd_height * wd_ch * sizeof(float)); 413 } 414 } 415 dwt_hat_transform(float * temp,float * const base,const int st,const int size,int sc)416 void dwt_hat_transform(float *temp, float *const base, const int st, const int size, int sc) 417 { 418 int i, c; 419 const float hat_mult = 2.f; 420 sc = std::min(size, (int)(sc * wd_preview_scale)); 421 422 for (i = 0; i < sc; i++) 423 { 424 for (c = 0; c < wd_ch; c++, temp++) 425 { 426 *temp = hat_mult * base[INDEX_WT_IMAGE(wd_ch, st * i)] + base[INDEX_WT_IMAGE(wd_ch, st * (sc - i))] + base[INDEX_WT_IMAGE(wd_ch, st * (i + sc))]; 427 } 428 } 429 for (; i + sc < size; i++) 430 { 431 for (c = 0; c < wd_ch; c++, temp++) 432 { 433 *temp = hat_mult * base[INDEX_WT_IMAGE(wd_ch, st * i)] + base[INDEX_WT_IMAGE(wd_ch, st * (i - sc))] + base[INDEX_WT_IMAGE(wd_ch, st * (i + sc))]; 434 } 435 } 436 for (; i < size; i++) 437 { 438 for (c = 0; c < wd_ch; c++, temp++) 439 { 440 *temp = hat_mult * base[INDEX_WT_IMAGE(wd_ch, st * i)] + base[INDEX_WT_IMAGE(wd_ch, st * (i - sc))] 441 + base[INDEX_WT_IMAGE(wd_ch, st * (2 * size - 2 - (i + sc)))]; 442 } 443 } 444 445 } 446 447 448 }; 449 450 451 ProcessorBase* new_wavdec_algo(); 452 ProcessorBase* new_wavdec(); 453 454 } 455 456 #endif 457