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 
diffx(const Mat & img,Mat & temp)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 
diffy(const Mat & img,Mat & temp)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 
getGradientx(const Mat & img,Mat & gx)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 
getGradienty(const Mat & img,Mat & gy)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 
find_magnitude(Mat & img,Mat & mag)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 
compute_Rfilter(Mat & output,Mat & hz,float sigma_h)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 
compute_boxfilter(Mat & output,Mat & hz,Mat & psketch,float radius)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 }
compute_NCfilter(Mat & output,Mat & hz,Mat & psketch,float radius)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 }
init(const Mat & img,int flags,float sigma_s,float sigma_r)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 
filter(const Mat & img,Mat & res,float sigma_s=60,float sigma_r=0.4,int flags=1)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 
pencil_sketch(const Mat & img,Mat & sketch,Mat & color_res,float sigma_s,float sigma_r,float shade_factor)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