1 /*M/////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. 4 // 5 // By downloading, copying, installing or using the software you agree to this license. 6 // If you do not agree to this license, do not download, install, 7 // copy or use the software. 8 // 9 // 10 // License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2013, OpenCV Foundation, all rights reserved. 14 // Third party copyrights are property of their respective owners. 15 // 16 // Redistribution and use in source and binary forms, with or without modification, 17 // are permitted provided that the following conditions are met: 18 // 19 // * Redistribution's of source code must retain the above copyright notice, 20 // this list of conditions and the following disclaimer. 21 // 22 // * Redistribution's in binary form must reproduce the above copyright notice, 23 // this list of conditions and the following disclaimer in the documentation 24 // and/or other materials provided with the distribution. 25 // 26 // * The name of the copyright holders may not be used to endorse or promote products 27 // derived from this software without specific prior written permission. 28 // 29 // This software is provided by the copyright holders and contributors "as is" and 30 // any express or implied warranties, including, but not limited to, the implied 31 // warranties of merchantability and fitness for a particular purpose are disclaimed. 32 // In no event shall the Intel Corporation or contributors be liable for any direct, 33 // indirect, incidental, special, exemplary, or consequential damages 34 // (including, but not limited to, procurement of substitute goods or services; 35 // loss of use, data, or profits; or business interruption) however caused 36 // and on any theory of liability, whether in contract, strict liability, 37 // or tort (including negligence or otherwise) arising in any way out of 38 // the use of this software, even if advised of the possibility of such damage. 39 // 40 //M*/ 41 42 #include "precomp.hpp" 43 #include "opencv2/photo.hpp" 44 #include <iostream> 45 #include <stdlib.h> 46 #include <limits> 47 #include "math.h" 48 49 50 using namespace std; 51 using namespace cv; 52 53 double myinf = std::numeric_limits<double>::infinity(); 54 55 class Domain_Filter 56 { 57 public: 58 Mat ct_H, ct_V, horiz, vert, O, O_t, lower_idx, upper_idx; 59 void init(const Mat &img, int flags, float sigma_s, float sigma_r); 60 void getGradientx( const Mat &img, Mat &gx); 61 void getGradienty( const Mat &img, Mat &gy); 62 void diffx(const Mat &img, Mat &temp); 63 void diffy(const Mat &img, Mat &temp); 64 void find_magnitude(Mat &img, Mat &mag); 65 void compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius); 66 void compute_Rfilter(Mat &O, Mat &horiz, float sigma_h); 67 void compute_NCfilter(Mat &O, Mat &horiz, Mat &psketch, float radius); 68 void filter(const Mat &img, Mat &res, float sigma_s, float sigma_r, int flags); 69 void pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor); 70 void Depth_of_field(const Mat &img, Mat &img1, float sigma_s, float sigma_r); 71 }; 72 73 void Domain_Filter::diffx(const Mat &img, Mat &temp) 74 { 75 int channel = img.channels(); 76 77 for(int i = 0; i < img.size().height; i++) 78 for(int j = 0; j < img.size().width-1; j++) 79 { 80 for(int c =0; c < channel; c++) 81 { 82 temp.at<float>(i,j*channel+c) = 83 img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c); 84 } 85 } 86 } 87 88 void Domain_Filter::diffy(const Mat &img, Mat &temp) 89 { 90 int channel = img.channels(); 91 92 for(int i = 0; i < img.size().height-1; i++) 93 for(int j = 0; j < img.size().width; j++) 94 { 95 for(int c =0; c < channel; c++) 96 { 97 temp.at<float>(i,j*channel+c) = 98 img.at<float>((i+1),j*channel+c) - img.at<float>(i,j*channel+c); 99 } 100 } 101 } 102 103 void Domain_Filter::getGradientx( const Mat &img, Mat &gx) 104 { 105 int w = img.cols; 106 int h = img.rows; 107 int channel = img.channels(); 108 109 for(int i=0;i<h;i++) 110 for(int j=0;j<w;j++) 111 for(int c=0;c<channel;++c) 112 { 113 gx.at<float>(i,j*channel+c) = 114 img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c); 115 } 116 } 117 118 void Domain_Filter::getGradienty( const Mat &img, Mat &gy) 119 { 120 int w = img.cols; 121 int h = img.rows; 122 int channel = img.channels(); 123 124 for(int i=0;i<h;i++) 125 for(int j=0;j<w;j++) 126 for(int c=0;c<channel;++c) 127 { 128 gy.at<float>(i,j*channel+c) = 129 img.at<float>(i+1,j*channel+c) - img.at<float>(i,j*channel+c); 130 131 } 132 } 133 134 void Domain_Filter::find_magnitude(Mat &img, Mat &mag) 135 { 136 int h = img.rows; 137 int w = img.cols; 138 139 vector <Mat> planes; 140 split(img, planes); 141 142 Mat magXR = Mat(h, w, CV_32FC1); 143 Mat magYR = Mat(h, w, CV_32FC1); 144 145 Mat magXG = Mat(h, w, CV_32FC1); 146 Mat magYG = Mat(h, w, CV_32FC1); 147 148 Mat magXB = Mat(h, w, CV_32FC1); 149 Mat magYB = Mat(h, w, CV_32FC1); 150 151 Sobel(planes[0], magXR, CV_32FC1, 1, 0, 3); 152 Sobel(planes[0], magYR, CV_32FC1, 0, 1, 3); 153 154 Sobel(planes[1], magXG, CV_32FC1, 1, 0, 3); 155 Sobel(planes[1], magYG, CV_32FC1, 0, 1, 3); 156 157 Sobel(planes[2], magXB, CV_32FC1, 1, 0, 3); 158 Sobel(planes[2], magYB, CV_32FC1, 0, 1, 3); 159 160 Mat mag1 = Mat(h,w,CV_32FC1); 161 Mat mag2 = Mat(h,w,CV_32FC1); 162 Mat mag3 = Mat(h,w,CV_32FC1); 163 164 magnitude(magXR,magYR,mag1); 165 magnitude(magXG,magYG,mag2); 166 magnitude(magXB,magYB,mag3); 167 168 mag = mag1 + mag2 + mag3; 169 mag = 1.0f - mag; 170 } 171 172 void Domain_Filter::compute_Rfilter(Mat &output, Mat &hz, float sigma_h) 173 { 174 int h = output.rows; 175 int w = output.cols; 176 int channel = output.channels(); 177 178 float a = (float) exp((-1.0 * sqrt(2.0)) / sigma_h); 179 180 Mat temp = Mat(h,w,CV_32FC3); 181 182 output.copyTo(temp); 183 Mat V = Mat(h,w,CV_32FC1); 184 185 for(int i=0;i<h;i++) 186 for(int j=0;j<w;j++) 187 V.at<float>(i,j) = pow(a,hz.at<float>(i,j)); 188 189 for(int i=0; i<h; i++) 190 { 191 for(int j =1; j < w; j++) 192 { 193 for(int c = 0; c<channel; c++) 194 { 195 temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) + 196 (temp.at<float>(i,(j-1)*channel+c) - temp.at<float>(i,j*channel+c)) * V.at<float>(i,j); 197 } 198 } 199 } 200 201 for(int i=0; i<h; i++) 202 { 203 for(int j =w-2; j >= 0; j--) 204 { 205 for(int c = 0; c<channel; c++) 206 { 207 temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) + 208 (temp.at<float>(i,(j+1)*channel+c) - temp.at<float>(i,j*channel+c))*V.at<float>(i,j+1); 209 } 210 } 211 } 212 213 temp.copyTo(output); 214 } 215 216 void Domain_Filter::compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius) 217 { 218 int h = output.rows; 219 int w = output.cols; 220 Mat lower_pos = Mat(h,w,CV_32FC1); 221 Mat upper_pos = Mat(h,w,CV_32FC1); 222 223 lower_pos = hz - radius; 224 upper_pos = hz + radius; 225 226 lower_idx = Mat::zeros(h,w,CV_32FC1); 227 upper_idx = Mat::zeros(h,w,CV_32FC1); 228 229 Mat domain_row = Mat::zeros(1,w+1,CV_32FC1); 230 231 for(int i=0;i<h;i++) 232 { 233 for(int j=0;j<w;j++) 234 domain_row.at<float>(0,j) = hz.at<float>(i,j); 235 domain_row.at<float>(0,w) = (float) myinf; 236 237 Mat lower_pos_row = Mat::zeros(1,w,CV_32FC1); 238 Mat upper_pos_row = Mat::zeros(1,w,CV_32FC1); 239 240 for(int j=0;j<w;j++) 241 { 242 lower_pos_row.at<float>(0,j) = lower_pos.at<float>(i,j); 243 upper_pos_row.at<float>(0,j) = upper_pos.at<float>(i,j); 244 } 245 246 Mat temp_lower_idx = Mat::zeros(1,w,CV_32FC1); 247 Mat temp_upper_idx = Mat::zeros(1,w,CV_32FC1); 248 249 for(int j=0;j<w;j++) 250 { 251 if(domain_row.at<float>(0,j) > lower_pos_row.at<float>(0,0)) 252 { 253 temp_lower_idx.at<float>(0,0) = (float) j; 254 break; 255 } 256 } 257 for(int j=0;j<w;j++) 258 { 259 if(domain_row.at<float>(0,j) > upper_pos_row.at<float>(0,0)) 260 { 261 temp_upper_idx.at<float>(0,0) = (float) j; 262 break; 263 } 264 } 265 266 int temp = 0; 267 for(int j=1;j<w;j++) 268 { 269 int count=0; 270 for(int k=(int) temp_lower_idx.at<float>(0,j-1);k<w+1;k++) 271 { 272 if(domain_row.at<float>(0,k) > lower_pos_row.at<float>(0,j)) 273 { 274 temp = count; 275 break; 276 } 277 count++; 278 } 279 280 temp_lower_idx.at<float>(0,j) = temp_lower_idx.at<float>(0,j-1) + temp; 281 282 count = 0; 283 for(int k=(int) temp_upper_idx.at<float>(0,j-1);k<w+1;k++) 284 { 285 286 287 if(domain_row.at<float>(0,k) > upper_pos_row.at<float>(0,j)) 288 { 289 temp = count; 290 break; 291 } 292 count++; 293 } 294 295 temp_upper_idx.at<float>(0,j) = temp_upper_idx.at<float>(0,j-1) + temp; 296 } 297 298 for(int j=0;j<w;j++) 299 { 300 lower_idx.at<float>(i,j) = temp_lower_idx.at<float>(0,j) + 1; 301 upper_idx.at<float>(i,j) = temp_upper_idx.at<float>(0,j) + 1; 302 } 303 304 } 305 psketch = upper_idx - lower_idx; 306 } 307 void Domain_Filter::compute_NCfilter(Mat &output, Mat &hz, Mat &psketch, float radius) 308 { 309 int h = output.rows; 310 int w = output.cols; 311 int channel = output.channels(); 312 313 compute_boxfilter(output,hz,psketch,radius); 314 315 Mat box_filter = Mat::zeros(h,w+1,CV_32FC3); 316 317 for(int i = 0; i < h; i++) 318 { 319 box_filter.at<float>(i,1*channel+0) = output.at<float>(i,0*channel+0); 320 box_filter.at<float>(i,1*channel+1) = output.at<float>(i,0*channel+1); 321 box_filter.at<float>(i,1*channel+2) = output.at<float>(i,0*channel+2); 322 for(int j = 2; j < w+1; j++) 323 { 324 for(int c=0;c<channel;c++) 325 box_filter.at<float>(i,j*channel+c) = output.at<float>(i,(j-1)*channel+c) + box_filter.at<float>(i,(j-1)*channel+c); 326 } 327 } 328 329 Mat indices = Mat::zeros(h,w,CV_32FC1); 330 Mat final = Mat::zeros(h,w,CV_32FC3); 331 332 for(int i=0;i<h;i++) 333 for(int j=0;j<w;j++) 334 indices.at<float>(i,j) = (float) i+1; 335 336 Mat a = Mat::zeros(h,w,CV_32FC1); 337 Mat b = Mat::zeros(h,w,CV_32FC1); 338 339 // Compute the box filter using a summed area table. 340 for(int c=0;c<channel;c++) 341 { 342 Mat flag = Mat::ones(h,w,CV_32FC1); 343 multiply(flag,c+1,flag); 344 345 Mat temp1, temp2; 346 multiply(flag - 1,h*(w+1),temp1); 347 multiply(lower_idx - 1,h,temp2); 348 a = temp1 + temp2 + indices; 349 350 multiply(flag - 1,h*(w+1),temp1); 351 multiply(upper_idx - 1,h,temp2); 352 b = temp1 + temp2 + indices; 353 354 int p,q,r,rem; 355 int p1,q1,r1,rem1; 356 357 // Calculating indices 358 for(int i=0;i<h;i++) 359 { 360 for(int j=0;j<w;j++) 361 { 362 363 r = (int) b.at<float>(i,j)/(h*(w+1)); 364 rem = (int) b.at<float>(i,j) - r*h*(w+1); 365 q = rem/h; 366 p = rem - q*h; 367 if(q==0) 368 { 369 p=h; 370 q=w; 371 r=r-1; 372 } 373 if(p==0) 374 { 375 p=h; 376 q=q-1; 377 } 378 379 r1 = (int) a.at<float>(i,j)/(h*(w+1)); 380 rem1 = (int) a.at<float>(i,j) - r1*h*(w+1); 381 q1 = rem1/h; 382 p1 = rem1 - q1*h; 383 if(p1==0) 384 { 385 p1=h; 386 q1=q1-1; 387 } 388 389 final.at<float>(i,j*channel+2-c) = (box_filter.at<float>(p-1,q*channel+(2-r)) - box_filter.at<float>(p1-1,q1*channel+(2-r1))) 390 /(upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j)); 391 } 392 } 393 } 394 395 final.copyTo(output); 396 } 397 void Domain_Filter::init(const Mat &img, int flags, float sigma_s, float sigma_r) 398 { 399 int h = img.size().height; 400 int w = img.size().width; 401 int channel = img.channels(); 402 403 //////////////////////////////////// horizontal and vertical partial derivatives ///////////////////////////////// 404 405 Mat derivx = Mat::zeros(h,w-1,CV_32FC3); 406 Mat derivy = Mat::zeros(h-1,w,CV_32FC3); 407 408 diffx(img,derivx); 409 diffy(img,derivy); 410 411 Mat distx = Mat::zeros(h,w,CV_32FC1); 412 Mat disty = Mat::zeros(h,w,CV_32FC1); 413 414 //////////////////////// Compute the l1-norm distance of neighbor pixels //////////////////////////////////////////////// 415 416 for(int i = 0; i < h; i++) 417 for(int j = 0,k=1; j < w-1; j++,k++) 418 for(int c = 0; c < channel; c++) 419 { 420 distx.at<float>(i,k) = 421 distx.at<float>(i,k) + abs(derivx.at<float>(i,j*channel+c)); 422 } 423 424 for(int i = 0,k=1; i < h-1; i++,k++) 425 for(int j = 0; j < w; j++) 426 for(int c = 0; c < channel; c++) 427 { 428 disty.at<float>(k,j) = 429 disty.at<float>(k,j) + abs(derivy.at<float>(i,j*channel+c)); 430 } 431 432 ////////////////////// Compute the derivatives of the horizontal and vertical domain transforms. ///////////////////////////// 433 434 horiz = Mat(h,w,CV_32FC1); 435 vert = Mat(h,w,CV_32FC1); 436 437 Mat final = Mat(h,w,CV_32FC3); 438 439 Mat tempx,tempy; 440 multiply(distx,sigma_s/sigma_r,tempx); 441 multiply(disty,sigma_s/sigma_r,tempy); 442 443 horiz = 1.0f + tempx; 444 vert = 1.0f + tempy; 445 446 O = Mat(h,w,CV_32FC3); 447 img.copyTo(O); 448 449 O_t = Mat(w,h,CV_32FC3); 450 451 if(flags == 2) 452 { 453 454 ct_H = Mat(h,w,CV_32FC1); 455 ct_V = Mat(h,w,CV_32FC1); 456 457 for(int i = 0; i < h; i++) 458 { 459 ct_H.at<float>(i,0) = horiz.at<float>(i,0); 460 for(int j = 1; j < w; j++) 461 { 462 ct_H.at<float>(i,j) = horiz.at<float>(i,j) + ct_H.at<float>(i,j-1); 463 } 464 } 465 466 for(int j = 0; j < w; j++) 467 { 468 ct_V.at<float>(0,j) = vert.at<float>(0,j); 469 for(int i = 1; i < h; i++) 470 { 471 ct_V.at<float>(i,j) = vert.at<float>(i,j) + ct_V.at<float>(i-1,j); 472 } 473 } 474 } 475 476 } 477 478 void Domain_Filter::filter(const Mat &img, Mat &res, float sigma_s = 60, float sigma_r = 0.4, int flags = 1) 479 { 480 int no_of_iter = 3; 481 int h = img.size().height; 482 int w = img.size().width; 483 float sigma_h = sigma_s; 484 485 init(img,flags,sigma_s,sigma_r); 486 487 if(flags == 1) 488 { 489 Mat vert_t = vert.t(); 490 491 for(int i=0;i<no_of_iter;i++) 492 { 493 sigma_h = (float) (sigma_s * sqrt(3.0) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1)); 494 495 compute_Rfilter(O, horiz, sigma_h); 496 497 O_t = O.t(); 498 499 compute_Rfilter(O_t, vert_t, sigma_h); 500 501 O = O_t.t(); 502 503 } 504 } 505 else if(flags == 2) 506 { 507 508 Mat vert_t = ct_V.t(); 509 Mat temp = Mat(h,w,CV_32FC1); 510 Mat temp1 = Mat(w,h,CV_32FC1); 511 512 float radius; 513 514 for(int i=0;i<no_of_iter;i++) 515 { 516 sigma_h = (float) (sigma_s * sqrt(3.0) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1)); 517 518 radius = (float) sqrt(3.0) * sigma_h; 519 520 compute_NCfilter(O, ct_H, temp,radius); 521 522 O_t = O.t(); 523 524 compute_NCfilter(O_t, vert_t, temp1, radius); 525 526 O = O_t.t(); 527 } 528 } 529 530 res = O.clone(); 531 } 532 533 void Domain_Filter::pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor) 534 { 535 536 int no_of_iter = 3; 537 init(img,2,sigma_s,sigma_r); 538 int h = img.size().height; 539 int w = img.size().width; 540 541 /////////////////////// convert to YCBCR model for color pencil drawing ////////////////////////////////////////////////////// 542 543 Mat color_sketch = Mat(h,w,CV_32FC3); 544 545 cvtColor(img,color_sketch,COLOR_BGR2YCrCb); 546 547 vector <Mat> YUV_channel; 548 Mat vert_t = ct_V.t(); 549 550 float sigma_h = sigma_s; 551 552 Mat penx = Mat(h,w,CV_32FC1); 553 554 Mat pen_res = Mat::zeros(h,w,CV_32FC1); 555 Mat peny = Mat(w,h,CV_32FC1); 556 557 Mat peny_t; 558 559 float radius; 560 561 for(int i=0;i<no_of_iter;i++) 562 { 563 sigma_h = (float) (sigma_s * sqrt(3.0) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1)); 564 565 radius = (float) sqrt(3.0) * sigma_h; 566 567 compute_boxfilter(O, ct_H, penx, radius); 568 569 O_t = O.t(); 570 571 compute_boxfilter(O_t, vert_t, peny, radius); 572 573 O = O_t.t(); 574 575 peny_t = peny.t(); 576 577 for(int k=0;k<h;k++) 578 for(int j=0;j<w;j++) 579 pen_res.at<float>(k,j) = (shade_factor * (penx.at<float>(k,j) + peny_t.at<float>(k,j))); 580 581 if(i==0) 582 { 583 sketch = pen_res.clone(); 584 split(color_sketch,YUV_channel); 585 pen_res.copyTo(YUV_channel[0]); 586 merge(YUV_channel,color_sketch); 587 cvtColor(color_sketch,color_res,COLOR_YCrCb2BGR); 588 } 589 590 } 591 } 592