1/*
2* cloning.cpp
3*
4* Author:
5* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com>
6*
7* This tutorial demonstrates how to use OpenCV seamless cloning
8* module.
9*
10* 1- Normal Cloning
11* 2- Mixed Cloning
12* 3- Monochrome Transfer
13* 4- Color Change
14* 5- Illumination change
15* 6- Texture Flattening
16
17* The program takes as input a source and a destination image (for 1-3 methods)
18* and ouputs the cloned image.
19
20* Step 1:
21* -> In the source image, select the region of interest by left click mouse button. A Polygon ROI will be created by left clicking mouse button.
22* -> To set the Polygon ROI, click the right mouse button or 'd' key.
23* -> To reset the region selected, click the middle mouse button or 'r' key.
24
25* Step 2:
26* -> In the destination image, select the point where you want to place the ROI in the image by left clicking mouse button.
27* -> To get the cloned result, click the right mouse button or 'c' key.
28* -> To quit the program, use 'q' key.
29*
30* Result: The cloned image will be displayed.
31*/
32
33#include <signal.h>
34#include "opencv2/photo.hpp"
35#include "opencv2/imgproc.hpp"
36#include "opencv2/highgui.hpp"
37#include "opencv2/core.hpp"
38#include <iostream>
39#include <stdlib.h>
40
41using namespace std;
42using namespace cv;
43
44Mat img0, img1, img2, res, res1, final, final1, blend;
45
46Point point;
47int drag = 0;
48int destx, desty;
49
50int numpts = 100;
51Point* pts = new Point[100];
52Point* pts2 = new Point[100];
53Point* pts_diff = new Point[100];
54
55int var = 0;
56int flag = 0, flag1 = 0, flag4 = 0;
57
58int minx, miny, maxx, maxy, lenx, leny;
59int minxd, minyd, maxxd, maxyd, lenxd, lenyd;
60
61int channel, num, kernel_size;
62
63float alpha,beta;
64
65float red, green, blue;
66
67float low_t, high_t;
68
69void source(int, int, int, int, void*);
70void destination(int, int, int, int, void*);
71void checkfile(char*);
72
73void source(int event, int x, int y, int, void*)
74{
75
76    if (event == EVENT_LBUTTONDOWN && !drag)
77    {
78        if(flag1 == 0)
79        {
80            if(var==0)
81                img1 = img0.clone();
82            point = Point(x, y);
83            circle(img1,point,2,Scalar(0, 0, 255),-1, 8, 0);
84            pts[var] = point;
85            var++;
86            drag  = 1;
87            if(var>1)
88                line(img1,pts[var-2], point, Scalar(0, 0, 255), 2, 8, 0);
89
90            imshow("Source", img1);
91        }
92    }
93
94    if (event == EVENT_LBUTTONUP && drag)
95    {
96        imshow("Source", img1);
97
98        drag = 0;
99    }
100    if (event == EVENT_RBUTTONDOWN)
101    {
102        flag1 = 1;
103        img1 = img0.clone();
104        for(int i = var; i < numpts ; i++)
105            pts[i] = point;
106
107        if(var!=0)
108        {
109            const Point* pts3[1] = {&pts[0]};
110            polylines( img1, pts3, &numpts,1, 1, Scalar(0,0,0), 2, 8, 0);
111        }
112
113        for(int i=0;i<var;i++)
114        {
115            minx = min(minx,pts[i].x);
116            maxx = max(maxx,pts[i].x);
117            miny = min(miny,pts[i].y);
118            maxy = max(maxy,pts[i].y);
119        }
120        lenx = maxx - minx;
121        leny = maxy - miny;
122
123        int mid_pointx = minx + lenx/2;
124        int mid_pointy = miny + leny/2;
125
126        for(int i=0;i<var;i++)
127        {
128            pts_diff[i].x = pts[i].x - mid_pointx;
129            pts_diff[i].y = pts[i].y - mid_pointy;
130        }
131
132        imshow("Source", img1);
133    }
134
135    if (event == EVENT_RBUTTONUP)
136    {
137        flag = var;
138
139        final = Mat::zeros(img0.size(),CV_8UC3);
140        res1 = Mat::zeros(img0.size(),CV_8UC1);
141        const Point* pts4[1] = {&pts[0]};
142
143        fillPoly(res1, pts4,&numpts, 1, Scalar(255, 255, 255), 8, 0);
144        bitwise_and(img0, img0, final,res1);
145
146        imshow("Source", img1);
147
148        if(num == 4)
149        {
150            colorChange(img0,res1,blend,red,green,blue);
151            imshow("Color Change Image", blend);
152            waitKey(0);
153
154        }
155        else if(num == 5)
156        {
157            illuminationChange(img0,res1,blend,alpha,beta);
158            imshow("Illum Change Image", blend);
159            waitKey(0);
160        }
161        else if(num == 6)
162        {
163            textureFlattening(img0,res1,blend,low_t,high_t,kernel_size);
164            imshow("Texture Flattened", blend);
165            waitKey(0);
166        }
167
168    }
169    if (event == EVENT_MBUTTONDOWN)
170    {
171        for(int i = 0; i < numpts ; i++)
172        {
173            pts[i].x=0;
174            pts[i].y=0;
175        }
176        var = 0;
177        flag1 = 0;
178        minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;
179        imshow("Source", img0);
180        if(num == 1 || num == 2 || num == 3)
181            imshow("Destination",img2);
182        drag = 0;
183    }
184}
185
186void destination(int event, int x, int y, int, void*)
187{
188
189    Mat im1;
190    minxd = INT_MAX; minyd = INT_MAX; maxxd = INT_MIN; maxyd = INT_MIN;
191    im1 = img2.clone();
192    if (event == EVENT_LBUTTONDOWN)
193    {
194        flag4 = 1;
195        if(flag1 == 1)
196        {
197            point = Point(x, y);
198
199            for(int i=0;i<var;i++)
200            {
201                pts2[i].x = point.x + pts_diff[i].x;
202                pts2[i].y = point.y + pts_diff[i].y;
203            }
204
205            for(int i=var;i<numpts;i++)
206            {
207                pts2[i].x = point.x + pts_diff[0].x;
208                pts2[i].y = point.y + pts_diff[0].y;
209            }
210
211            const Point* pts5[1] = {&pts2[0]};
212            polylines( im1, pts5, &numpts,1, 1, Scalar(0,0,255), 2, 8, 0);
213
214            destx = x;
215            desty = y;
216
217            imshow("Destination", im1);
218        }
219    }
220    if (event == EVENT_RBUTTONUP)
221    {
222        for(int i=0;i<flag;i++)
223        {
224            minxd = min(minxd,pts2[i].x);
225            maxxd = max(maxxd,pts2[i].x);
226            minyd = min(minyd,pts2[i].y);
227            maxyd = max(maxyd,pts2[i].y);
228        }
229
230        if(maxxd > im1.size().width || maxyd > im1.size().height || minxd < 0 || minyd < 0)
231        {
232            cout << "Index out of range" << endl;
233            exit(0);
234        }
235
236        final1 = Mat::zeros(img2.size(),CV_8UC3);
237        res = Mat::zeros(img2.size(),CV_8UC1);
238        for(int i=miny, k=minyd;i<(miny+leny);i++,k++)
239            for(int j=minx,l=minxd ;j<(minx+lenx);j++,l++)
240            {
241                for(int c=0;c<channel;c++)
242                {
243                    final1.at<uchar>(k,l*channel+c) = final.at<uchar>(i,j*channel+c);
244
245                }
246            }
247
248        const Point* pts6[1] = {&pts2[0]};
249        fillPoly(res, pts6, &numpts, 1, Scalar(255, 255, 255), 8, 0);
250
251        if(num == 1 || num == 2 || num == 3)
252        {
253            seamlessClone(img0,img2,res1,point,blend,num);
254            imshow("Cloned Image", blend);
255            imwrite("cloned.png",blend);
256            waitKey(0);
257        }
258
259        for(int i = 0; i < flag ; i++)
260        {
261            pts2[i].x=0;
262            pts2[i].y=0;
263        }
264
265        minxd = INT_MAX; minyd = INT_MAX; maxxd = INT_MIN; maxyd = INT_MIN;
266    }
267
268    im1.release();
269}
270
271int main()
272{
273    cout << endl;
274    cout << "Cloning Module" << endl;
275    cout << "---------------" << endl;
276    cout << "Step 1:" << endl;
277    cout << " -> In the source image, select the region of interest by left click mouse button. A Polygon ROI will be created by left clicking mouse button." << endl;
278    cout << " -> To set the Polygon ROI, click the right mouse button or use 'd' key" << endl;
279    cout << " -> To reset the region selected, click the middle mouse button or use 'r' key." << endl;
280
281    cout << "Step 2:" << endl;
282    cout << " -> In the destination image, select the point where you want to place the ROI in the image by left clicking mouse button." << endl;
283    cout << " -> To get the cloned result, click the right mouse button or use 'c' key." << endl;
284    cout << " -> To quit the program, use 'q' key." << endl;
285    cout << endl;
286    cout << "Options: " << endl;
287    cout << endl;
288    cout << "1) Normal Cloning " << endl;
289    cout << "2) Mixed Cloning " << endl;
290    cout << "3) Monochrome Transfer " << endl;
291    cout << "4) Local Color Change " << endl;
292    cout << "5) Local Illumination Change " << endl;
293    cout << "6) Texture Flattening " << endl;
294
295    cout << endl;
296
297    cout << "Press number 1-6 to choose from above techniques: ";
298    cin >> num;
299    cout << endl;
300
301    minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;
302
303    minxd = INT_MAX; minyd = INT_MAX; maxxd = INT_MIN; maxyd = INT_MIN;
304
305    int flag3 = 0;
306
307    if(num == 1 || num == 2 || num == 3)
308    {
309
310        string src,dest;
311        cout << "Enter Source Image: ";
312        cin >> src;
313
314        cout << "Enter Destination Image: ";
315        cin >> dest;
316
317        img0 = imread(src);
318
319        img2 = imread(dest);
320
321        if(img0.empty())
322        {
323            cout << "Source Image does not exist" << endl;
324            exit(0);
325        }
326        if(img2.empty())
327        {
328            cout << "Destination Image does not exist" << endl;
329            exit(0);
330        }
331
332        channel = img0.channels();
333
334        res = Mat::zeros(img2.size(),CV_8UC1);
335        res1 = Mat::zeros(img0.size(),CV_8UC1);
336        final = Mat::zeros(img0.size(),CV_8UC3);
337        final1 = Mat::zeros(img2.size(),CV_8UC3);
338        //////////// source image ///////////////////
339
340        namedWindow("Source", 1);
341        setMouseCallback("Source", source, NULL);
342        imshow("Source", img0);
343
344        /////////// destination image ///////////////
345
346        namedWindow("Destination", 1);
347        setMouseCallback("Destination", destination, NULL);
348        imshow("Destination",img2);
349
350    }
351    else if(num == 4)
352    {
353        string src;
354        cout << "Enter Source Image: ";
355        cin >> src;
356
357        cout << "Enter RGB values: " << endl;
358        cout << "Red: ";
359        cin >> red;
360
361        cout << "Green: ";
362        cin >> green;
363
364        cout << "Blue: ";
365        cin >> blue;
366
367        img0 = imread(src);
368
369        if(img0.empty())
370        {
371            cout << "Source Image does not exist" << endl;
372            exit(0);
373        }
374
375        res1 = Mat::zeros(img0.size(),CV_8UC1);
376        final = Mat::zeros(img0.size(),CV_8UC3);
377
378        //////////// source image ///////////////////
379
380        namedWindow("Source", 1);
381        setMouseCallback("Source", source, NULL);
382        imshow("Source", img0);
383
384    }
385    else if(num == 5)
386    {
387        string src;
388        cout << "Enter Source Image: ";
389        cin >> src;
390
391        cout << "alpha: ";
392        cin >> alpha;
393
394        cout << "beta: ";
395        cin >> beta;
396
397        img0 = imread(src);
398
399        if(img0.empty())
400        {
401            cout << "Source Image does not exist" << endl;
402            exit(0);
403        }
404
405        res1 = Mat::zeros(img0.size(),CV_8UC1);
406        final = Mat::zeros(img0.size(),CV_8UC3);
407
408        //////////// source image ///////////////////
409
410        namedWindow("Source", 1);
411        setMouseCallback("Source", source, NULL);
412        imshow("Source", img0);
413
414    }
415    else if(num == 6)
416    {
417        string src;
418        cout << "Enter Source Image: ";
419        cin >> src;
420
421        cout << "low_threshold: ";
422        cin >> low_t;
423
424        cout << "high_threshold: ";
425        cin >> high_t;
426
427        cout << "kernel_size: ";
428        cin >> kernel_size;
429
430        img0 = imread(src);
431
432        if(img0.empty())
433        {
434            cout << "Source Image does not exist" << endl;
435            exit(0);
436        }
437
438        res1 = Mat::zeros(img0.size(),CV_8UC1);
439        final = Mat::zeros(img0.size(),CV_8UC3);
440
441        //////////// source image ///////////////////
442
443        namedWindow("Source", 1);
444        setMouseCallback("Source", source, NULL);
445        imshow("Source", img0);
446    }
447    else
448    {
449        cout << "Wrong Option Choosen" << endl;
450        exit(0);
451    }
452
453    for(;;)
454    {
455        char key = (char) waitKey(0);
456
457        if(key == 'd' && flag3 == 0)
458        {
459            flag1 = 1;
460            flag3 = 1;
461            img1 = img0.clone();
462            for(int i = var; i < numpts ; i++)
463                pts[i] = point;
464
465            if(var!=0)
466            {
467                const Point* pts3[1] = {&pts[0]};
468                polylines( img1, pts3, &numpts,1, 1, Scalar(0,0,0), 2, 8, 0);
469            }
470
471            for(int i=0;i<var;i++)
472            {
473                minx = min(minx,pts[i].x);
474                maxx = max(maxx,pts[i].x);
475                miny = min(miny,pts[i].y);
476                maxy = max(maxy,pts[i].y);
477            }
478            lenx = maxx - minx;
479            leny = maxy - miny;
480
481            int mid_pointx = minx + lenx/2;
482            int mid_pointy = miny + leny/2;
483
484            for(int i=0;i<var;i++)
485            {
486                pts_diff[i].x = pts[i].x - mid_pointx;
487                pts_diff[i].y = pts[i].y - mid_pointy;
488            }
489
490            flag = var;
491
492            final = Mat::zeros(img0.size(),CV_8UC3);
493            res1 = Mat::zeros(img0.size(),CV_8UC1);
494            const Point* pts4[1] = {&pts[0]};
495
496            fillPoly(res1, pts4,&numpts, 1, Scalar(255, 255, 255), 8, 0);
497            bitwise_and(img0, img0, final,res1);
498
499            imshow("Source", img1);
500        }
501        else if(key == 'r')
502        {
503            for(int i = 0; i < numpts ; i++)
504            {
505                pts[i].x=0;
506                pts[i].y=0;
507            }
508            var = 0;
509            flag1 = 0;
510            flag3 = 0;
511            flag4 = 0;
512            minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;
513            imshow("Source", img0);
514            if(num == 1 || num == 2 || num == 3)
515                imshow("Destination",img2);
516            drag = 0;
517        }
518        else if ((num == 1 || num == 2 || num == 3) && key == 'c' && flag1 == 1 && flag4 == 1)
519        {
520            seamlessClone(img0,img2,res1,point,blend,num);
521            imshow("Cloned Image", blend);
522            imwrite("cloned.png",blend);
523        }
524        else if (num == 4 && key == 'c' && flag1 == 1)
525        {
526            colorChange(img0,res1,blend,red,green,blue);
527            imshow("Color Change Image", blend);
528            imwrite("cloned.png",blend);
529        }
530        else if (num == 5 && key == 'c' && flag1 == 1)
531        {
532            illuminationChange(img0,res1,blend,alpha,beta);
533            imshow("Illum Change Image", blend);
534            imwrite("cloned.png",blend);
535        }
536        else if (num == 6 && key == 'c' && flag1 == 1)
537        {
538            textureFlattening(img0,res1,blend,low_t,high_t,kernel_size);
539            imshow("Texture Flattened", blend);
540            imwrite("cloned.png",blend);
541        }
542        else if(key == 'q')
543            exit(0);
544    }
545    waitKey(0);
546}
547