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 #ifndef PF_DEFRINGE_H 31 #define PF_DEFRINGE_H 32 33 namespace PF 34 { 35 36 #define MAGIC_THRESHOLD_COEFF 33.0f 37 38 const float fib[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233 }; 39 40 enum defringe_method_t 41 { 42 MODE_GLOBAL_AVERAGE, 43 MODE_LOCAL_AVERAGE, 44 MODE_STATIC 45 }; 46 47 class DefringePar: public OpParBase 48 { 49 PropertyBase op_mode; 50 Property<float> radius; 51 Property<float> threshold; 52 53 ProcessorBase* gauss; 54 ProcessorBase* convert2lab; 55 ProcessorBase* convert2input; 56 ProcessorBase* defringe_algo; 57 58 PF::ICCProfile* in_profile; 59 60 public: 61 DefringePar(); 62 has_intensity()63 bool has_intensity() { return false; } 64 bool needs_caching(); 65 get_op_mode()66 int get_op_mode() { return op_mode.get_enum_value().first; } get_radius()67 float get_radius() { return radius.get(); } get_threshold()68 float get_threshold() { return threshold.get(); } 69 70 void compute_padding( VipsImage* full_res, unsigned int id, unsigned int level ); 71 72 VipsImage* build(std::vector<VipsImage*>& in, int first, 73 VipsImage* imap, VipsImage* omap, 74 unsigned int& level); 75 }; 76 77 78 79 class DefringeAlgoPar: public OpParBase 80 { 81 defringe_method_t op_mode; 82 float sigma; 83 int radius; 84 float threshold; 85 86 int* xy_avg; 87 int* xy_small; 88 int samples_avg; 89 int samples_small; 90 int dmax, dmax_small; 91 92 public: DefringeAlgoPar()93 DefringeAlgoPar(): OpParBase() 94 { 95 radius = threshold = 0; 96 xy_avg = xy_small = NULL; 97 dmax = dmax_small = 0; 98 } 99 ~DefringeAlgoPar()100 ~DefringeAlgoPar() 101 { 102 if( xy_avg ) free( xy_avg ); 103 if( xy_small ) free( xy_small ); 104 } 105 get_padding()106 int get_padding() { return( (op_mode == MODE_LOCAL_AVERAGE) ? dmax : dmax_small ); } 107 108 /* Function to derive the output area from the input area 109 */ transform(const VipsRect * rin,VipsRect * rout,int)110 virtual void transform(const VipsRect* rin, VipsRect* rout, int /*id*/) 111 { 112 int pad = get_padding(); 113 rout->left = rin->left+pad; 114 rout->top = rin->top+pad; 115 rout->width = rin->width-pad*2; 116 rout->height = rin->height-pad*2; 117 } 118 119 /* Function to derive the area to be read from input images, 120 based on the requested output area 121 */ transform_inv(const VipsRect * rout,VipsRect * rin,int)122 virtual void transform_inv(const VipsRect* rout, VipsRect* rin, int /*id*/) 123 { 124 int pad = get_padding(); 125 rin->left = rout->left-pad; 126 rin->top = rout->top-pad; 127 rin->width = rout->width+pad*2; 128 rin->height = rout->height+pad*2; 129 } 130 131 set_op_mode(defringe_method_t m)132 void set_op_mode( defringe_method_t m ) { op_mode = m; } get_op_mode()133 defringe_method_t get_op_mode() { return op_mode; } set_sigma(float s)134 void set_sigma( float s ) { sigma = s; } get_radius()135 int get_radius() { return radius; } set_threshold(float t)136 void set_threshold( float t ) { threshold = t; } get_threshold()137 float get_threshold() { return threshold; } 138 get_samples_avg()139 int get_samples_avg() { return samples_avg; } get_xy_avg()140 int* get_xy_avg() { return xy_avg; } get_samples_small()141 int get_samples_small() { return samples_small; } get_xy_small()142 int* get_xy_small() { return xy_small; } 143 fib_latt(int * const x,int * const y,float radius,int step,int idx)144 void fib_latt(int *const x, int *const y, float radius, int step, int idx) 145 { 146 // idx < 1 because division by zero is also a problem in the following line 147 if(idx >= (int)(sizeof(fib) / sizeof(float)) - 1 || idx < 1) 148 { 149 *x = 0; 150 *y = 0; 151 std::cout<<"Fibonacci lattice index wrong/out of bounds in: defringe module"<<std::endl; 152 return; 153 } 154 float px = step / fib[idx], py = step * (fib[idx + 1] / fib[idx]); 155 py -= (int)py; 156 float dx = px * radius, dy = py * radius; 157 *x = round(dx - radius / 2.0); 158 *y = round(dy - radius / 2.0); 159 } 160 fb_init()161 bool fb_init() 162 { 163 const float sigma_tmp = fmax(0.1f, fabs(sigma)); 164 radius = ceil(2.0f * ceilf(sigma_tmp)); 165 int samples_wish = (int)(radius * radius); 166 int sampleidx_avg = 0; 167 dmax = 0; 168 dmax_small = 0; 169 170 // select samples by fibonacci number 171 if(samples_wish > 89) { 172 sampleidx_avg = 12; // 144 samples 173 } else if(samples_wish > 55) { 174 sampleidx_avg = 11; // 89 samples 175 } else if(samples_wish > 34) { 176 sampleidx_avg = 10; // ..you get the idea 177 } else if(samples_wish > 21) { 178 sampleidx_avg = 9; 179 } else if(samples_wish > 13) { 180 sampleidx_avg = 8; 181 } else { 182 // don't use less than 13 samples 183 sampleidx_avg = 7; 184 } 185 186 const int sampleidx_small = sampleidx_avg - 1; 187 const int small_radius = MAX(radius, 3); 188 const int avg_radius = 24 + radius * 4; 189 190 samples_small = (int)fib[sampleidx_small]; 191 samples_avg = (int)fib[sampleidx_avg]; 192 193 int* tmp; 194 195 // precompute all required fibonacci lattices: 196 if( xy_avg ) free( xy_avg ); 197 xy_avg = (int*)malloc((size_t)2 * sizeof(int) * samples_avg); 198 if (xy_avg != NULL) 199 { 200 tmp = xy_avg; 201 for(int u = 0; u < samples_avg; u++) 202 { 203 int dx, dy; 204 fib_latt(&dx, &dy, avg_radius, u, sampleidx_avg); 205 *tmp++ = dx; 206 *tmp++ = dy; 207 if( dmax < abs(dx) ) dmax = abs(dx); 208 if( dmax < abs(dy) ) dmax = abs(dy); 209 } 210 } 211 else 212 { 213 std::cout<<"Error allocating memory for fibonacci lattice in: defringe module"<<std::endl; 214 return false; 215 } 216 217 if( xy_small ) free( xy_small ); 218 xy_small = (int*)malloc((size_t)2 * sizeof(int) * samples_small); 219 if (xy_small != NULL) 220 { 221 tmp = xy_small; 222 for(int u = 0; u < samples_small; u++) 223 { 224 int dx, dy; 225 fib_latt(&dx, &dy, small_radius, u, sampleidx_small); 226 *tmp++ = dx; 227 *tmp++ = dy; 228 if( dmax_small < abs(dx) ) dmax_small = abs(dx); 229 if( dmax_small < abs(dy) ) dmax_small = abs(dy); 230 } 231 } 232 else 233 { 234 std::cout<<"Error allocating memory for fibonacci lattice in: defringe module"<<std::endl; 235 return false; 236 } 237 238 return true; 239 } 240 }; 241 242 243 244 template < OP_TEMPLATE_DEF > 245 class DefringeProc 246 { 247 public: render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)248 void render(VipsRegion** in, int n, int in_first, 249 VipsRegion* imap, VipsRegion* omap, 250 VipsRegion* out, OpParBase* par) 251 { 252 } 253 }; 254 255 256 257 template < OP_TEMPLATE_DEF > 258 class DefringeAlgoProc 259 { 260 public: render(VipsRegion ** in,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * out,OpParBase * par)261 void render(VipsRegion** in, int n, int in_first, 262 VipsRegion* imap, VipsRegion* omap, 263 VipsRegion* out, OpParBase* par) 264 { 265 } 266 }; 267 268 269 270 template < OP_TEMPLATE_DEF_TYPE_SPEC > 271 class DefringeAlgoProc< OP_TEMPLATE_IMP_TYPE_SPEC(float) > 272 { 273 public: render(VipsRegion ** ireg,int n,int in_first,VipsRegion * imap,VipsRegion * omap,VipsRegion * oreg,OpParBase * par)274 void render(VipsRegion** ireg, int n, int in_first, 275 VipsRegion* imap, VipsRegion* omap, 276 VipsRegion* oreg, OpParBase* par) 277 { 278 if( n != 2 ) return; 279 if( ireg[0] == NULL ) return; 280 if( ireg[1] == NULL ) return; 281 282 DefringeAlgoPar* opar = dynamic_cast<DefringeAlgoPar*>(par); 283 if( !opar ) return; 284 285 #define RESCALE_LAB(ab) ((ab)/255.f) 286 287 bool bError = false; 288 289 const float threshold = RESCALE_LAB(opar->get_threshold()); 290 VipsRect *ir = &ireg[0]->valid; 291 VipsRect *r = &oreg->valid; 292 const int ch = oreg->im->Bands; 293 const int iwidth = ir->width; 294 const int iheight = ir->height; 295 const int width = r->width; 296 const int height = r->height; 297 const int ilsz = iwidth*ch; 298 const int lsz = width*ch; 299 const int border = r->top - ir->top; 300 301 float* in = NULL; 302 float* pblur = NULL; 303 float* out = NULL; 304 305 //const int order = 1; // 0,1,2 306 const int radius = opar->get_radius(); 307 308 float avg_edge_chroma = 0.0f; 309 310 // save the fibonacci lattices in them later 311 int *xy_avg = NULL; 312 int *xy_small = NULL; 313 float *edge_chroma = NULL; 314 315 if(width < 2 * radius + 1 || height < 2 * radius + 1) 316 bError = true; 317 318 // Pre-Computed Fibonacci Lattices 319 int *tmp = NULL; 320 321 const int samples_small = opar->get_samples_small(); 322 const int samples_avg = opar->get_samples_avg(); 323 324 xy_avg = opar->get_xy_avg(); 325 xy_small = opar->get_xy_small(); 326 327 if (!bError) { 328 edge_chroma = (float*)malloc(iwidth * iheight * sizeof(float)); 329 if (edge_chroma != NULL) 330 { 331 memset(edge_chroma, 0, iwidth * iheight * sizeof(float)); 332 333 for(int v = 0; v < iheight; v++) 334 { 335 in = (float*)VIPS_REGION_ADDR( ireg[0], ir->left, ir->top + v ); 336 pblur = (float*)VIPS_REGION_ADDR( ireg[1], ir->left, ir->top + v ); 337 float *ec = edge_chroma + v * iwidth; 338 339 for(int t = 0, tt = 0; t < ilsz; t+=ch, tt++) 340 { 341 // edge-detect on color channels 342 // method: difference of original to gaussian blurred image: 343 //std::cout<<"defringe: t="<<t<<" ilsz="<<ilsz<<std::endl; 344 float a = in[t + 1] - pblur[t + 1]; 345 float b = in[t + 2] - pblur[t + 2]; 346 347 float edge = (a * a + b * b); // range up to 2*(256)^2 -> approx. 0 to 131072 348 349 // save local edge chroma in out[.. +3] , this is later compared with threshold 350 ec[tt] = edge; 351 352 // the average chroma of the edge-layer in the roi 353 if(MODE_GLOBAL_AVERAGE == opar->get_op_mode()) avg_edge_chroma += edge; 354 } 355 } 356 } 357 else 358 { 359 std::cout<<"Error allocating memory for edge detection in: defringe module"<<std::endl; 360 bError = true; 361 } 362 } 363 364 float thresh = 0; 365 if (!bError) { 366 if(MODE_GLOBAL_AVERAGE == opar->get_op_mode()) 367 { 368 avg_edge_chroma = avg_edge_chroma / (iwidth * iheight) + RESCALE_LAB(10.0f) * FLT_EPSILON; 369 thresh = fmax(RESCALE_LAB(0.1f), RESCALE_LAB(4.0f) * threshold * avg_edge_chroma / MAGIC_THRESHOLD_COEFF); 370 } 371 else 372 { 373 // this fixed value will later be changed when doing local averaging, or kept as-is in "static" mode 374 avg_edge_chroma = RESCALE_LAB(RESCALE_LAB(MAGIC_THRESHOLD_COEFF)); 375 thresh = fmax(RESCALE_LAB(0.1f), threshold); 376 } 377 thresh = RESCALE_LAB(thresh); 378 } 379 380 if (!bError) { 381 for(int v = 0, vv = border; v < height; v++, vv++) 382 { 383 in = (float*)VIPS_REGION_ADDR( ireg[0], r->left, r->top + v ); 384 out = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + v ); 385 386 int l = vv * iwidth; 387 int lprev = (vv - 1) * iwidth; 388 int lnext = (vv + 1) * iwidth; 389 float *ec = edge_chroma + l; 390 391 for(int t = 0, tt = border; t < lsz; t+=ch, tt++) 392 { 393 float local_thresh = thresh; 394 // think of compiler setting "-funswitch-loops" to maybe improve these things: 395 if(MODE_LOCAL_AVERAGE == opar->get_op_mode() && ec[tt] > thresh) 396 { 397 float local_avg = 0.0f; 398 // use some and not all values from the neigbourhood to speed things up: 399 const int *tmp = xy_avg; 400 for(int u = 0; u < samples_avg; u++) 401 { 402 int dx = *tmp++; 403 int dy = *tmp++; 404 int x = tt + dx; //provided that the region padding is correct, this is not needed: MAX(0, MIN(width - 1, t + dx)); 405 int y = vv + dy; //provided that the region padding is correct, this is not needed: MAX(0, MIN(height - 1, v + dy)); 406 local_avg += edge_chroma[y * iwidth + x]; 407 } 408 avg_edge_chroma = fmax(RESCALE_LAB(0.01f), (float)local_avg / samples_avg); 409 local_thresh = fmax(RESCALE_LAB(0.1f), RESCALE_LAB(4.0f) * threshold * avg_edge_chroma / MAGIC_THRESHOLD_COEFF); 410 } 411 412 if(edge_chroma[(size_t)vv * iwidth + tt] > local_thresh 413 // reduces artifacts ("region growing by 1 pixel"): 414 || edge_chroma[lprev + tt - 1] > local_thresh 415 || edge_chroma[lprev + tt] > local_thresh 416 || edge_chroma[lprev + tt + 1] > local_thresh 417 || edge_chroma[l + tt - 1] > local_thresh 418 || edge_chroma[l + tt + 1] > local_thresh 419 || edge_chroma[lnext + tt - 1] > local_thresh 420 || edge_chroma[lnext + tt] > local_thresh 421 || edge_chroma[lnext + tt + 1] > local_thresh) 422 { 423 float atot = 0.f, btot = 0.f; 424 float norm = 0.f; 425 float weight = 0.f; 426 427 // it seems better to use only some pixels from a larger window instead of all pixels from a smaller window 428 // we use a fibonacci lattice for that, samples amount need to be a fibonacci number, this can then be 429 // scaled to a certain radius 430 431 // use some neighbourhood pixels for lowest chroma average 432 const int *tmp = xy_small; 433 for(int u = 0; u < samples_small; u++) 434 { 435 int dx = *tmp++; 436 int dy = *tmp++; 437 int x = tt + dx; //provided that the region padding is correct, this is not needed: MAX(0, MIN(width - 1, t + dx)); 438 int y = vv + dy; //provided that the region padding is correct, this is not needed: MAX(0, MIN(height - 1, v + dy)); 439 440 // inverse chroma weighted average of neigbouring pixels inside window 441 // also taking average edge chromaticity into account (either global or local average) 442 443 weight = 1.0f / (edge_chroma[(size_t)y * iwidth + x] + avg_edge_chroma); 444 float *in2 = (float*)VIPS_REGION_ADDR( ireg[0], ir->left, ir->top + y ); 445 446 atot += weight * in2[x * ch + 1]; 447 btot += weight * in2[x * ch + 2]; 448 norm += weight; 449 } 450 451 // here we could try using a "balance" between original and changed value, this could be used to 452 // reduce artifcats but on first tries results weren't very convincing, 453 // and there are blend settings available anyway 454 455 double a = (atot / norm); 456 double b = (btot / norm); 457 458 out[t + 1] = a; 459 out[t + 2] = b; 460 } 461 else 462 { 463 out[t + 1] = in[t + 1]; 464 out[t + 2] = in[t + 2]; 465 } 466 out[t] = in[t]; 467 468 } 469 } 470 } 471 472 if (bError) { 473 // something went wrong, return original image 474 for( int y = 0; y < height; y++ ) { 475 in = (float*)VIPS_REGION_ADDR( ireg[0], r->left, r->top + y ); 476 out = (float*)VIPS_REGION_ADDR( oreg, r->left, r->top + y ); 477 478 for( int x = 0; x < width * ch; x++ ) out[x] = in[x]; 479 } 480 } 481 482 if (edge_chroma) free(edge_chroma); 483 } 484 485 }; 486 487 488 ProcessorBase* new_defringe(); 489 490 ProcessorBase* new_defringe_algo(); 491 492 } 493 494 #endif 495 496 497