From 56ab1c18e651bfb466c644437e37a6c8b4f41809 Mon Sep 17 00:00:00 2001 From: siddharth <siddharthkherada27@gmail.com> Date: Tue, 3 Sep 2013 21:12:05 +0530 Subject: [PATCH] new filters added and all the 3 modules updated All 3 modules Updated --- modules/photo/doc/cloning.rst | 27 +- modules/photo/doc/npr.rst | 90 +++ modules/photo/include/opencv2/photo.hpp | 34 +- modules/photo/src/contrast_preserve.cpp | 274 ++++--- modules/photo/src/contrast_preserve.hpp | 30 +- modules/photo/src/npr.cpp | 236 +++++- modules/photo/src/npr.hpp | 953 ++++++++++++++---------- modules/photo/src/seamless_cloning.cpp | 58 +- modules/photo/src/seamless_cloning.hpp | 30 +- modules/photo/test/test_cloning.cpp | 4 +- modules/photo/test/test_npr.cpp | 146 ++++ samples/cpp/cloning.cpp | 42 +- samples/cpp/cloning_example.cpp | 243 ++++++ samples/cpp/npr_demo.cpp | 107 +++ 14 files changed, 1652 insertions(+), 622 deletions(-) create mode 100644 modules/photo/doc/npr.rst create mode 100644 modules/photo/test/test_npr.cpp create mode 100644 samples/cpp/cloning_example.cpp create mode 100644 samples/cpp/npr_demo.cpp diff --git a/modules/photo/doc/cloning.rst b/modules/photo/doc/cloning.rst index 740e1ab64b..11e9bce135 100644 --- a/modules/photo/doc/cloning.rst +++ b/modules/photo/doc/cloning.rst @@ -39,19 +39,21 @@ colorChange ----------- Given an original color image, two differently colored versions of this image can be mixed seamlessly. -.. ocv:function:: void colorChange( InputArray src, OutputArray dst, float red = 1.0, float green = 1.0, float blue = 1.0) +.. ocv:function:: void colorChange( InputArray src, InputArray mask, OutputArray dst, float red_mul = 1.0, float green_mul = 1.0, float blue_mul = 1.0) :param src: Input 8-bit 3-channel image. + :param mask: Input 8-bit 1 or 3-channel image. + :param dst: Output image with the same size and type as ``src`` . - :param red: R-channel Value + :param red_mul: R-channel multiply factor. - :param green: G-channel Value + :param green_mul: G-channel multiply factor. - :param blue: B-channel Value + :param blue_mul: B-channel multiply factor. -RGB values between .5 to 2.5 +Multiplication factor is between .5 to 2.5. illuminationChange @@ -59,10 +61,12 @@ illuminationChange Applying an appropriate non-linear transformation to the gradient field inside the selection and then integrating back with a Poisson solver, modifies locally the apparent illumination of an image. -.. ocv:function:: void illuminationChange(InputArray src, OutputArray dst, float alpha = 0.2, float beta = 0.4) +.. ocv:function:: void illuminationChange(InputArray src, InputArray mask, OutputArray dst, float alpha = 0.2, float beta = 0.4) :param src: Input 8-bit 3-channel image. + :param mask: Input 8-bit 1 or 3-channel image. + :param dst: Output image with the same size and type as ``src``. :param alpha: Value ranges between 0-2. @@ -74,14 +78,21 @@ This is useful to highlight under-exposed foreground objects or to reduce specul textureFlattening ----------------- By retaining only the gradients at edge locations, before integrating with the Poisson solver, one washes out the texture of the selected -region, giving its contents a flat aspect. +region, giving its contents a flat aspect. Here Canny Edge Detector is used. -.. ocv:function:: void textureFlattening(InputArray src, OutputArray dst) +.. ocv:function:: void textureFlattening(InputArray src, InputArray mask, OutputArray dst, double low_threshold, double high_threshold, int kernel_size) :param src: Input 8-bit 3-channel image. + :param mask: Input 8-bit 1 or 3-channel image. + :param dst: Output image with the same size and type as ``src``. + :param low_threshold: Range from 0 to 100. + + :param high_threshold: Value > 100. + + :param kernel_size: The size of the Sobel kernel to be used. **NOTE:** diff --git a/modules/photo/doc/npr.rst b/modules/photo/doc/npr.rst new file mode 100644 index 0000000000..db56c6955c --- /dev/null +++ b/modules/photo/doc/npr.rst @@ -0,0 +1,90 @@ +Non-Photorealistic Rendering +============================ + +.. highlight:: cpp + +edgePreservingFilter +-------------------- + +Filtering is the fundamental operation in image and video processing. Edge-preserving smoothing filters are used in many different applications. + +.. ocv:function:: void edgePreservingFilter(InputArray src, OutputArray dst, int flags = 1, float sigma_s = 60, float sigma_r = 0.4); + + :param src: Input 8-bit 3-channel image. + + :param dst: Output 8-bit 3-channel image. + + :param flags: Edge preserving filters: + + * **RECURS_FILTER** + + * **NORMCONV_FILTER** + + :param sigma_s: Range between 0 to 200. + + :param sigma_r: Range between 0 to 1. + + +detailEnhance +------------- +This filter enhances the details of a particular image. + +.. ocv:function:: void detailEnhance(InputArray src, OutputArray dst, float sigma_s = 10, float sigma_r = 0.15); + + :param src: Input 8-bit 3-channel image. + + :param dst: Output image with the same size and type as ``src``. + + :param sigma_s: Range between 0 to 200. + + :param sigma_r: Range between 0 to 1. + + +pencilSketch +------------ +Pencil-like non-photorealistic line drawing + +.. ocv:function:: void pencilSketch(InputArray src, OutputArray dst1, OutputArray dst2, float sigma_s = 60, float sigma_r = 0.07, float shade_factor = 0.02); + + :param src: Input 8-bit 3-channel image. + + :param dst1: Output 8-bit 1-channel image. + + :param dst2: Output image with the same size and type as ``src``. + + :param sigma_s: Range between 0 to 200. + + :param sigma_r: Range between 0 to 1. + + :param shade_factor: Range between 0 to 0.1. + + +stylization +----------- +Stylization aims to produce digital imagery with a wide variety of effects not focused on photorealism. Edge-aware filters are ideal for stylization, as they can abstract regions of low contrast while preserving, or enhancing, high-contrast features. + +.. ocv:function:: void stylization(InputArray src, OutputArray dst, float sigma_s = 60, float sigma_r = 0.45); + + :param src: Input 8-bit 3-channel image. + + :param dst: Output image with the same size and type as ``src``. + + :param sigma_s: Range between 0 to 200. + + :param sigma_r: Range between 0 to 1. + + +edgeEnhance +----------- +Able to suppress low-amplitude details and enhance edges. + +.. ocv:function:: void edgeEnhance(InputArray src, OutputArray dst, float sigma_s = 60, float sigma_r = 0.45); + + :param src: Input 8-bit 3-channel image. + + :param dst: Output 8-bit 1-channel image. + + :param sigma_s: Range between 0 to 200. + + :param sigma_r: Range between 0 to 1. + diff --git a/modules/photo/include/opencv2/photo.hpp b/modules/photo/include/opencv2/photo.hpp index d825c2489e..1eb5ccaab1 100644 --- a/modules/photo/include/opencv2/photo.hpp +++ b/modules/photo/include/opencv2/photo.hpp @@ -68,8 +68,8 @@ enum enum { - RECURSIVE_FILTER = 1, - NC_FILTER = 2 + RECURS_FILTER = 1, + NORMCONV_FILTER = 2 }; //! restores the damaged image areas using one of the available intpainting algorithms @@ -301,17 +301,35 @@ public: CV_EXPORTS_W Ptr<MergeRobertson> createMergeRobertson(); -CV_EXPORTS_W void decolor(InputArray src, OutputArray grayscale, OutputArray color_boost); +CV_EXPORTS_W void decolor( InputArray src, OutputArray grayscale, OutputArray color_boost); -CV_EXPORTS_W void seamlessClone(InputArray src, InputArray dst, InputArray mask, Point p, OutputArray _blend, int flags); +CV_EXPORTS_W void seamlessClone( InputArray src, InputArray dst, InputArray mask, Point p, + OutputArray _blend, int flags); -CV_EXPORTS_W void colorChange(InputArray src, InputArray mask, OutputArray dst, float red = 1.0, float green = 1.0, float blue = 1.0); +CV_EXPORTS_W void colorChange(InputArray src, InputArray mask, OutputArray dst, float red_mul = 1.0, + float green_mul = 1.0, float blue_mul = 1.0); -CV_EXPORTS_W void illuminationChange(InputArray src, InputArray mask, OutputArray dst, float alpha = 0.2, float beta = 0.4); +CV_EXPORTS_W void illuminationChange(InputArray src, InputArray mask, OutputArray dst, + float alpha = 0.2, float beta = 0.4); -CV_EXPORTS_W void textureFlattening(InputArray src, InputArray mask, OutputArray dst); +CV_EXPORTS_W void textureFlattening(InputArray src, InputArray mask, OutputArray dst, + double low_threshold, double high_threshold, + int kernel_size); -CV_EXPORTS_W void edgepreservefilter(InputArray _src, OutputArray _dst, int flags = 1, float sigma_h = 60, float sigma_r = 0.4); +CV_EXPORTS_W void edgePreservingFilter(InputArray src, OutputArray dst, int flags = 1, + float sigma_s = 60, float sigma_r = 0.4); + +CV_EXPORTS_W void detailEnhance(InputArray src, OutputArray dst, float sigma_s = 10, + float sigma_r = 0.15); + +CV_EXPORTS_W void pencilSketch(InputArray src, OutputArray dst, OutputArray dst1, + float sigma_s = 60, float sigma_r = 0.07, float shade_factor = 0.02); + +CV_EXPORTS_W void stylization(InputArray src, OutputArray dst, float sigma_s = 60, + float sigma_r = 0.45); + +CV_EXPORTS_W void edgeEnhance(InputArray src, OutputArray dst, float sigma_s = 60, + float sigma_r = 0.45); } // cv diff --git a/modules/photo/src/contrast_preserve.cpp b/modules/photo/src/contrast_preserve.cpp index 9a659691a8..14ed6ac30f 100644 --- a/modules/photo/src/contrast_preserve.cpp +++ b/modules/photo/src/contrast_preserve.cpp @@ -57,7 +57,7 @@ double norm(double); double norm(double E) { - return (sqrt(pow(E,2))); + return (sqrt(pow(E,2))); } void cv::decolor(InputArray _src, OutputArray _dst, OutputArray _boost) @@ -71,170 +71,168 @@ void cv::decolor(InputArray _src, OutputArray _dst, OutputArray _boost) if(!I.data ) { - cout << "Could not open or find the image" << endl ; - return; - } - if(I.channels() !=3) - { - cout << "Input Color Image" << endl; - return; - } - - int maxIter = 15; - int iterCount = 0; + cout << "Could not open or find the image" << endl ; + return; + } + if(I.channels() !=3) + { + cout << "Input Color Image" << endl; + return; + } + + int maxIter = 15; + int iterCount = 0; float tol = .0001; double E = 0; double pre_E = std::numeric_limits<double>::infinity(); - Decolor obj; + Decolor obj; - Mat img; + Mat img; img = Mat(I.size(),CV_32FC3); I.convertTo(img,CV_32FC3,1.0/255.0); obj.init(); - vector <double> Cg; - vector < vector <double> > polyGrad; - vector < vector <double> > bc; - vector < vector < int > > comb; + vector <double> Cg; + vector < vector <double> > polyGrad; + vector < vector <double> > bc; + vector < vector < int > > comb; - vector <double> alf; + vector <double> alf; - obj.grad_system(img,polyGrad,Cg,comb); - obj.weak_order(img,alf); + obj.grad_system(img,polyGrad,Cg,comb); + obj.weak_order(img,alf); - Mat Mt = Mat(polyGrad.size(),polyGrad[0].size(), CV_32FC1); - obj.wei_update_matrix(polyGrad,Cg,Mt); + Mat Mt = Mat(polyGrad.size(),polyGrad[0].size(), CV_32FC1); + obj.wei_update_matrix(polyGrad,Cg,Mt); - vector <double> wei; - obj.wei_inti(comb,wei); + vector <double> wei; + obj.wei_inti(comb,wei); - //////////////////////////////// main loop starting //////////////////////////////////////// + //////////////////////////////// main loop starting //////////////////////////////////////// - while(norm(E-pre_E) > tol) - { - iterCount +=1; + while(norm(E-pre_E) > tol) + { + iterCount +=1; pre_E = E; - vector <double> G_pos; - vector <double> G_neg; - - vector <double> temp; - vector <double> temp1; - - double val = 0.0; - for(unsigned int i=0;i< polyGrad[0].size();i++) - { - val = 0.0; - for(unsigned int j =0;j<polyGrad.size();j++) - val = val + (polyGrad[j][i] * wei[j]); - temp.push_back(val - Cg[i]); - temp1.push_back(val + Cg[i]); - } - - double ans = 0.0; - double ans1 = 0.0; - for(unsigned int i =0;i<alf.size();i++) - { - ans = ((1 + alf[i])/2) * exp((-1.0 * 0.5 * pow(temp[i],2))/pow(sigma,2)); - ans1 =((1 - alf[i])/2) * exp((-1.0 * 0.5 * pow(temp1[i],2))/pow(sigma,2)); - G_pos.push_back(ans); - G_neg.push_back(ans1); - } - - vector <double> EXPsum; - vector <double> EXPterm; - - for(unsigned int i = 0;i<G_pos.size();i++) - EXPsum.push_back(G_pos[i]+G_neg[i]); - - - vector <double> temp2; - - for(unsigned int i=0;i<EXPsum.size();i++) - { - if(EXPsum[i] == 0) - temp2.push_back(1.0); - else - temp2.push_back(0.0); - } - - for(unsigned int i =0; i < G_pos.size();i++) - EXPterm.push_back((G_pos[i] - G_neg[i])/(EXPsum[i] + temp2[i])); - - - double val1 = 0.0; - vector <double> wei1; - - for(unsigned int i=0;i< polyGrad.size();i++) - { - val1 = 0.0; - for(unsigned int j =0;j<polyGrad[0].size();j++) - { - val1 = val1 + (Mt.at<float>(i,j) * EXPterm[j]); - } - wei1.push_back(val1); - } - - for(unsigned int i =0;i<wei.size();i++) - wei[i] = wei1[i]; + vector <double> G_pos; + vector <double> G_neg; + + vector <double> temp; + vector <double> temp1; + + double val = 0.0; + for(unsigned int i=0;i< polyGrad[0].size();i++) + { + val = 0.0; + for(unsigned int j =0;j<polyGrad.size();j++) + val = val + (polyGrad[j][i] * wei[j]); + temp.push_back(val - Cg[i]); + temp1.push_back(val + Cg[i]); + } + + double ans = 0.0; + double ans1 = 0.0; + for(unsigned int i =0;i<alf.size();i++) + { + ans = ((1 + alf[i])/2) * exp((-1.0 * 0.5 * pow(temp[i],2))/pow(sigma,2)); + ans1 =((1 - alf[i])/2) * exp((-1.0 * 0.5 * pow(temp1[i],2))/pow(sigma,2)); + G_pos.push_back(ans); + G_neg.push_back(ans1); + } + + vector <double> EXPsum; + vector <double> EXPterm; + + for(unsigned int i = 0;i<G_pos.size();i++) + EXPsum.push_back(G_pos[i]+G_neg[i]); + + vector <double> temp2; + + for(unsigned int i=0;i<EXPsum.size();i++) + { + if(EXPsum[i] == 0) + temp2.push_back(1.0); + else + temp2.push_back(0.0); + } + + for(unsigned int i =0; i < G_pos.size();i++) + EXPterm.push_back((G_pos[i] - G_neg[i])/(EXPsum[i] + temp2[i])); + + double val1 = 0.0; + vector <double> wei1; + + for(unsigned int i=0;i< polyGrad.size();i++) + { + val1 = 0.0; + for(unsigned int j =0;j<polyGrad[0].size();j++) + { + val1 = val1 + (Mt.at<float>(i,j) * EXPterm[j]); + } + wei1.push_back(val1); + } + + for(unsigned int i =0;i<wei.size();i++) + wei[i] = wei1[i]; E = obj.energyCalcu(Cg,polyGrad,wei); if(iterCount > maxIter) break; - G_pos.clear(); - G_neg.clear(); - temp.clear(); - temp1.clear(); - EXPsum.clear(); - EXPterm.clear(); - temp2.clear(); - wei1.clear(); - } + G_pos.clear(); + G_neg.clear(); + temp.clear(); + temp1.clear(); + EXPsum.clear(); + EXPterm.clear(); + temp2.clear(); + wei1.clear(); + } - Mat Gray = Mat::zeros(img.size(),CV_32FC1); - obj.grayImContruct(wei, img, Gray); + Mat Gray = Mat::zeros(img.size(),CV_32FC1); + obj.grayImContruct(wei, img, Gray); - Gray.convertTo(dst,CV_8UC1,255); + Gray.convertTo(dst,CV_8UC1,255); /////////////////////////////////// Contrast Boosting ///////////////////////////////// - - Mat lab = Mat(img.size(),CV_8UC3); - Mat color = Mat(img.size(),CV_8UC3); - Mat l = Mat(img.size(),CV_8UC1); - Mat a = Mat(img.size(),CV_8UC1); - Mat b = Mat(img.size(),CV_8UC1); - - cvtColor(I,lab,COLOR_BGR2Lab); - - int h1 = img.size().height; - int w1 = img.size().width; - - for(int i =0;i<h1;i++) - for(int j=0;j<w1;j++) - { - l.at<uchar>(i,j) = lab.at<uchar>(i,j*3+0); - a.at<uchar>(i,j) = lab.at<uchar>(i,j*3+1); - b.at<uchar>(i,j) = lab.at<uchar>(i,j*3+2); - } - - for(int i =0;i<h1;i++) - for(int j=0;j<w1;j++) - { - l.at<uchar>(i,j) = 255.0*Gray.at<float>(i,j); - } - - for(int i =0;i<h1;i++) - for(int j=0;j<w1;j++) - { - lab.at<uchar>(i,j*3+0) = l.at<uchar>(i,j); - lab.at<uchar>(i,j*3+1) = a.at<uchar>(i,j); - lab.at<uchar>(i,j*3+2) = b.at<uchar>(i,j); - } - - cvtColor(lab,color_boost,COLOR_Lab2BGR); + + Mat lab = Mat(img.size(),CV_8UC3); + Mat color = Mat(img.size(),CV_8UC3); + Mat l = Mat(img.size(),CV_8UC1); + Mat a = Mat(img.size(),CV_8UC1); + Mat b = Mat(img.size(),CV_8UC1); + + cvtColor(I,lab,COLOR_BGR2Lab); + + int h1 = img.size().height; + int w1 = img.size().width; + + for(int i =0;i<h1;i++) + for(int j=0;j<w1;j++) + { + l.at<uchar>(i,j) = lab.at<uchar>(i,j*3+0); + a.at<uchar>(i,j) = lab.at<uchar>(i,j*3+1); + b.at<uchar>(i,j) = lab.at<uchar>(i,j*3+2); + } + + for(int i =0;i<h1;i++) + for(int j=0;j<w1;j++) + { + l.at<uchar>(i,j) = 255.0*Gray.at<float>(i,j); + } + + for(int i =0;i<h1;i++) + for(int j=0;j<w1;j++) + { + lab.at<uchar>(i,j*3+0) = l.at<uchar>(i,j); + lab.at<uchar>(i,j*3+1) = a.at<uchar>(i,j); + lab.at<uchar>(i,j*3+2) = b.at<uchar>(i,j); + } + + cvtColor(lab,color_boost,COLOR_Lab2BGR); } diff --git a/modules/photo/src/contrast_preserve.hpp b/modules/photo/src/contrast_preserve.hpp index 607b494832..c9d75036c2 100644 --- a/modules/photo/src/contrast_preserve.hpp +++ b/modules/photo/src/contrast_preserve.hpp @@ -39,7 +39,6 @@ // //M*/ - #include "precomp.hpp" #include "opencv2/photo.hpp" #include "opencv2/imgproc.hpp" @@ -69,7 +68,7 @@ class Decolor void add_to_vector_poly(vector < vector <double> > &polyGrad, vector <double> &curGrad); void weak_order(Mat img, vector <double> &alf); void grad_system(Mat img, vector < vector < double > > &polyGrad, - vector < double > &Cg, vector < vector <int> >& comb); + vector < double > &Cg, vector < vector <int> >& comb); void wei_update_matrix(vector < vector <double> > &poly, vector <double> &Cg, Mat &X); void wei_inti(vector < vector <int> > &comb, vector <double> &wei); void grayImContruct(vector <double> &wei, Mat img, Mat &Gray); @@ -87,7 +86,6 @@ float sigma = .02; double Decolor::energyCalcu(vector <double> &Cg, vector < vector <double> > &polyGrad, vector <double> &wei) { vector <double> P; - vector <double> temp; vector <double> temp1; @@ -112,8 +110,6 @@ double Decolor::energyCalcu(vector <double> &Cg, vector < vector <double> > &pol } - - void Decolor::init() { kernel = Mat(1,2, CV_32FC1); @@ -123,7 +119,6 @@ void Decolor::init() kernel1.at<float>(0,0)=1.0; kernel1.at<float>(1,0)=-1.0; order = 2; - } vector<double> Decolor::product(vector < vector<int> > &comb, vector <double> &initRGB) @@ -183,7 +178,7 @@ void Decolor::gradvector(const Mat &img, vector <double> &grad) dest.release(); dest1.release(); } - + void Decolor::colorGrad(Mat img, vector <double> &Cg) { @@ -222,7 +217,6 @@ void Decolor::colorGrad(Mat img, vector <double> &Cg) ImL.clear(); Ima.clear(); Imb.clear(); - } void Decolor::add_vector(vector < vector <int> > &comb, int r,int g,int b) @@ -267,7 +261,7 @@ void Decolor::weak_order(Mat img, vector <double> &alf) green.at<float>(i,j) = img.at<float>(i,j*3+1); blue.at<float>(i,j) = img.at<float>(i,j*3+0); } - + vector <double> Rg; vector <double> Gg; vector <double> Bg; @@ -275,7 +269,7 @@ void Decolor::weak_order(Mat img, vector <double> &alf) vector <double> t1; vector <double> t2; vector <double> t3; - + vector <double> tmp1; vector <double> tmp2; vector <double> tmp3; @@ -333,7 +327,7 @@ void Decolor::weak_order(Mat img, vector <double> &alf) for(unsigned int i =0 ;i < Rg.size();i++) alf[i] -= tmp1[i] * tmp2[i] * tmp3[i]; - + double sum =0.0; for(unsigned int i=0;i<alf.size();i++) sum += abs(alf[i]); @@ -359,12 +353,11 @@ void Decolor::weak_order(Mat img, vector <double> &alf) } void Decolor::grad_system(Mat img, vector < vector < double > > &polyGrad, - vector < double > &Cg, vector < vector <int> >& comb) + vector < double > &Cg, vector < vector <int> >& comb) { int h = img.size().height; int w = img.size().width; - double sizefactor; if((h + w) > 800) { @@ -375,7 +368,7 @@ void Decolor::grad_system(Mat img, vector < vector < double > > &polyGrad, h = img.size().height; w = img.size().width; colorGrad(img,Cg); - + Mat curIm = Mat(img.size(),CV_32FC1); Mat red = Mat(img.size(),CV_32FC1); Mat green = Mat(img.size(),CV_32FC1); @@ -400,7 +393,7 @@ void Decolor::grad_system(Mat img, vector < vector < double > > &polyGrad, for(int j=0;j<w;j++) curIm.at<float>(i,j)= pow(red.at<float>(i,j),r)*pow(green.at<float>(i,j),g)* - pow(blue.at<float>(i,j),b); + pow(blue.at<float>(i,j),b); vector <double> curGrad; gradvector(curIm,curGrad); add_to_vector_poly(polyGrad,curGrad); @@ -468,7 +461,6 @@ void Decolor::wei_inti(vector < vector <int> > &comb, vector <double> &wei) void Decolor::grayImContruct(vector <double> &wei, Mat img, Mat &Gray) { - int h=img.size().height; int w=img.size().width; @@ -485,7 +477,7 @@ void Decolor::grayImContruct(vector <double> &wei, Mat img, Mat &Gray) } int kk =0; - + for(int r =0;r<=order;r++) for(int g=0;g<=order;g++) for(int b=0;b<=order;b++) @@ -495,7 +487,7 @@ void Decolor::grayImContruct(vector <double> &wei, Mat img, Mat &Gray) for(int j=0;j<w;j++) Gray.at<float>(i,j)=Gray.at<float>(i,j) + wei[kk]*pow(red.at<float>(i,j),r)*pow(green.at<float>(i,j),g)* - pow(blue.at<float>(i,j),b); + pow(blue.at<float>(i,j),b); kk=kk+1; } @@ -508,7 +500,7 @@ void Decolor::grayImContruct(vector <double> &wei, Mat img, Mat &Gray) { if(Gray.at<float>(i,j) < minval) minval = Gray.at<float>(i,j); - + if(Gray.at<float>(i,j) > maxval) maxval = Gray.at<float>(i,j); } diff --git a/modules/photo/src/npr.cpp b/modules/photo/src/npr.cpp index c2311c8122..54f5ce55ed 100644 --- a/modules/photo/src/npr.cpp +++ b/modules/photo/src/npr.cpp @@ -1,3 +1,44 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + #include "precomp.hpp" #include "opencv2/photo.hpp" #include "opencv2/highgui.hpp" @@ -11,7 +52,7 @@ using namespace std; using namespace cv; -void cv::edgepreservefilter(InputArray _src, OutputArray _dst, int flags, float sigma_s, float sigma_r) +void cv::edgePreservingFilter(InputArray _src, OutputArray _dst, int flags, float sigma_s, float sigma_r) { Mat I = _src.getMat(); _dst.create(I.size(), CV_8UC3); @@ -32,3 +73,196 @@ void cv::edgepreservefilter(InputArray _src, OutputArray _dst, int flags, float convertScaleAbs(res, dst, 255,0); } + +void cv::detailEnhance(InputArray _src, OutputArray _dst, float sigma_s, float sigma_r) +{ + Mat I = _src.getMat(); + _dst.create(I.size(), CV_8UC3); + Mat dst = _dst.getMat(); + + int h = I.size().height; + int w = I.size().width; + int channel = I.channels(); + float factor = 3.0; + + Mat img = Mat(I.size(),CV_32FC3); + I.convertTo(img,CV_32FC3,1.0/255.0); + + Mat res = Mat(h,w,CV_32FC3); + dst.convertTo(res,CV_32FC3,1.0/255.0); + + Mat result = Mat(img.size(),CV_32FC3); + Mat lab = Mat(img.size(),CV_32FC3); + Mat l_channel = Mat(img.size(),CV_32FC1); + Mat a_channel = Mat(img.size(),CV_32FC1); + Mat b_channel = Mat(img.size(),CV_32FC1); + + cvtColor(img,lab,COLOR_BGR2Lab); + + for(int i = 0; i < h; i++) + for(int j = 0; j < w; j++) + { + l_channel.at<float>(i,j) = lab.at<float>(i,j*channel+0); + a_channel.at<float>(i,j) = lab.at<float>(i,j*channel+1); + b_channel.at<float>(i,j) = lab.at<float>(i,j*channel+2); + } + + Mat L = Mat(img.size(),CV_32FC1); + + l_channel.convertTo(L,CV_32FC1,1.0/255.0); + + Domain_Filter obj; + + obj.filter(L, res, sigma_s, sigma_r, 1); + + Mat detail = Mat(h,w,CV_32FC1); + + for(int i = 0; i < h; i++) + for(int j = 0; j < w; j++) + detail.at<float>(i,j) = L.at<float>(i,j) - res.at<float>(i,j); + + for(int i = 0; i < h; i++) + for(int j = 0; j < w; j++) + L.at<float>(i,j) = res.at<float>(i,j) + factor*detail.at<float>(i,j); + + L.convertTo(l_channel,CV_32FC1,255); + + for(int i = 0; i < h; i++) + for(int j = 0; j < w; j++) + { + lab.at<float>(i,j*channel+0) = l_channel.at<float>(i,j); + lab.at<float>(i,j*channel+1) = a_channel.at<float>(i,j); + lab.at<float>(i,j*channel+2) = b_channel.at<float>(i,j); + } + + cvtColor(lab,result,COLOR_Lab2BGR); + result.convertTo(dst,CV_8UC3,255); +} + +void cv::pencilSketch(InputArray _src, OutputArray _dst, OutputArray _dst1, float sigma_s, float sigma_r, float shade_factor) +{ + Mat I = _src.getMat(); + _dst.create(I.size(), CV_8UC1); + Mat dst = _dst.getMat(); + + _dst1.create(I.size(), CV_8UC3); + Mat dst1 = _dst1.getMat(); + + Mat img = Mat(I.size(),CV_32FC3); + I.convertTo(img,CV_32FC3,1.0/255.0); + + Domain_Filter obj; + + Mat sketch = Mat(I.size(),CV_32FC1); + Mat color_sketch = Mat(I.size(),CV_32FC3); + + obj.pencil_sketch(img, sketch, color_sketch, sigma_s, sigma_r, shade_factor); + + sketch.convertTo(dst,CV_8UC1,255); + color_sketch.convertTo(dst1,CV_8UC3,255); + +} + +void cv::stylization(InputArray _src, OutputArray _dst, float sigma_s, float sigma_r) +{ + Mat I = _src.getMat(); + _dst.create(I.size(), CV_8UC3); + Mat dst = _dst.getMat(); + + Mat img = Mat(I.size(),CV_32FC3); + I.convertTo(img,CV_32FC3,1.0/255.0); + + int h = img.size().height; + int w = img.size().width; + int channel = img.channels(); + + Mat res = Mat(h,w,CV_32FC3); + + Domain_Filter obj; + obj.filter(img, res, sigma_s, sigma_r, NORMCONV_FILTER); + + vector <Mat> planes; + split(res, planes); + + Mat magXR = Mat(h, w, CV_32FC1); + Mat magYR = Mat(h, w, CV_32FC1); + + Mat magXG = Mat(h, w, CV_32FC1); + Mat magYG = Mat(h, w, CV_32FC1); + + Mat magXB = Mat(h, w, CV_32FC1); + Mat magYB = Mat(h, w, CV_32FC1); + + Sobel(planes[0], magXR, CV_32FC1, 1, 0, 3); + Sobel(planes[0], magYR, CV_32FC1, 0, 1, 3); + + Sobel(planes[1], magXG, CV_32FC1, 1, 0, 3); + Sobel(planes[1], magYG, CV_32FC1, 0, 1, 3); + + Sobel(planes[2], magXB, CV_32FC1, 1, 0, 3); + Sobel(planes[2], magYB, CV_32FC1, 0, 1, 3); + + Mat magx = Mat(h,w,CV_32FC1); + Mat magy = Mat(h,w,CV_32FC1); + + Mat mag1 = Mat(h,w,CV_32FC1); + Mat mag2 = Mat(h,w,CV_32FC1); + Mat mag3 = Mat(h,w,CV_32FC1); + + magnitude(magXR,magYR,mag1); + magnitude(magXG,magYG,mag2); + magnitude(magXB,magYB,mag3); + + Mat magnitude = Mat(h,w,CV_32FC1); + + for(int i =0;i < h;i++) + for(int j=0;j<w;j++) + { + magnitude.at<float>(i,j) = mag1.at<float>(i,j) + mag2.at<float>(i,j) + mag3.at<float>(i,j); + } + + for(int i =0;i < h;i++) + for(int j=0;j<w;j++) + { + magnitude.at<float>(i,j) = 1.0 - magnitude.at<float>(i,j); + } + + Mat stylized = Mat(h,w,CV_32FC3); + + for(int i =0;i < h;i++) + for(int j=0;j<w;j++) + for(int c=0;c<channel;c++) + { + stylized.at<float>(i,j*channel + c) = res.at<float>(i,j*channel + c) * magnitude.at<float>(i,j); + } + + stylized.convertTo(dst,CV_8UC3,255); +} + +void cv::edgeEnhance(InputArray _src, OutputArray _dst, float sigma_s, float sigma_r) +{ + Mat I = _src.getMat(); + _dst.create(I.size(), CV_8UC1); + Mat dst = _dst.getMat(); + + Mat img = Mat(I.size(),CV_32FC3); + I.convertTo(img,CV_32FC3,1.0/255.0); + + Mat orig = img.clone(); + + int h = img.size().height; + int w = img.size().width; + + Mat res = Mat(h,w,CV_32FC3); + Mat magnitude = Mat(h,w,CV_32FC1); + + Mat mag8 = Mat(h,w,CV_32FC1); + + Domain_Filter obj; + + obj.filter(img, res, sigma_s, sigma_r, NORMCONV_FILTER); + + obj.find_magnitude(res,magnitude); + + magnitude.convertTo(dst,CV_8UC1,255); +} diff --git a/modules/photo/src/npr.hpp b/modules/photo/src/npr.hpp index 856a4768b6..66638ccbd8 100644 --- a/modules/photo/src/npr.hpp +++ b/modules/photo/src/npr.hpp @@ -1,3 +1,44 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + #include "precomp.hpp" #include "opencv2/photo.hpp" #include "opencv2/imgproc.hpp" @@ -13,478 +54,616 @@ double myinf = std::numeric_limits<double>::infinity(); class Domain_Filter { - public: - Mat ct_H, ct_V, horiz, vert, O, O_t, lower_idx, upper_idx; - void init(const Mat &img, int flags, float sigma_s, float sigma_r); - void getGradientx( const Mat &img, Mat &gx); - void getGradienty( const Mat &img, Mat &gy); - void diffx(const Mat &img, Mat &temp); - void diffy(const Mat &img, Mat &temp); - void compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius); - void compute_Rfilter(Mat &O, Mat &horiz, float sigma_h); - void compute_NCfilter(Mat &O, Mat &horiz, Mat &psketch, float radius); - void filter(const Mat &img, Mat &res, float sigma_s, float sigma_r, int flags); + public: + Mat ct_H, ct_V, horiz, vert, O, O_t, lower_idx, upper_idx; + void init(const Mat &img, int flags, float sigma_s, float sigma_r); + void getGradientx( const Mat &img, Mat &gx); + void getGradienty( const Mat &img, Mat &gy); + void diffx(const Mat &img, Mat &temp); + void diffy(const Mat &img, Mat &temp); + void find_magnitude(Mat &img, Mat &mag); + void compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius); + void compute_Rfilter(Mat &O, Mat &horiz, float sigma_h); + void compute_NCfilter(Mat &O, Mat &horiz, Mat &psketch, float radius); + void filter(const Mat &img, Mat &res, float sigma_s, float sigma_r, int flags); + void pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor); + void Depth_of_field(const Mat &img, Mat &img1, float sigma_s, float sigma_r); }; void Domain_Filter::diffx(const Mat &img, Mat &temp) { - int channel = img.channels(); - - for(int i = 0; i < img.size().height; i++) - for(int j = 0; j < img.size().width-1; j++) - { - for(int c =0; c < channel; c++) - { - temp.at<float>(i,j*channel+c) = - img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c); - } - } + int channel = img.channels(); + + for(int i = 0; i < img.size().height; i++) + for(int j = 0; j < img.size().width-1; j++) + { + for(int c =0; c < channel; c++) + { + temp.at<float>(i,j*channel+c) = + img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c); + } + } } void Domain_Filter::diffy(const Mat &img, Mat &temp) { - int channel = img.channels(); - - for(int i = 0; i < img.size().height-1; i++) - for(int j = 0; j < img.size().width; j++) - { - for(int c =0; c < channel; c++) - { - temp.at<float>(i,j*channel+c) = - img.at<float>((i+1),j*channel+c) - img.at<float>(i,j*channel+c); - } - } + int channel = img.channels(); + + for(int i = 0; i < img.size().height-1; i++) + for(int j = 0; j < img.size().width; j++) + { + for(int c =0; c < channel; c++) + { + temp.at<float>(i,j*channel+c) = + img.at<float>((i+1),j*channel+c) - img.at<float>(i,j*channel+c); + } + } } void Domain_Filter::getGradientx( const Mat &img, Mat &gx) { - int w = img.cols; - int h = img.rows; - int channel = img.channels(); - - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - for(int c=0;c<channel;++c) - { - gx.at<float>(i,j*channel+c) = - img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c); - } + int w = img.cols; + int h = img.rows; + int channel = img.channels(); + + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + for(int c=0;c<channel;++c) + { + gx.at<float>(i,j*channel+c) = + img.at<float>(i,(j+1)*channel+c) - img.at<float>(i,j*channel+c); + } } + void Domain_Filter::getGradienty( const Mat &img, Mat &gy) { - int w = img.cols; - int h = img.rows; - int channel = img.channels(); - - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - for(int c=0;c<channel;++c) - { - gy.at<float>(i,j*channel+c) = - img.at<float>(i+1,j*channel+c) - img.at<float>(i,j*channel+c); - - } + int w = img.cols; + int h = img.rows; + int channel = img.channels(); + + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + for(int c=0;c<channel;++c) + { + gy.at<float>(i,j*channel+c) = + img.at<float>(i+1,j*channel+c) - img.at<float>(i,j*channel+c); + + } } -void Domain_Filter::compute_Rfilter(Mat &output, Mat &hz, float sigma_h) +void Domain_Filter::find_magnitude(Mat &img, Mat &mag) { - float a; + int h = img.rows; + int w = img.cols; + + vector <Mat> planes; + split(img, planes); + + Mat magXR = Mat(h, w, CV_32FC1); + Mat magYR = Mat(h, w, CV_32FC1); + + Mat magXG = Mat(h, w, CV_32FC1); + Mat magYG = Mat(h, w, CV_32FC1); + + Mat magXB = Mat(h, w, CV_32FC1); + Mat magYB = Mat(h, w, CV_32FC1); + + getGradientx(planes[0], magXR); + getGradienty(planes[0], magYR); + + getGradientx(planes[1], magXG); + getGradienty(planes[1], magYG); + + getGradientx(planes[2], magXR); + getGradienty(planes[2], magYR); + + Mat magx = Mat(h,w,CV_32FC1); + Mat magy = Mat(h,w,CV_32FC1); + + Mat mag1 = Mat(h,w,CV_32FC1); + Mat mag2 = Mat(h,w,CV_32FC1); + Mat mag3 = Mat(h,w,CV_32FC1); + + magnitude(magXR,magYR,mag1); + magnitude(magXG,magYG,mag2); + magnitude(magXB,magYB,mag3); - int h = output.rows; - int w = output.cols; - int channel = output.channels(); + for(int i =0;i < h;i++) + for(int j=0;j<w;j++) + { + mag.at<float>(i,j) = mag1.at<float>(i,j) + mag2.at<float>(i,j) + mag3.at<float>(i,j); + } - a = exp(-sqrt(2) / sigma_h); - Mat temp = Mat(h,w,CV_32FC3); + for(int i =0;i < h;i++) + for(int j=0;j<w;j++) + { + mag.at<float>(i,j) = 1.0 - mag.at<float>(i,j); + } - for(int i =0; i < h;i++) - for(int j=0;j<w;j++) - for(int c=0;c<channel;c++) - temp.at<float>(i,j*channel+c) = output.at<float>(i,j*channel+c); +} + +void Domain_Filter::compute_Rfilter(Mat &output, Mat &hz, float sigma_h) +{ + + float a; + + int h = output.rows; + int w = output.cols; + int channel = output.channels(); + + a = exp(-sqrt(2) / sigma_h); + Mat temp = Mat(h,w,CV_32FC3); - Mat V = Mat(h,w,CV_32FC1); + for(int i =0; i < h;i++) + for(int j=0;j<w;j++) + for(int c=0;c<channel;c++) + temp.at<float>(i,j*channel+c) = output.at<float>(i,j*channel+c); - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - V.at<float>(i,j) = pow(a,hz.at<float>(i,j)); + Mat V = Mat(h,w,CV_32FC1); - for(int i=0; i<h; i++) - { - for(int j =1; j < w; j++) - { - for(int c = 0; c<channel; c++) - { - temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) + - (temp.at<float>(i,(j-1)*channel+c) - temp.at<float>(i,j*channel+c)) * V.at<float>(i,j); - } - } - } - - for(int i=0; i<h; i++) - { - for(int j =w-2; j >= 0; j--) - { - for(int c = 0; c<channel; c++) - { - temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) + - (temp.at<float>(i,(j+1)*channel+c) - temp.at<float>(i,j*channel+c))*V.at<float>(i,j+1); - } - } - } + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + V.at<float>(i,j) = pow(a,hz.at<float>(i,j)); - for(int i =0; i < h;i++) - for(int j=0;j<w;j++) - for(int c=0;c<channel;c++) - output.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c); + for(int i=0; i<h; i++) + { + for(int j =1; j < w; j++) + { + for(int c = 0; c<channel; c++) + { + temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) + + (temp.at<float>(i,(j-1)*channel+c) - temp.at<float>(i,j*channel+c)) * V.at<float>(i,j); + } + } + } - temp.release(); - V.release(); + for(int i=0; i<h; i++) + { + for(int j =w-2; j >= 0; j--) + { + for(int c = 0; c<channel; c++) + { + temp.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c) + + (temp.at<float>(i,(j+1)*channel+c) - temp.at<float>(i,j*channel+c))*V.at<float>(i,j+1); + } + } + } + for(int i =0; i < h;i++) + for(int j=0;j<w;j++) + for(int c=0;c<channel;c++) + output.at<float>(i,j*channel+c) = temp.at<float>(i,j*channel+c); + + temp.release(); + V.release(); } void Domain_Filter::compute_boxfilter(Mat &output, Mat &hz, Mat &psketch, float radius) { - int h = output.rows; - int w = output.cols; - Mat lower_pos = Mat(h,w,CV_32FC1); - Mat upper_pos = Mat(h,w,CV_32FC1); - - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - { - lower_pos.at<float>(i,j) = hz.at<float>(i,j) - radius; - upper_pos.at<float>(i,j) = hz.at<float>(i,j) + radius; - } - - lower_idx = Mat::zeros(h,w,CV_32FC1); - upper_idx = Mat::zeros(h,w,CV_32FC1); - - Mat domain_row = Mat::zeros(1,w+1,CV_32FC1); - - for(int i=0;i<h;i++) - { - for(int j=0;j<w;j++) - domain_row.at<float>(0,j) = hz.at<float>(i,j); - domain_row.at<float>(0,w) = myinf; - - Mat lower_pos_row = Mat::zeros(1,w,CV_32FC1); - Mat upper_pos_row = Mat::zeros(1,w,CV_32FC1); - - for(int j=0;j<w;j++) - { - lower_pos_row.at<float>(0,j) = lower_pos.at<float>(i,j); - upper_pos_row.at<float>(0,j) = upper_pos.at<float>(i,j); - } - - Mat temp_lower_idx = Mat::zeros(1,w,CV_32FC1); - Mat temp_upper_idx = Mat::zeros(1,w,CV_32FC1); - - for(int j=0;j<w;j++) - { - if(domain_row.at<float>(0,j) > lower_pos_row.at<float>(0,0)) - { - temp_lower_idx.at<float>(0,0) = j; - break; - } - } - for(int j=0;j<w;j++) - { - if(domain_row.at<float>(0,j) > upper_pos_row.at<float>(0,0)) - { - temp_upper_idx.at<float>(0,0) = j; - break; - } - } - - int temp = 0; - for(int j=1;j<w;j++) - { - int count=0; - for(int k=temp_lower_idx.at<float>(0,j-1);k<w+1;k++) - { - if(domain_row.at<float>(0,k) > lower_pos_row.at<float>(0,j)) - { - temp = count; - break; - } - count++; - } - - temp_lower_idx.at<float>(0,j) = temp_lower_idx.at<float>(0,j-1) + temp; - - count = 0; - for(int k=temp_upper_idx.at<float>(0,j-1);k<w+1;k++) - { - - - if(domain_row.at<float>(0,k) > upper_pos_row.at<float>(0,j)) - { - temp = count; - break; - } - count++; - } - - temp_upper_idx.at<float>(0,j) = temp_upper_idx.at<float>(0,j-1) + temp; - } - - for(int j=0;j<w;j++) - { - lower_idx.at<float>(i,j) = temp_lower_idx.at<float>(0,j) + 1; - upper_idx.at<float>(i,j) = temp_upper_idx.at<float>(0,j) + 1; - } - - - lower_pos_row.release(); - upper_pos_row.release(); - temp_lower_idx.release(); - temp_upper_idx.release(); - } - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - psketch.at<float>(i,j) = upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j); + int h = output.rows; + int w = output.cols; + Mat lower_pos = Mat(h,w,CV_32FC1); + Mat upper_pos = Mat(h,w,CV_32FC1); + + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + { + lower_pos.at<float>(i,j) = hz.at<float>(i,j) - radius; + upper_pos.at<float>(i,j) = hz.at<float>(i,j) + radius; + } + + lower_idx = Mat::zeros(h,w,CV_32FC1); + upper_idx = Mat::zeros(h,w,CV_32FC1); + + Mat domain_row = Mat::zeros(1,w+1,CV_32FC1); + + for(int i=0;i<h;i++) + { + for(int j=0;j<w;j++) + domain_row.at<float>(0,j) = hz.at<float>(i,j); + domain_row.at<float>(0,w) = myinf; + + Mat lower_pos_row = Mat::zeros(1,w,CV_32FC1); + Mat upper_pos_row = Mat::zeros(1,w,CV_32FC1); + + for(int j=0;j<w;j++) + { + lower_pos_row.at<float>(0,j) = lower_pos.at<float>(i,j); + upper_pos_row.at<float>(0,j) = upper_pos.at<float>(i,j); + } + + Mat temp_lower_idx = Mat::zeros(1,w,CV_32FC1); + Mat temp_upper_idx = Mat::zeros(1,w,CV_32FC1); + + for(int j=0;j<w;j++) + { + if(domain_row.at<float>(0,j) > lower_pos_row.at<float>(0,0)) + { + temp_lower_idx.at<float>(0,0) = j; + break; + } + } + for(int j=0;j<w;j++) + { + if(domain_row.at<float>(0,j) > upper_pos_row.at<float>(0,0)) + { + temp_upper_idx.at<float>(0,0) = j; + break; + } + } + + int temp = 0; + for(int j=1;j<w;j++) + { + int count=0; + for(int k=temp_lower_idx.at<float>(0,j-1);k<w+1;k++) + { + if(domain_row.at<float>(0,k) > lower_pos_row.at<float>(0,j)) + { + temp = count; + break; + } + count++; + } + + temp_lower_idx.at<float>(0,j) = temp_lower_idx.at<float>(0,j-1) + temp; + + count = 0; + for(int k=temp_upper_idx.at<float>(0,j-1);k<w+1;k++) + { + + + if(domain_row.at<float>(0,k) > upper_pos_row.at<float>(0,j)) + { + temp = count; + break; + } + count++; + } + + temp_upper_idx.at<float>(0,j) = temp_upper_idx.at<float>(0,j-1) + temp; + } + + for(int j=0;j<w;j++) + { + lower_idx.at<float>(i,j) = temp_lower_idx.at<float>(0,j) + 1; + upper_idx.at<float>(i,j) = temp_upper_idx.at<float>(0,j) + 1; + } + + lower_pos_row.release(); + upper_pos_row.release(); + temp_lower_idx.release(); + temp_upper_idx.release(); + } + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + psketch.at<float>(i,j) = upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j); } void Domain_Filter::compute_NCfilter(Mat &output, Mat &hz, Mat &psketch, float radius) { - int h = output.rows; - int w = output.cols; - int channel = output.channels(); - - compute_boxfilter(output,hz,psketch,radius); - - Mat box_filter = Mat::zeros(h,w+1,CV_32FC3); - - for(int i = 0; i < h; i++) - { - box_filter.at<float>(i,1*channel+0) = output.at<float>(i,0*channel+0); - box_filter.at<float>(i,1*channel+1) = output.at<float>(i,0*channel+1); - box_filter.at<float>(i,1*channel+2) = output.at<float>(i,0*channel+2); - for(int j = 2; j < w+1; j++) - { - for(int c=0;c<channel;c++) - 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); - } - } - - Mat indices = Mat::zeros(h,w,CV_32FC1); - Mat final = Mat::zeros(h,w,CV_32FC3); - - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - indices.at<float>(i,j) = i+1; - - Mat a = Mat::zeros(h,w,CV_32FC1); - Mat b = Mat::zeros(h,w,CV_32FC1); - - for(int c=0;c<channel;c++) - { - Mat flag = Mat::ones(h,w,CV_32FC1); - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - flag.at<float>(i,j) = (c+1)*flag.at<float>(i,j); - - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - { - a.at<float>(i,j) = (flag.at<float>(i,j) - 1) * h * (w+1) + (lower_idx.at<float>(i,j) - 1) * h + indices.at<float>(i,j); - b.at<float>(i,j) = (flag.at<float>(i,j) - 1) * h * (w+1) + (upper_idx.at<float>(i,j) - 1) * h + indices.at<float>(i,j); - - } - - int p,q,r,rem; - int p1,q1,r1,rem1; - - for(int i=0;i<h;i++) - { - for(int j=0;j<w;j++) - { - - r = b.at<float>(i,j)/(h*(w+1)); - rem = b.at<float>(i,j) - r*h*(w+1); - q = rem/h; - p = rem - q*h; - if(q==0) - { - p=h; - q=w; - r=r-1; - } - if(p==0) - { - p=h; - q=q-1; - } - - - r1 = a.at<float>(i,j)/(h*(w+1)); - rem1 = a.at<float>(i,j) - r1*h*(w+1); - q1 = rem1/h; - p1 = rem1 - q1*h; - if(p1==0) - { - p1=h; - q1=q1-1; - } - - - 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))) - /(upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j)); - } - } - } - - for(int i=0;i<h;i++) - for(int j=0;j<w;j++) - for(int c=0;c<channel;c++) - output.at<float>(i,j*channel+c) = final.at<float>(i,j*channel+c); - + int h = output.rows; + int w = output.cols; + int channel = output.channels(); + + compute_boxfilter(output,hz,psketch,radius); + + Mat box_filter = Mat::zeros(h,w+1,CV_32FC3); + + for(int i = 0; i < h; i++) + { + box_filter.at<float>(i,1*channel+0) = output.at<float>(i,0*channel+0); + box_filter.at<float>(i,1*channel+1) = output.at<float>(i,0*channel+1); + box_filter.at<float>(i,1*channel+2) = output.at<float>(i,0*channel+2); + for(int j = 2; j < w+1; j++) + { + for(int c=0;c<channel;c++) + 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); + } + } + + Mat indices = Mat::zeros(h,w,CV_32FC1); + Mat final = Mat::zeros(h,w,CV_32FC3); + + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + indices.at<float>(i,j) = i+1; + + Mat a = Mat::zeros(h,w,CV_32FC1); + Mat b = Mat::zeros(h,w,CV_32FC1); + + for(int c=0;c<channel;c++) + { + Mat flag = Mat::ones(h,w,CV_32FC1); + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + flag.at<float>(i,j) = (c+1)*flag.at<float>(i,j); + + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + { + a.at<float>(i,j) = (flag.at<float>(i,j) - 1) * h * (w+1) + (lower_idx.at<float>(i,j) - 1) * h + indices.at<float>(i,j); + b.at<float>(i,j) = (flag.at<float>(i,j) - 1) * h * (w+1) + (upper_idx.at<float>(i,j) - 1) * h + indices.at<float>(i,j); + + } + + int p,q,r,rem; + int p1,q1,r1,rem1; + + for(int i=0;i<h;i++) + { + for(int j=0;j<w;j++) + { + + r = b.at<float>(i,j)/(h*(w+1)); + rem = b.at<float>(i,j) - r*h*(w+1); + q = rem/h; + p = rem - q*h; + if(q==0) + { + p=h; + q=w; + r=r-1; + } + if(p==0) + { + p=h; + q=q-1; + } + + + r1 = a.at<float>(i,j)/(h*(w+1)); + rem1 = a.at<float>(i,j) - r1*h*(w+1); + q1 = rem1/h; + p1 = rem1 - q1*h; + if(p1==0) + { + p1=h; + q1=q1-1; + } + + + 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))) + /(upper_idx.at<float>(i,j) - lower_idx.at<float>(i,j)); + } + } + } + + for(int i=0;i<h;i++) + for(int j=0;j<w;j++) + for(int c=0;c<channel;c++) + output.at<float>(i,j*channel+c) = final.at<float>(i,j*channel+c); } void Domain_Filter::init(const Mat &img, int flags, float sigma_s, float sigma_r) { - int h = img.size().height; - int w = img.size().width; - int channel = img.channels(); + int h = img.size().height; + int w = img.size().width; + int channel = img.channels(); - //////////////////////////////////// horizontal and vertical partial derivatives ///////////////////////////////// + //////////////////////////////////// horizontal and vertical partial derivatives ///////////////////////////////// - Mat derivx = Mat::zeros(h,w-1,CV_32FC3); - Mat derivy = Mat::zeros(h-1,w,CV_32FC3); + Mat derivx = Mat::zeros(h,w-1,CV_32FC3); + Mat derivy = Mat::zeros(h-1,w,CV_32FC3); - diffx(img,derivx); - diffy(img,derivy); + diffx(img,derivx); + diffy(img,derivy); - Mat distx = Mat::zeros(h,w,CV_32FC1); - Mat disty = Mat::zeros(h,w,CV_32FC1); + Mat distx = Mat::zeros(h,w,CV_32FC1); + Mat disty = Mat::zeros(h,w,CV_32FC1); - //////////////////////// Compute the l1-norm distance of neighbor pixels //////////////////////////////////////////////// + //////////////////////// Compute the l1-norm distance of neighbor pixels //////////////////////////////////////////////// - for(int i = 0; i < h; i++) - for(int j = 0,k=1; j < w-1; j++,k++) - for(int c = 0; c < channel; c++) - { - distx.at<float>(i,k) = - distx.at<float>(i,k) + abs(derivx.at<float>(i,j*channel+c)); - } + for(int i = 0; i < h; i++) + for(int j = 0,k=1; j < w-1; j++,k++) + for(int c = 0; c < channel; c++) + { + distx.at<float>(i,k) = + distx.at<float>(i,k) + abs(derivx.at<float>(i,j*channel+c)); + } - for(int i = 0,k=1; i < h-1; i++,k++) - for(int j = 0; j < w; j++) - for(int c = 0; c < channel; c++) - { - disty.at<float>(k,j) = - disty.at<float>(k,j) + abs(derivy.at<float>(i,j*channel+c)); - } + for(int i = 0,k=1; i < h-1; i++,k++) + for(int j = 0; j < w; j++) + for(int c = 0; c < channel; c++) + { + disty.at<float>(k,j) = + disty.at<float>(k,j) + abs(derivy.at<float>(i,j*channel+c)); + } - ////////////////////// Compute the derivatives of the horizontal and vertical domain transforms. ///////////////////////////// + ////////////////////// Compute the derivatives of the horizontal and vertical domain transforms. ///////////////////////////// - horiz = Mat(h,w,CV_32FC1); - vert = Mat(h,w,CV_32FC1); + horiz = Mat(h,w,CV_32FC1); + vert = Mat(h,w,CV_32FC1); - Mat final = Mat(h,w,CV_32FC3); + Mat final = Mat(h,w,CV_32FC3); - for(int i = 0; i < h; i++) - for(int j = 0; j < w; j++) - { - horiz.at<float>(i,j) = (float) 1.0 + (sigma_s/sigma_r) * distx.at<float>(i,j); - vert.at<float>(i,j) = (float) 1.0 + (sigma_s/sigma_r) * disty.at<float>(i,j); - } + for(int i = 0; i < h; i++) + for(int j = 0; j < w; j++) + { + horiz.at<float>(i,j) = (float) 1.0 + (sigma_s/sigma_r) * distx.at<float>(i,j); + vert.at<float>(i,j) = (float) 1.0 + (sigma_s/sigma_r) * disty.at<float>(i,j); + } - O = Mat(h,w,CV_32FC3); + O = Mat(h,w,CV_32FC3); - for(int i =0;i<h;i++) - for(int j =0;j<w;j++) - for(int c=0;c<channel;c++) - O.at<float>(i,j*channel+c) = img.at<float>(i,j*channel+c); + for(int i =0;i<h;i++) + for(int j =0;j<w;j++) + for(int c=0;c<channel;c++) + O.at<float>(i,j*channel+c) = img.at<float>(i,j*channel+c); - O_t = Mat(w,h,CV_32FC3); + O_t = Mat(w,h,CV_32FC3); - if(flags == 2) - { + if(flags == 2) + { - ct_H = Mat(h,w,CV_32FC1); - ct_V = Mat(h,w,CV_32FC1); + ct_H = Mat(h,w,CV_32FC1); + ct_V = Mat(h,w,CV_32FC1); - for(int i = 0; i < h; i++) - { - ct_H.at<float>(i,0) = horiz.at<float>(i,0); - for(int j = 1; j < w; j++) - { - ct_H.at<float>(i,j) = horiz.at<float>(i,j) + ct_H.at<float>(i,j-1); - } - } + for(int i = 0; i < h; i++) + { + ct_H.at<float>(i,0) = horiz.at<float>(i,0); + for(int j = 1; j < w; j++) + { + ct_H.at<float>(i,j) = horiz.at<float>(i,j) + ct_H.at<float>(i,j-1); + } + } - for(int j = 0; j < w; j++) - { - ct_V.at<float>(0,j) = vert.at<float>(0,j); - for(int i = 1; i < h; i++) - { - ct_V.at<float>(i,j) = vert.at<float>(i,j) + ct_V.at<float>(i-1,j); - } - } - } + for(int j = 0; j < w; j++) + { + ct_V.at<float>(0,j) = vert.at<float>(0,j); + for(int i = 1; i < h; i++) + { + ct_V.at<float>(i,j) = vert.at<float>(i,j) + ct_V.at<float>(i-1,j); + } + } + } } + void Domain_Filter::filter(const Mat &img, Mat &res, float sigma_s = 60, float sigma_r = 0.4, int flags = 1) { - int no_of_iter = 3; - int h = img.size().height; - int w = img.size().width; - float sigma_h = sigma_s; + int no_of_iter = 3; + int h = img.size().height; + int w = img.size().width; + float sigma_h = sigma_s; + + init(img,flags,sigma_s,sigma_r); + + if(flags == 1) + { + Mat vert_t = vert.t(); + + for(int i=0;i<no_of_iter;i++) + { + sigma_h = sigma_s * sqrt(3) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1); + + compute_Rfilter(O, horiz, sigma_h); + + O_t = O.t(); + + compute_Rfilter(O_t, vert_t, sigma_h); + + O = O_t.t(); + + } + } + else if(flags == 2) + { + + Mat vert_t = ct_V.t(); + Mat temp = Mat(h,w,CV_32FC1); + Mat temp1 = Mat(w,h,CV_32FC1); + + float radius; + + for(int i=0;i<no_of_iter;i++) + { + sigma_h = sigma_s * sqrt(3) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1); + + radius = sqrt(3) * sigma_h; + + compute_NCfilter(O, ct_H, temp,radius); + + O_t = O.t(); + + compute_NCfilter(O_t, vert_t, temp1, radius); + + O = O_t.t(); + } + } + + res = O.clone(); +} + +void Domain_Filter::pencil_sketch(const Mat &img, Mat &sketch, Mat &color_res, float sigma_s, float sigma_r, float shade_factor) +{ + + int no_of_iter = 3; + init(img,2,sigma_s,sigma_r); + int h = img.size().height; + int w = img.size().width; + int channel = img.channels(); + + /////////////////////// convert to YCBCR model for color pencil drawing ////////////////////////////////////////////////////// + + Mat color_sketch = Mat(h,w,CV_32FC3); + Mat Y_channel = Mat(h,w,CV_32FC1); + Mat U_channel = Mat(h,w,CV_32FC1); + Mat V_channel = Mat(h,w,CV_32FC1); + + cvtColor(img,color_sketch,COLOR_BGR2YCrCb); + + Mat vert_t = ct_V.t(); + + float sigma_h = sigma_s; + + Mat penx = Mat(h,w,CV_32FC1); + + Mat pen_res = Mat::zeros(h,w,CV_32FC1); + Mat peny = Mat(w,h,CV_32FC1); + + Mat peny_t; - init(img,flags,sigma_s,sigma_r); + float radius; - if(flags == 1) - { + for(int i=0;i<no_of_iter;i++) + { + sigma_h = sigma_s * sqrt(3) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1); - Mat vert_t = vert.t(); + radius = sqrt(3) * sigma_h; - for(int i=0;i<no_of_iter;i++) - { - sigma_h = sigma_s * sqrt(3) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1); + compute_boxfilter(O, ct_H, penx, radius); - compute_Rfilter(O, horiz, sigma_h); + O_t = O.t(); - O_t = O.t(); + compute_boxfilter(O_t, vert_t, peny, radius); - compute_Rfilter(O_t, vert_t, sigma_h); + O = O_t.t(); - O = O_t.t(); + peny_t = peny.t(); - } - } - else if(flags == 2) - { + for(int k=0;k<h;k++) + for(int j=0;j<w;j++) + pen_res.at<float>(k,j) = (shade_factor * (penx.at<float>(k,j) + peny_t.at<float>(k,j))); - Mat vert_t = ct_V.t(); - Mat temp = Mat(h,w,CV_32FC1); - Mat temp1 = Mat(w,h,CV_32FC1); + if(i==0) + { + sketch = pen_res.clone(); - float radius; + for(int k = 0; k < h; k++) + for(int j = 0; j < w; j++) + { + Y_channel.at<float>(k,j) = color_sketch.at<float>(k,j*channel+0); + U_channel.at<float>(k,j) = color_sketch.at<float>(k,j*channel+1); + V_channel.at<float>(k,j) = color_sketch.at<float>(k,j*channel+2); + } - for(int i=0;i<no_of_iter;i++) - { - sigma_h = sigma_s * sqrt(3) * pow(2.0,(no_of_iter - (i+1))) / sqrt(pow(4.0,no_of_iter) -1); - radius = sqrt(3) * sigma_h; + for(int k=0;k<h;k++) + for(int j=0;j<w;j++) + Y_channel.at<float>(k,j) = pen_res.at<float>(k,j); - compute_NCfilter(O, ct_H, temp,radius); + // cvMerge(Y_channel,U_channel,V_channel,0,color_sketch); + for(int k = 0; k < h; k++) + for(int j = 0; j < w; j++) + { + color_sketch.at<float>(k,j*channel+0) = Y_channel.at<float>(k,j); + color_sketch.at<float>(k,j*channel+1) = U_channel.at<float>(k,j); + color_sketch.at<float>(k,j*channel+2) = V_channel.at<float>(k,j); + } - O_t = O.t(); + cvtColor(color_sketch,color_res,COLOR_YCrCb2BGR); - compute_NCfilter(O_t, vert_t, temp1, radius); + } - O = O_t.t(); - } - } + } - res = O.clone(); } diff --git a/modules/photo/src/seamless_cloning.cpp b/modules/photo/src/seamless_cloning.cpp index 92beb02fe8..c67794ed7a 100644 --- a/modules/photo/src/seamless_cloning.cpp +++ b/modules/photo/src/seamless_cloning.cpp @@ -39,7 +39,6 @@ // //M*/ - #include "precomp.hpp" #include "opencv2/photo.hpp" #include "opencv2/imgproc.hpp" @@ -55,11 +54,11 @@ using namespace cv; void cv::seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point p, OutputArray _blend, int flags) { - Mat src = _src.getMat(); - Mat dest = _dst.getMat(); - Mat mask = _mask.getMat(); - _blend.create(dest.size(), CV_8UC3); - Mat blend = _blend.getMat(); + Mat src = _src.getMat(); + Mat dest = _dst.getMat(); + Mat mask = _mask.getMat(); + _blend.create(dest.size(), CV_8UC3); + Mat blend = _blend.getMat(); int minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN; int h = mask.size().height; @@ -134,14 +133,14 @@ void cv::seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point void cv::colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float r, float g, float b) { - Mat src = _src.getMat(); - Mat mask = _mask.getMat(); - _dst.create(src.size(), src.type()); - Mat blend = _dst.getMat(); - - float red = r; - float green = g; - float blue = b; + Mat src = _src.getMat(); + Mat mask = _mask.getMat(); + _dst.create(src.size(), src.type()); + Mat blend = _dst.getMat(); + + float red = r; + float green = g; + float blue = b; Mat gray = Mat::zeros(mask.size(),CV_8UC1); @@ -149,7 +148,7 @@ void cv::colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float cvtColor(mask, gray, COLOR_BGR2GRAY ); else gray = mask; - + Mat cs_mask = Mat::zeros(src.size(),CV_8UC3); int channel = 3; @@ -174,12 +173,12 @@ void cv::colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float void cv::illuminationChange(InputArray _src, InputArray _mask, OutputArray _dst, float a, float b) { - Mat src = _src.getMat(); - Mat mask = _mask.getMat(); - _dst.create(src.size(), src.type()); - Mat blend = _dst.getMat(); - float alpha = a; - float beta = b; + Mat src = _src.getMat(); + Mat mask = _mask.getMat(); + _dst.create(src.size(), src.type()); + Mat blend = _dst.getMat(); + float alpha = a; + float beta = b; Mat gray = Mat::zeros(mask.size(),CV_8UC1); @@ -203,19 +202,20 @@ void cv::illuminationChange(InputArray _src, InputArray _mask, OutputArray _dst, } } - + Cloning obj; obj.illum_change(src,cs_mask,gray,blend,alpha,beta); } -void cv::textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst) +void cv::textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst, + double low_threshold, double high_threshold, int kernel_size) { - Mat src = _src.getMat(); - Mat mask = _mask.getMat(); - _dst.create(src.size(), src.type()); - Mat blend = _dst.getMat(); + Mat src = _src.getMat(); + Mat mask = _mask.getMat(); + _dst.create(src.size(), src.type()); + Mat blend = _dst.getMat(); Mat gray = Mat::zeros(mask.size(),CV_8UC1); @@ -239,8 +239,8 @@ void cv::textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst) } } - + Cloning obj; - obj.texture_flatten(src,cs_mask,gray,blend); + obj.texture_flatten(src,cs_mask,gray,low_threshold,high_threshold,kernel_size,blend); } diff --git a/modules/photo/src/seamless_cloning.hpp b/modules/photo/src/seamless_cloning.hpp index a59cb5d16b..f039d2974d 100644 --- a/modules/photo/src/seamless_cloning.hpp +++ b/modules/photo/src/seamless_cloning.hpp @@ -39,7 +39,6 @@ // //M*/ - #include "precomp.hpp" #include "opencv2/photo.hpp" #include "opencv2/imgproc.hpp" @@ -74,9 +73,9 @@ class Cloning void transpose(double *mat, double *mat_t,int h,int w); void poisson_solver(const Mat &img, Mat &gxx , Mat &gyy, Mat &result); void normal_clone(Mat &I, Mat &mask, Mat &wmask, Mat &final, int num); - void local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, float red, float green, float blue); + void local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, float red_mul, float green_mul, float blue_mul); void illum_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, float alpha, float beta); - void texture_flatten(Mat &I, Mat &mask, Mat &wmask, Mat &final); + void texture_flatten(Mat &I, Mat &mask, Mat &wmask, double low_threshold, double high_threhold, int kernel_size, Mat &final); }; void Cloning::getGradientx( const Mat &img, Mat &gx) @@ -94,6 +93,7 @@ void Cloning::getGradientx( const Mat &img, Mat &gx) (float)img.at<uchar>(i,(j+1)*channel+c) - (float)img.at<uchar>(i,j*channel+c); } } + void Cloning::getGradienty( const Mat &img, Mat &gy) { int w = img.size().width; @@ -110,6 +110,7 @@ void Cloning::getGradienty( const Mat &img, Mat &gy) } } + void Cloning::lapx( const Mat &img, Mat &gxx) { int w = img.size().width; @@ -125,6 +126,7 @@ void Cloning::lapx( const Mat &img, Mat &gxx) (float)img.at<float>(i,(j+1)*channel+c) - (float)img.at<float>(i,j*channel+c); } } + void Cloning::lapy( const Mat &img, Mat &gyy) { int w = img.size().width; @@ -242,6 +244,7 @@ void Cloning::transpose(double *mat, double *mat_t,int h,int w) tmp.release(); } + void Cloning::poisson_solver(const Mat &img, Mat &gxx , Mat &gyy, Mat &result) { @@ -275,7 +278,6 @@ void Cloning::poisson_solver(const Mat &img, Mat &gxx , Mat &gyy, Mat &result) + (int)bound.at<uchar>(i-1,j) + (int)bound.at<uchar>(i+1,j); } - Mat diff = Mat(h,w,CV_32FC1); for(int i =0;i<h;i++) { @@ -391,7 +393,6 @@ void Cloning::poisson_solver(const Mat &img, Mat &gxx , Mat &gyy, Mat &result) delete [] img_d; delete [] gtest; delete [] f_bp; - } void Cloning::init(Mat &I, Mat &wmask) @@ -486,6 +487,7 @@ void Cloning::calc(Mat &I, Mat &gx, Mat &gy, Mat &sx, Mat &sy) } + void Cloning::normal_clone(Mat &I, Mat &mask, Mat &wmask, Mat &final, int num) { init(I,wmask); @@ -609,7 +611,8 @@ void Cloning::normal_clone(Mat &I, Mat &mask, Mat &wmask, Mat &final, int num) } -void Cloning::local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, float red=1.0, float green=1.0, float blue=1.0) +void Cloning::local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, float red_mul=1.0, + float green_mul=1.0, float blue_mul=1.0) { init(I,wmask); @@ -649,13 +652,11 @@ void Cloning::local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, floa for(int i=0;i < h; i++) for(int j=0; j < w; j++) { - factor.at<float>(i,j*channel+0) = blue; - factor.at<float>(i,j*channel+1) = green; - factor.at<float>(i,j*channel+2) = red; + factor.at<float>(i,j*channel+0) = blue_mul; + factor.at<float>(i,j*channel+1) = green_mul; + factor.at<float>(i,j*channel+2) = red_mul; } - - for(int i=0;i < h; i++) for(int j=0; j < w; j++) for(int c=0;c<channel;++c) @@ -783,7 +784,8 @@ void Cloning::illum_change(Mat &I, Mat &mask, Mat &wmask, Mat &final, float alph } -void Cloning::texture_flatten(Mat &I, Mat &mask, Mat &wmask, Mat &final) +void Cloning::texture_flatten(Mat &I, Mat &mask, Mat &wmask, double low_threshold, + double high_threshold, int kernel_size, Mat &final) { init(I,wmask); @@ -807,10 +809,8 @@ void Cloning::texture_flatten(Mat &I, Mat &mask, Mat &wmask, Mat &final) I.convertTo(srx32,CV_32FC3,1.0/255.0); I.convertTo(sry32,CV_32FC3,1.0/255.0); - - Mat out = Mat(mask.size(),CV_8UC1); - Canny( mask, out, 30, 45, 3 ); + Canny(mask,out,low_threshold,high_threshold,kernel_size); int channel = mask.channels(); diff --git a/modules/photo/test/test_cloning.cpp b/modules/photo/test/test_cloning.cpp index bb62d874aa..e6fbf3e6a3 100644 --- a/modules/photo/test/test_cloning.cpp +++ b/modules/photo/test/test_cloning.cpp @@ -100,7 +100,7 @@ TEST(Photo_SeamlessClone_mixed, regression) TEST(Photo_SeamlessClone_featureExchange, regression) { - string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Feature_Exchange/"; + string folder = string(cvtest::TS::ptr()->get_data_path()) + "cloning/Monochrome_Transfer/"; string original_path1 = folder + "source1.png"; string original_path2 = folder + "destination1.png"; string original_path3 = folder + "mask.png"; @@ -174,7 +174,7 @@ TEST(Photo_SeamlessClone_textureFlattening, regression) ASSERT_FALSE(mask.empty()) << "Could not load mask image " << original_path2; Mat result; - textureFlattening(source, mask, result); + textureFlattening(source, mask, result, 30, 45, 3); imwrite(folder + "cloned.png", result); diff --git a/modules/photo/test/test_npr.cpp b/modules/photo/test/test_npr.cpp new file mode 100644 index 0000000000..0a4b2c56ef --- /dev/null +++ b/modules/photo/test/test_npr.cpp @@ -0,0 +1,146 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2013, OpenCV Foundation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + + +#include "test_precomp.hpp" +#include "opencv2/photo.hpp" +#include <string> + +using namespace cv; +using namespace std; + + +TEST(Photo_NPR_EdgePreserveSmoothing_RecursiveFilter, regression) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/Smoothing/"; + string original_path = folder + "test1.png"; + + Mat source = imread(original_path, IMREAD_COLOR); + + ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path; + + Mat result; + edgePreservingFilter(source,result,1); + + imwrite(folder + "smoothened_RF.png", result); + +} + +TEST(Photo_NPR_EdgePreserveSmoothing_NormConvFilter, regression) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/Smoothing/"; + string original_path = folder + "test1.png"; + + Mat source = imread(original_path, IMREAD_COLOR); + + ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path; + + Mat result; + edgePreservingFilter(source,result,2); + + imwrite(folder + "smoothened_NCF.png", result); + +} + +TEST(Photo_NPR_DetailEnhance, regression) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/Detail_Enhance/"; + string original_path = folder + "test1.png"; + + Mat source = imread(original_path, IMREAD_COLOR); + + ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path; + + Mat result; + detailEnhance(source,result); + + imwrite(folder + "detail_enhanced.png", result); + +} + +TEST(Photo_NPR_PencilSketch, regression) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/Pencil_Sketch/"; + string original_path = folder + "test1.png"; + + Mat source = imread(original_path, IMREAD_COLOR); + + ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path; + + Mat result,result1; + pencilSketch(source,result,result1,10,.1,.03); + + imwrite(folder + "pencil_sketch.png", result); + imwrite(folder + "color_pencil_sketch.png", result1); + +} + +TEST(Photo_NPR_Stylization, regression) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/Stylization/"; + string original_path = folder + "test1.png"; + + Mat source = imread(original_path, IMREAD_COLOR); + + ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path; + + Mat result; + stylization(source,result); + + imwrite(folder + "stylized.png", result); + +} + +TEST(Photo_NPR_EdgeEnhance, regression) +{ + string folder = string(cvtest::TS::ptr()->get_data_path()) + "npr/Edge_Enhance/"; + string original_path = folder + "test1.png"; + + Mat source = imread(original_path, IMREAD_COLOR); + + ASSERT_FALSE(source.empty()) << "Could not load input image " << original_path; + + Mat result; + edgeEnhance(source,result); + + imwrite(folder + "edge_enhanced.png", result); + +} diff --git a/samples/cpp/cloning.cpp b/samples/cpp/cloning.cpp index 9a41ce229b..84458fd4fe 100644 --- a/samples/cpp/cloning.cpp +++ b/samples/cpp/cloning.cpp @@ -56,18 +56,19 @@ char src[50]; char dest[50]; int var = 0; -int flag = 0; -int flag1 = 0; +int flag = 0, flag1 = 0, flag4 = 0; -int minx,miny,maxx,maxy,lenx,leny; -int minxd,minyd,maxxd,maxyd,lenxd,lenyd; +int minx, miny, maxx, maxy, lenx, leny; +int minxd, minyd, maxxd, maxyd, lenxd, lenyd; -int channel,num; +int channel, num, kernel_size; float alpha,beta; float red, green, blue; +double low_t, high_t; + void source(int event, int x, int y, int, void*) { @@ -159,7 +160,7 @@ void source(int event, int x, int y, int, void*) } else if(num == 6) { - textureFlattening(img0,res1,blend); + textureFlattening(img0,res1,blend,low_t,high_t,kernel_size); imshow("Texture Flattened", blend); waitKey(0); } @@ -176,11 +177,12 @@ void source(int event, int x, int y, int, void*) flag1 = 0; minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN; imshow("Source", img0); + if(num == 1 || num == 2 || num == 3) + imshow("Destination",img2); drag = 0; } } - void destination(int event, int x, int y, int, void*) { @@ -190,6 +192,7 @@ void destination(int event, int x, int y, int, void*) im1 = img2.clone(); if (event == EVENT_LBUTTONDOWN) { + flag4 = 1; if(flag1 == 1) { point = Point(x, y); @@ -386,14 +389,12 @@ int main(int argc, char **argv) res1 = Mat::zeros(img0.size(),CV_8UC1); final = Mat::zeros(img0.size(),CV_8UC3); - //////////// source image /////////////////// namedWindow("Source", 1); setMouseCallback("Source", source, NULL); imshow("Source", img0); - } else if(num == 5) { @@ -416,12 +417,20 @@ int main(int argc, char **argv) setMouseCallback("Source", source, NULL); imshow("Source", img0); - } else if(num == 6) { checkfile(s); + cout << "low_threshold: "; + cin >> low_t; + + cout << "high_threshold: "; + cin >> high_t; + + cout << "kernel_size: "; + cin >> kernel_size; + img0 = imread(src); res1 = Mat::zeros(img0.size(),CV_8UC1); @@ -434,13 +443,16 @@ int main(int argc, char **argv) imshow("Source", img0); } + int flag3 = 0; + while(true) { char key = waitKey(0); - if(key == 'd') + if(key == 'd' && flag3 == 0) { flag1 = 1; + flag3 = 1; img1 = img0.clone(); for(int i = var; i < numpts ; i++) pts[i] = point; @@ -490,13 +502,15 @@ int main(int argc, char **argv) } var = 0; flag1 = 0; + flag3 = 0; + flag4 = 0; minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN; imshow("Source", img0); if(num == 1 || num == 2 || num == 3) imshow("Destination",img2); drag = 0; } - else if ((num == 1 || num == 2 || num == 3) && key == 'c' && flag1 == 1) + else if ((num == 1 || num == 2 || num == 3) && key == 'c' && flag1 == 1 && flag4 == 1) { seamlessClone(img0,img2,res1,point,blend,num); imshow("Cloned Image", blend); @@ -516,14 +530,12 @@ int main(int argc, char **argv) } else if (num == 6 && key == 'c' && flag1 == 1) { - textureFlattening(img0,res1,blend); + textureFlattening(img0,res1,blend,low_t,high_t,kernel_size); imshow("Texture Flattened", blend); imwrite("cloned.png",blend); } else if(key == 'q') exit(0); - } - waitKey(0); } diff --git a/samples/cpp/cloning_example.cpp b/samples/cpp/cloning_example.cpp new file mode 100644 index 0000000000..6c31d2f44e --- /dev/null +++ b/samples/cpp/cloning_example.cpp @@ -0,0 +1,243 @@ +/* +* cloning.cpp +* +* Author: +* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com> +* +* This tutorial demonstrates how to use OpenCV seamless cloning +* module without GUI. +* +* 1- Normal Cloning +* 2- Mixed Cloning +* 3- Monochrome Transfer +* 4- Color Change +* 5- Illumination change +* 6- Texture Flattening + +* The program takes as input a source and a destination image (for 1-3 methods) +* and ouputs the cloned image. +*/ + +#include "opencv2/photo.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/core.hpp" +#include <iostream> +#include <stdlib.h> + +using namespace std; +using namespace cv; + +int main(int argc, char **argv) +{ + cout << endl; + cout << "Cloning Module" << endl; + cout << "---------------" << endl; + cout << "Options: " << endl; + cout << endl; + cout << "1) Normal Cloning " << endl; + cout << "2) Mixed Cloning " << endl; + cout << "3) Monochrome Transfer " << endl; + cout << "4) Local Color Change " << endl; + cout << "5) Local Illumination Change " << endl; + cout << "6) Texture Flattening " << endl; + cout << endl; + cout << "Press number 1-6 to choose from above techniques: "; + int num; + cin >> num; + cout << endl; + + if(num == 1) + { + string folder = "cloning/Normal_Cloning/"; + string original_path1 = folder + "source1.png"; + string original_path2 = folder + "destination1.png"; + string original_path3 = folder + "mask.png"; + + Mat source = imread(original_path1, IMREAD_COLOR); + Mat destination = imread(original_path2, IMREAD_COLOR); + Mat mask = imread(original_path3, IMREAD_COLOR); + + if(source.empty()) + { + cout << "Could not load source image " << original_path1 << endl; + exit(0); + } + if(destination.empty()) + { + cout << "Could not load destination image " << original_path2 << endl; + exit(0); + } + if(mask.empty()) + { + cout << "Could not load mask image " << original_path3 << endl; + exit(0); + } + + Mat result; + Point p; + p.x = 400; + p.y = 100; + + seamlessClone(source, destination, mask, p, result, 1); + + imshow("Output",result); + imwrite(folder + "cloned.png", result); + } + else if(num == 2) + { + string folder = "cloning/Mixed_Cloning/"; + string original_path1 = folder + "source1.png"; + string original_path2 = folder + "destination1.png"; + string original_path3 = folder + "mask.png"; + + Mat source = imread(original_path1, IMREAD_COLOR); + Mat destination = imread(original_path2, IMREAD_COLOR); + Mat mask = imread(original_path3, IMREAD_COLOR); + + if(source.empty()) + { + cout << "Could not load source image " << original_path1 << endl; + exit(0); + } + if(destination.empty()) + { + cout << "Could not load destination image " << original_path2 << endl; + exit(0); + } + if(mask.empty()) + { + cout << "Could not load mask image " << original_path3 << endl; + exit(0); + } + + Mat result; + Point p; + p.x = destination.size().width/2; + p.y = destination.size().height/2; + + seamlessClone(source, destination, mask, p, result, 2); + + imshow("Output",result); + imwrite(folder + "cloned.png", result); + } + else if(num == 3) + { + string folder = "cloning/Monochrome_Transfer/"; + string original_path1 = folder + "source1.png"; + string original_path2 = folder + "destination1.png"; + string original_path3 = folder + "mask.png"; + + Mat source = imread(original_path1, IMREAD_COLOR); + Mat destination = imread(original_path2, IMREAD_COLOR); + Mat mask = imread(original_path3, IMREAD_COLOR); + + if(source.empty()) + { + cout << "Could not load source image " << original_path1 << endl; + exit(0); + } + if(destination.empty()) + { + cout << "Could not load destination image " << original_path2 << endl; + exit(0); + } + if(mask.empty()) + { + cout << "Could not load mask image " << original_path3 << endl; + exit(0); + } + + Mat result; + Point p; + p.x = destination.size().width/2; + p.y = destination.size().height/2; + + seamlessClone(source, destination, mask, p, result, 3); + + imshow("Output",result); + imwrite(folder + "cloned.png", result); + } + else if(num == 4) + { + string folder = "cloning/Color_Change/"; + string original_path1 = folder + "source1.png"; + string original_path2 = folder + "mask.png"; + + Mat source = imread(original_path1, IMREAD_COLOR); + Mat mask = imread(original_path2, IMREAD_COLOR); + + if(source.empty()) + { + cout << "Could not load source image " << original_path1 << endl; + exit(0); + } + if(mask.empty()) + { + cout << "Could not load mask image " << original_path2 << endl; + exit(0); + } + + Mat result; + + colorChange(source, mask, result, 1.5, .5, .5); + + imshow("Output",result); + imwrite(folder + "cloned.png", result); + } + else if(num == 5) + { + string folder = "cloning/Illumination_Change/"; + string original_path1 = folder + "source1.png"; + string original_path2 = folder + "mask.png"; + + Mat source = imread(original_path1, IMREAD_COLOR); + Mat mask = imread(original_path2, IMREAD_COLOR); + + if(source.empty()) + { + cout << "Could not load source image " << original_path1 << endl; + exit(0); + } + if(mask.empty()) + { + cout << "Could not load mask image " << original_path2 << endl; + exit(0); + } + + Mat result; + + illuminationChange(source, mask, result, .2, .4); + + imshow("Output",result); + imwrite(folder + "cloned.png", result); + } + else if(num == 6) + { + string folder = "cloning/Texture_Flattening/"; + string original_path1 = folder + "source1.png"; + string original_path2 = folder + "mask.png"; + + Mat source = imread(original_path1, IMREAD_COLOR); + Mat mask = imread(original_path2, IMREAD_COLOR); + + if(source.empty()) + { + cout << "Could not load source image " << original_path1 << endl; + exit(0); + } + if(mask.empty()) + { + cout << "Could not load mask image " << original_path2 << endl; + exit(0); + } + + Mat result; + + textureFlattening(source, mask, result, 30, 45, 3); + + imshow("Output",result); + imwrite(folder + "cloned.png", result); + } + waitKey(0); +} diff --git a/samples/cpp/npr_demo.cpp b/samples/cpp/npr_demo.cpp new file mode 100644 index 0000000000..871fd32405 --- /dev/null +++ b/samples/cpp/npr_demo.cpp @@ -0,0 +1,107 @@ +/* +* npr_demo.cpp +* +* Author: +* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com> +* +* This tutorial demonstrates how to use OpenCV Non-Photorealistic Rendering Module. +* 1) Edge Preserve Smoothing +* -> Using Normalized convolution Filter +* -> Using Recursive Filter +* 2) Detail Enhancement +* 3) Pencil sketch/Color Pencil Drawing +* 4) Stylization +* 5) Edge Enhancement +* +*/ + +#include <signal.h> +#include "opencv2/photo.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/core.hpp" +#include <iostream> +#include <stdlib.h> + +using namespace std; +using namespace cv; + +int main(int argc, char* argv[]) +{ + int num,type; + + int flag = 0; + + Mat I = imread(argv[1]); + + + if(argc < 2) + { + cout << "usage: " << argv[0] << " <Input image> " << endl; + exit(0); + } + + if(!I.data) + { + cout << "Image not found" << endl; + exit(0); + } + + cout << endl; + cout << " Edge Preserve Filter" << endl; + cout << "----------------------" << endl; + + cout << "Options: " << endl; + cout << endl; + + cout << "1) Edge Preserve Smoothing" << endl; + cout << " -> Using Normalized convolution Filter" << endl; + cout << " -> Using Recursive Filter" << endl; + cout << "2) Detail Enhancement" << endl; + cout << "3) Pencil sketch/Color Pencil Drawing" << endl; + cout << "4) Stylization" << endl; + cout << "5) Edge Enhancement" << endl; + cout << endl; + + cout << "Press number 1-5 to choose from above techniques: "; + + cin >> num; + + Mat img; + + if(num == 1) + { + cout << endl; + cout << "Press 1 for Normalized Convolution Filter and 2 for Recursive Filter: "; + + cin >> type; + + edgePreservingFilter(I,img,type); + imshow("Edge Preserve Smoothing",img); + + } + else if(num == 2) + { + detailEnhance(I,img); + imshow("Detail Enhanced",img); + } + else if(num == 3) + { + Mat img1; + pencilSketch(I,img1, img, 10 ,.1,.03); + imshow("Pencil Sketch",img1); + imshow("Color Pencil Sketch",img); + } + else if(num == 4) + { + stylization(I,img); + imshow("Stylization",img); + } + else if(num == 5) + { + edgeEnhance(I,img); + imshow("Edge Enhance",img); + } + + waitKey(0); +} -- 2.18.0