├── .DS_Store
├── .gitattributes
├── .idea
├── .name
├── Part2.iml
├── encodings.xml
├── misc.xml
├── modules.xml
└── workspace.xml
├── 10.jpg
├── 3.jpg
├── 9.jpg
├── Assignment 2 - Report.pdf
├── LipOut.jpg
├── README.md
├── __pycache__
├── face_parts.cpython-36.pyc
├── faces.cpython-36.pyc
├── facial_landmark.cpython-36.pyc
├── flood.cpython-36.pyc
├── lip.cpython-36.pyc
├── makeup.cpython-36.pyc
├── manual_select.cpython-36.pyc
├── overlay_blur.cpython-36.pyc
├── skin_detector.cpython-36.pyc
└── warp.cpython-36.pyc
├── b1.jpg
├── bluelip.jpg
├── bluelip2.jpg
├── example_01.jpg
├── example_02.jpg
├── example_03.jpg
├── face_makeup_cvpr09_lowres.pdf
├── face_parts.py
├── facial_landmark.py
├── haarcascade_frontalface_default.xml
├── input.jpg
├── lip.py
├── main.py
├── makeup.py
├── manual_select.py
├── out1.jpg
├── overlay_blur.py
├── res.jpg
├── result_with_masking_all.jpg
├── result_with_masking_all_but_nose.jpg
├── result_with_no_transfer.jpg
├── skin_detector.py
├── subject.jpg
├── target.jpg
└── warp.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/.DS_Store
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Part2
--------------------------------------------------------------------------------
/.idea/Part2.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 | 1504330731677
409 |
410 | 1504330731677
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
--------------------------------------------------------------------------------
/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/10.jpg
--------------------------------------------------------------------------------
/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/3.jpg
--------------------------------------------------------------------------------
/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/9.jpg
--------------------------------------------------------------------------------
/Assignment 2 - Report.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/Assignment 2 - Report.pdf
--------------------------------------------------------------------------------
/LipOut.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/LipOut.jpg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Face-Makeup-by-Example
2 |
3 | This is an implementation of following paper in python with OpenCV -
4 | Digital Face Makeup by Example, Dong Guo and Terence Sim, School of Computing, National University of Singapore Singapore.
5 |
6 | The code present in the following files -
7 |
8 | 1. makeup.py - main file
9 | 2. facial_landmark.py - facial feature points
10 | 3. warp.py - warping target face to subject face
11 | 4. skin_detector.py - skin detection
12 | 5. face_parts.py - retrieving certain face parts
13 | 6. lip.py - lip makeup
14 | 7. manual_select.py - manual adjustment of facial feature points
15 | 8. overlay_blur.py - final step to reduce sharp edges of output
16 |
17 | The images can be used for testing. The report contains some intermediary results.
18 |
--------------------------------------------------------------------------------
/__pycache__/face_parts.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/face_parts.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/faces.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/faces.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/facial_landmark.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/facial_landmark.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/flood.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/flood.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/lip.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/lip.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/makeup.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/makeup.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/manual_select.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/manual_select.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/overlay_blur.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/overlay_blur.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/skin_detector.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/skin_detector.cpython-36.pyc
--------------------------------------------------------------------------------
/__pycache__/warp.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/__pycache__/warp.cpython-36.pyc
--------------------------------------------------------------------------------
/b1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/b1.jpg
--------------------------------------------------------------------------------
/bluelip.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/bluelip.jpg
--------------------------------------------------------------------------------
/bluelip2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/bluelip2.jpg
--------------------------------------------------------------------------------
/example_01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/example_01.jpg
--------------------------------------------------------------------------------
/example_02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/example_02.jpg
--------------------------------------------------------------------------------
/example_03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/example_03.jpg
--------------------------------------------------------------------------------
/face_makeup_cvpr09_lowres.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/face_makeup_cvpr09_lowres.pdf
--------------------------------------------------------------------------------
/face_parts.py:
--------------------------------------------------------------------------------
1 | # USAGE
2 | # python detect_face_parts.py --shape-predictor shape_predictor_68_face_landmarks.dat --image images/example_01.jpg
3 |
4 | # import the necessary packages
5 | from imutils import face_utils
6 | import numpy as np
7 | import argparse
8 | import imutils
9 | import dlib
10 | import cv2
11 |
12 |
13 | # initialize dlib's face detector (HOG-based) and then create
14 | # the facial landmark predictor
15 | detector = dlib.get_frontal_face_detector()
16 | predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
17 |
18 |
19 | def find_mask(image, betamap):
20 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
21 | # detect faces in the grayscale image
22 | rects = detector(gray, 1)
23 |
24 | # loop over the face detections
25 | for (i, rect) in enumerate(rects):
26 | # determine the facial landmarks for the face region, then
27 | # convert the landmark (x, y)-coordinates to a NumPy array
28 | shape = predictor(gray, rect)
29 | shape = face_utils.shape_to_np(shape)
30 |
31 | # loop over the face parts individually
32 | mask = np.zeros(image.shape, dtype=image.dtype)
33 | noseMask = np.zeros(image.shape, dtype=image.dtype)
34 | for (name, (i, j)) in face_utils.FACIAL_LANDMARKS_IDXS.items():
35 |
36 | clone = image.copy()
37 | cv2.putText(clone, name, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
38 | 0.7, (0, 0, 255), 2)
39 |
40 | if(betamap):
41 | if(name=='right_eyebrow' or name=='left_eyebrow'):
42 | continue
43 | if(name=='jaw'):
44 | continue
45 | else:
46 | if(name=='jaw' or name=='nose' or name=='left_eyebrow' or name=='right_eyebrow'):
47 | continue
48 | pts = shape[i:j]
49 | hull = cv2.convexHull(pts)
50 | if(name=='nose'):
51 | cv2.drawContours(noseMask, [hull], -1, (255,255,255), -1)
52 | else:
53 | cv2.drawContours(mask, [hull], -1, (255,255,255), -1)
54 |
55 | if(betamap):
56 | kernel = np.ones((5,5),np.uint8)
57 | dilation = cv2.dilate(mask,kernel,iterations = 4)
58 | gradient = cv2.morphologyEx(noseMask, cv2.MORPH_GRADIENT, kernel)
59 | gradient = cv2.dilate(gradient,kernel,iterations = 2)
60 | #cv2.imshow("Mask", mask)
61 | #cv2.imshow("Dilated Mask", dilation)
62 | #cv2.imshow("Nose Mask", gradient)
63 | #cv2.waitKey(0)
64 | #cv2.destroyAllWindows()
65 | mask = dilation+gradient
66 |
67 |
68 | return mask
69 |
70 | if __name__ == '__main__':
71 | image = cv2.imread('subject.jpg', 1)
72 | image = imutils.resize(image, width=500)
73 | find_mask(image, False)
--------------------------------------------------------------------------------
/facial_landmark.py:
--------------------------------------------------------------------------------
1 | # USAGE
2 | # python facial_landmarks.py --shape-predictor shape_predictor_68_face_landmarks.dat --image images/example_01.jpg
3 |
4 | # import the necessary packages
5 | from imutils import face_utils
6 | import numpy as np
7 | import argparse
8 | import skin_detector
9 | import imutils
10 | import dlib
11 | import cv2
12 | import manual_select
13 |
14 | def draw_delaunay(img, subdiv, delaunay_color) :
15 | triangleList = subdiv.getTriangleList()
16 | size = img.shape
17 | r = (0, 0, size[1], size[0])
18 | for t in triangleList :
19 | pt1 = (t[0], t[1])
20 | pt2 = (t[2], t[3])
21 | pt3 = (t[4], t[5])
22 | cv2.line(img, pt1, pt2, delaunay_color, 1, cv2.LINE_AA, 0)
23 | cv2.line(img, pt2, pt3, delaunay_color, 1, cv2.LINE_AA, 0)
24 | cv2.line(img, pt3, pt1, delaunay_color, 1, cv2.LINE_AA, 0)
25 |
26 | gmin = -1
27 | def findtop(img, coord):
28 | global gmin
29 | mask = skin_detector.process(img)
30 | #cv2.imshow('mask', mask)
31 | #cv2.waitKey(0)
32 | #cv2.destroyAllWindows()
33 | x, y = coord
34 | y = y-10
35 | if(gmin == -1):
36 | gmin = y+1
37 | else:
38 | if(gmin+20=15 or mask.item(y,x)==0):
48 | break
49 | gmin = y
50 | #cv2.imshow('img', img)
51 | #cv2.waitKey(0)
52 | return (x,y)
53 |
54 |
55 | def triangulate(image):
56 | global gmin
57 |
58 | detector = dlib.get_frontal_face_detector()
59 | predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
60 |
61 | # load the input image, resize it, and convert it to grayscale
62 | image = imutils.resize(image, width=500)
63 | img = image.copy() # used in manual editing
64 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
65 |
66 | # detect faces in the grayscale image
67 | rects = detector(gray, 1)
68 |
69 | # loop over the face detections
70 | for (i, rect) in enumerate(rects):
71 | # determine the facial landmarks for the face region, then convert the facial landmark (x, y)-coordinates to a NumPy array
72 | shape = predictor(gray, rect)
73 | shape = face_utils.shape_to_np(shape)
74 |
75 | # convert dlib's rectangle to a OpenCV-style bounding box [i.e., (x, y, w, h)], then draw the face bounding box
76 | (x, y, w, h) = face_utils.rect_to_bb(rect)
77 | cx = int(.15*w)
78 | cy = int(.5*h)
79 | #cv2.rectangle(image, (x-cx, y-cx), (x+w+cx, y+h+cx), (0, 255, 0), 2)
80 | #cv2.imshow('x', image)
81 | #cv2.waitKey(0)
82 |
83 | # show the face number
84 | #cv2.putText(image, "Face #{}".format(i + 1), (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
85 | #rect = (0, 0, image.shape[1], image.shape[0])
86 | subdiv = cv2.Subdiv2D((max(x-cx,0), max(y-cy,0), min(w+x+cx,image.shape[1]), min(h+y+cx,image.shape[0])))
87 | forehead = []
88 | gmin = -1
89 | for num, (x, y) in enumerate(shape):
90 |
91 |
92 | if((num>=17 and num<=27)):
93 | forehead.append(findtop(image, (x,y)))
94 |
95 | cv2.circle(image, (x, y), 1, (0, 0, 255), -1)
96 | #cv2.imshow('show', image)
97 | #cv2.waitKey(0)
98 |
99 | for item in forehead:
100 | shape = np.vstack((shape, item))
101 | #print(item)
102 |
103 | #manual_select.edit_points(img, shape)
104 |
105 |
106 | for (x, y) in shape:
107 | subdiv.insert((int(x),int(y)))
108 | #draw_delaunay(image, subdiv, (255, 255, 255))
109 | #cv2.imshow("Output", image)
110 | #cv2.waitKey(0)
111 |
112 | return shape, subdiv.getTriangleList()
113 |
114 | if __name__ == '__main__':
115 | image = cv2.imread('target.jpg')
116 | triangulate(image)
--------------------------------------------------------------------------------
/input.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/input.jpg
--------------------------------------------------------------------------------
/lip.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | import cv2
4 | import numpy as np
5 | from imutils import face_utils
6 | from math import e, sqrt, pi
7 |
8 | import warp
9 | import imutils
10 | import dlib
11 | import makeup
12 |
13 | def lip_makeup(subject, warped_target):
14 |
15 | gray_sub = cv2.cvtColor(subject, cv2.COLOR_BGR2GRAY)
16 |
17 |
18 | detector = dlib.get_frontal_face_detector()
19 | predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
20 |
21 | # detect faces in the grayscale image
22 | rects = detector(gray_sub, 1)
23 |
24 | upperlip_ind = [48, 49, 50, 51, 52, 53, 54, 64, 63, 62, 61, 60]
25 | lowerlip_ind = [48, 60, 67, 66, 65, 64, 53, 55, 56, 57, 58, 59]
26 | lip_pts = []
27 |
28 |
29 | lip_map = np.zeros(subject.shape, dtype=subject.dtype)
30 |
31 | # loop over the face detections
32 | for (i, rect) in enumerate(rects):
33 | # determine the facial landmarks for the face region, then
34 | # convert the landmark (x, y)-coordinates to a NumPy array
35 | shape = predictor(gray_sub, rect)
36 | shape = face_utils.shape_to_np(shape)
37 | for x in range (48, 62):
38 | lip_pts.append(shape[x])
39 | C2 = cv2.convexHull(np.array(lip_pts))
40 | cv2.drawContours(lip_map, [C2], -1, (255, 255, 255), -1)
41 |
42 | lip_pts = []
43 | for x in range (60, 67):
44 | lip_pts.append(shape[x])
45 | C2 = cv2.convexHull(np.array(lip_pts))
46 | cv2.drawContours(lip_map, [C2], -1, (0, 0, 0), -1)
47 |
48 | #cv2.imshow('s', subject)
49 | #cv2.imshow('t', warped_target)
50 | #cv2.imshow('lip map', lip_map)
51 | #cv2.imwrite('add', np.where(not lip_map[:] == [0, 0, 0], lip_map, subject))
52 | overlay = subject.copy()
53 | overlay = np.where(lip_map != [0, 0, 0], lip_map, overlay)
54 | #cv2.imshow('overlay', overlay)
55 |
56 | #cv2.waitKey(0)
57 | #cv2.destroyAllWindows()
58 |
59 |
60 |
61 | l_E , _, _ = cv2.split(cv2.cvtColor(warped_target, cv2.COLOR_BGR2LAB))
62 | l_I , _, _ = cv2.split(cv2.cvtColor(subject, cv2.COLOR_BGR2LAB))
63 |
64 | print('Histogram remapping for reference image \'E\' ...')
65 |
66 | l_E_sum = 0
67 | l_E_sumsq = 0
68 | l_I_sum = 0
69 | l_I_sumsq = 0
70 | lip_pts = []
71 | for y in range(0, lip_map.shape[0]):
72 | for x in range(0, lip_map.shape[1]):
73 | # print(lip_map[y][x])
74 | if (lip_map[y][x][2] != 0):
75 | l_E_sum += l_E[y, x] #calculating mean for only lip area
76 | l_E_sumsq += l_E[y, x]**2 #calculating var for only lip area
77 | l_I_sum += l_I[y, x] #calculating mean for only lip area
78 | l_I_sumsq += l_I[y, x]**2 #calculating var for only lip area
79 | lip_pts.append([y, x])
80 |
81 | print(len(lip_pts))
82 |
83 | l_E_mean = l_E_sum / len(lip_pts)
84 | l_I_mean = l_I_sum / len(lip_pts)
85 |
86 | l_E_std = sqrt((l_E_sumsq / len(lip_pts)) - l_E_mean**2)
87 | l_I_std = sqrt((l_I_sumsq / len(lip_pts)) - l_I_mean**2)
88 |
89 | l_E = (l_I_std / l_E_std * (l_E - l_E_mean)) + l_I_mean # fit the hostogram of source to match target(imgI) Luminance remapping
90 |
91 |
92 |
93 | def Gauss(x):
94 | return e ** (-0.5 * float(x))
95 |
96 | M = cv2.cvtColor(subject, cv2.COLOR_BGR2LAB)
97 | warped_target_LAB = cv2.cvtColor(warped_target, cv2.COLOR_BGR2LAB)
98 | counter = 0
99 |
100 | sample = lip_pts.copy()
101 | random.shuffle(sample)
102 | avg_maxval = 0
103 | for p in lip_pts:
104 | q_tilda = 0
105 | maxval = -1
106 | counter += 1
107 | print(counter / len(lip_pts) * 100, " %")
108 | for i in range(0, 500):
109 | q = sample[i]
110 | curr = (Gauss(((p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2) / 5) * Gauss(((float(l_E[q[0]][q[1]]) - float(l_I[p[0]][p[1]])) / 255) ** 2))
111 | if maxval < curr:
112 | maxval = curr
113 | q_tilda = q
114 | if maxval >= 0.9:
115 | break
116 |
117 | avg_maxval += maxval
118 | print("max = ", maxval)
119 | M[p[0]][p[1]] = warped_target_LAB[q_tilda[0]][q_tilda[1]]
120 | #cv2.imshow('M', cv2.cvtColor(M, cv2.COLOR_LAB2BGR))
121 | print("avgmax = ", avg_maxval/len(lip_pts))
122 |
123 | output = cv2.cvtColor(subject.copy(), cv2.COLOR_BGR2LAB)
124 | for p in lip_pts:
125 | output[p[0]][p[1]][1] = M[p[0]][p[1]][1]
126 | output[p[0]][p[1]][2] = M[p[0]][p[1]][2]
127 |
128 | output = cv2.cvtColor(output, cv2.COLOR_LAB2BGR)
129 | #cv2.imshow('out', output)
130 | #cv2.waitKey(0)
131 | #cv2.imwrite('LipOut.jpg', output)
132 | return output, lip_map
133 |
134 | if __name__ == '__main__':
135 | subject = cv2.imread('subject.jpg', 1)
136 | target = cv2.imread('bluelip2.jpg', 1)
137 | subject = imutils.resize(subject, width=500)
138 | target = imutils.resize(target, width=500)
139 | sub, warped_tar = makeup.warp_target(subject, target)
140 | lip_makeup(sub, warped_tar)
141 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import flood
4 | import faces
5 |
6 | img = cv2.imread('subject.jpg',1)
7 | fs = faces.boundingrect(img)
8 | for (x,y,w,h) in fs:
9 | img2 = img[y:y+h, x:x+w]
10 | flood.floodfill(img2)
11 |
12 |
--------------------------------------------------------------------------------
/makeup.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import numpy as np
3 | import warp
4 | import imutils
5 | import face_parts
6 | import lip
7 | import overlay_blur
8 |
9 | def decompose(img):
10 | base = cv2.bilateralFilter(img, 9, 75,75)
11 | return base, img-base
12 |
13 | def warp_target(subject, target):
14 |
15 | if(target.shape[0]>subject.shape[0]):
16 | print('bigger target')
17 | new_subject = np.zeros((target.shape[0]-subject.shape[0],subject.shape[1],3), dtype=subject.dtype)
18 | subject = np.vstack((subject, new_subject))
19 | else:
20 | print('bigger subject')
21 | #resizing target
22 | new_target = np.zeros((subject.shape[0]-target.shape[0],target.shape[1],3), dtype=target.dtype)
23 | target = np.vstack((target, new_target))
24 |
25 | if(subject.shape[0]%2!=0):
26 | zero_layer = np.zeros((1, target.shape[1],3), dtype=target.dtype)
27 | target = np.vstack((target, zero_layer))
28 | subject = np.vstack((subject, zero_layer))
29 |
30 | #cv2.imshow('s', subject)
31 | #cv2.imshow('t', target)
32 | #cv2.waitKey(0)
33 |
34 | warped_target = warp.warp(target, subject)
35 | #cv2.imshow('new', warped_target)
36 | #cv2.waitKey(0)
37 | #cv2.destroyAllWindows()
38 |
39 | return subject, warped_target
40 |
41 | def apply_makeup(subject, warped_target):
42 | zeros = np.zeros(warped_target.shape, dtype=warped_target.dtype)
43 | ones = np.ones(warped_target.shape, dtype=warped_target.dtype)
44 | face_mask = np.where(warped_target==[0,0,0], zeros, ones*255)
45 | cv2.imshow('mask', face_mask)
46 | cv2.waitKey(0)
47 | cv2.destroyAllWindows()
48 |
49 | sub_lab = cv2.cvtColor(subject, cv2.COLOR_BGR2LAB)
50 | tar_lab = cv2.cvtColor(warped_target, cv2.COLOR_BGR2LAB)
51 |
52 | sl, sa, sb = cv2.split(sub_lab)
53 | tl, ta, tb = cv2.split(tar_lab)
54 |
55 | face_struct_s, skin_detail_s = decompose(sl)
56 | face_struct_t, skin_detail_t = decompose(tl)
57 |
58 | #color transfer
59 | gamma = .8
60 | '''
61 | type = sa.dtype
62 | sa.dtype = float
63 | ta.dtype = float
64 | sb.dtype = float
65 | tb.dtype = float
66 | '''
67 | type = sa.dtype
68 | ra = np.where(True, sa*(1-gamma)+ta*gamma, zeros[:,:,0])
69 | rb = np.where(True, sb*(1-gamma)+tb*gamma, zeros[:,:,0])
70 | ra = ra.astype(type)
71 | rb = rb.astype(type)
72 | #print(ra.shape)
73 | ra = cv2.bitwise_and(ra,ra,mask = face_mask[:,:,0])
74 | rb = cv2.bitwise_and(rb,rb,mask = face_mask[:,:,0])
75 |
76 |
77 |
78 | #skin-detail transfer
79 | gammaI = 0
80 | gammaE = 1
81 | skin_detail_r = np.where(True, skin_detail_s*gammaI + skin_detail_t*gammaE, zeros[:,:,0])
82 | skin_detail_r = skin_detail_r.astype(type)
83 |
84 |
85 | #Work on the base layer
86 | fp_mask = face_parts.find_mask(subject, True)
87 | src_gauss = cv2.pyrDown(face_struct_s)
88 | src_lapla = face_struct_s - cv2.pyrUp(src_gauss)
89 | dst_gauss = cv2.pyrDown(face_struct_t)
90 | dst_lapla = face_struct_t - cv2.pyrUp(dst_gauss)
91 | face_struct_r = np.where(face_mask[:,:,0]==0, face_struct_s, dst_lapla + cv2.pyrUp(src_gauss))
92 | #cv2.imshow('transfering target', face_struct_r)
93 | #cv2.waitKey(0)
94 | face_struct_r = np.where(fp_mask[:,:,0]==255, face_struct_s, face_struct_r)
95 |
96 | #cv2.imshow('mask', fp_mask)
97 | #cv2.imshow('transfering target', face_struct_r)
98 | #cv2.imshow('keeping src', face_struct_s)
99 | #cv2.imshow('diff', face_struct_s - face_struct_r)
100 | cv2.waitKey(0)
101 |
102 | rl = face_struct_r+skin_detail_r
103 | rl = cv2.bitwise_and(rl,rl,mask = face_mask[:,:,0])
104 |
105 | res_lab = cv2.merge((rl, ra, rb))
106 | res = cv2.cvtColor(res_lab, cv2.COLOR_LAB2BGR)
107 |
108 | fp_mask = face_parts.find_mask(subject, False)
109 | res = cv2.bitwise_and(res,res,mask = face_mask[:,:,0])
110 | res = np.where(face_mask==[0,0,0], subject, res)
111 | res = np.where(fp_mask==[255,255,255], subject, res)
112 |
113 |
114 | #apply lip makeup
115 | M, lip_map = lip.lip_makeup(subject, warped_target)
116 | res = np.where(lip_map==[255,255,255], M, res)
117 |
118 | cv2.imshow('old', res)
119 | cv2.waitKey(0)
120 |
121 | res = overlay_blur.overlay(subject, res, face_mask[:,:,0])
122 |
123 | cv2.imshow('res', res)
124 | cv2.imwrite('res.jpg', res)
125 | cv2.waitKey(0)
126 | cv2.destroyAllWindows()
127 |
128 | if __name__ == '__main__':
129 | subject = cv2.imread('subject.jpg', 1)
130 | target = cv2.imread('target.jpg', 1)
131 | subject = imutils.resize(subject, width=500)
132 | target = imutils.resize(target, width=500)
133 | sub, warped_tar = warp_target(subject, target)
134 | apply_makeup(sub, warped_tar)
--------------------------------------------------------------------------------
/manual_select.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import dlib
3 | import imutils
4 | import numpy as np
5 | from imutils import face_utils
6 |
7 |
8 | def draw_points(img, shape):
9 | image = img.copy()
10 | for num, (x, y) in enumerate(shape):
11 | cv2.circle(image, (x, y), 1, (0, 0, 255), -1)
12 | txt = ("(" + str(num) + ")")
13 | cv2.putText(image, txt, (x, y), cv2.FONT_HERSHEY_PLAIN, 1, (255, 0, 0), 1, cv2.LINE_AA)
14 | return image
15 |
16 | def get_curr_location(event, x, y, flags, param):
17 | shape, ind = param
18 | if event == cv2.EVENT_LBUTTONUP:
19 | shape[ind] = x, y
20 |
21 | #This function returns the changed value of shape
22 | def edit_points(img, shape):
23 | print("Help:\n\t(*)type \'exit\' to stop editing\n\t(*)\'del\' to remove a point")
24 | while True:
25 | cv2.namedWindow('Edit')
26 | cv2.imshow("Edit", draw_points(img, shape))
27 | cv2.waitKey(200)
28 | cmd = input("Enter command: ")
29 | cv2.destroyAllWindows()
30 | if (cmd == 'exit'):
31 | cv2.destroyAllWindows()
32 | break
33 | elif (cmd == 'del'):
34 | ind = int(input("Enter index to delete: "))
35 | shape[ind] = (0, 0)
36 | cv2.destroyAllWindows()
37 | cv2.namedWindow('Edit')
38 | cv2.imshow("Edit", draw_points(img, shape))
39 | cv2.setMouseCallback("Edit", get_curr_location, param=(shape, ind))
40 | print("Now select the point by clicking on the image and press \'esc\' to confirm")
41 | while True:
42 | cv2.imshow("Edit", draw_points(img, shape))
43 | if cv2.waitKey(20) & 0xFF == 27:
44 | cv2.destroyAllWindows()
45 | break
46 |
47 |
48 | return shape
49 |
50 | if __name__ == '__main__':
51 | subject = cv2.imread('subject.jpg', 1)
52 | target = cv2.imread('target.jpg', 1)
53 | subject = imutils.resize(subject, width=500)
54 | target = imutils.resize(target, width=500)
55 | gray_sub = cv2.cvtColor(subject, cv2.COLOR_BGR2GRAY)
56 |
57 | detector = dlib.get_frontal_face_detector()
58 | predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
59 |
60 | # detect faces in the grayscale image
61 | rects = detector(gray_sub, 1)
62 |
63 | # loop over the face detections
64 | for (i, rect) in enumerate(rects):
65 | # determine the facial landmarks for the face region, then
66 | # convert the landmark (x, y)-coordinates to a NumPy array
67 | shape = predictor(gray_sub, rect)
68 | shape = face_utils.shape_to_np(shape)
69 | new_shape = edit_points(subject, shape)
70 |
71 | cv2.imshow("Final", draw_points(subject, new_shape))
72 | cv2.waitKey(0)
73 | cv2.destroyAllWindows()
74 |
75 |
--------------------------------------------------------------------------------
/out1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/out1.jpg
--------------------------------------------------------------------------------
/overlay_blur.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import imutils
3 | import numpy as np
4 | import warp
5 | import makeup
6 |
7 | def overlay(orig, makeup, mask):
8 |
9 | blur_mask = cv2.blur(mask, (20, 20))
10 | new = makeup.copy()
11 | for y in range(0, orig.shape[0]):
12 | for x in range(0, orig.shape[1]):
13 | w = blur_mask[y][x]/255
14 | if (w > 0.6):
15 | w = (w - 0.6) / 0.4
16 | else:
17 | w = 0
18 | new[y][x] = makeup[y][x]*w + orig[y][x]*(1 - w)
19 |
20 |
21 | return new
22 |
23 |
24 | if __name__ == '__main__':
25 | subject = cv2.imread('subject.jpg', 1)
26 | target = cv2.imread('bluelip2.jpg', 1)
27 | subject = imutils.resize(subject, width=500)
28 | target = imutils.resize(target, width=500)
29 | sub, warped_tar = makeup.warp_target(subject, target)
30 | zeros = np.zeros(warped_tar.shape, dtype=warped_tar.dtype)
31 | ones = np.ones(warped_tar.shape, dtype=warped_tar.dtype)
32 | face_mask = np.where(warped_tar==[0,0,0], zeros, ones*255)
33 | res = cv2.imread('res.jpg', 1)
34 |
35 | new = overlay(subject, res, face_mask[:,:,0])
36 | cv2.imshow('new', new)
37 | cv2.waitKey(0)
38 | cv2.destroyAllWindows()
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/res.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/res.jpg
--------------------------------------------------------------------------------
/result_with_masking_all.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/result_with_masking_all.jpg
--------------------------------------------------------------------------------
/result_with_masking_all_but_nose.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/result_with_masking_all_but_nose.jpg
--------------------------------------------------------------------------------
/result_with_no_transfer.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/result_with_no_transfer.jpg
--------------------------------------------------------------------------------
/skin_detector.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | __author__ = 'Will Brennan'
4 |
5 | # Built-in Modules
6 | import logging
7 | import cv2
8 | import numpy
9 | import scripts
10 |
11 | logger = logging.getLogger('main')
12 |
13 |
14 | def get_hsv_mask(img, debug=False):
15 | assert isinstance(img, numpy.ndarray), 'image must be a numpy array'
16 | assert img.ndim == 3, 'skin detection can only work on color images'
17 | logger.debug('getting hsv mask')
18 |
19 | lower_thresh = numpy.array([0, 50, 0], dtype=numpy.uint8)
20 | upper_thresh = numpy.array([120, 150, 255], dtype=numpy.uint8)
21 | img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
22 | msk_hsv = cv2.inRange(img_hsv, lower_thresh, upper_thresh)
23 |
24 | #msk_hsv[msk_hsv < 128] = 0
25 | #msk_hsv[msk_hsv >= 128] = 1
26 |
27 | if debug:
28 | scripts.display('input', img)
29 | scripts.display('mask_hsv', msk_hsv)
30 |
31 | return msk_hsv.astype(float)
32 |
33 |
34 | def get_rgb_mask(img, debug=False):
35 | assert isinstance(img, numpy.ndarray), 'image must be a numpy array'
36 | assert img.ndim == 3, 'skin detection can only work on color images'
37 | logger.debug('getting rgb mask')
38 |
39 | lower_thresh = numpy.array([45, 52, 108], dtype=numpy.uint8)
40 | upper_thresh = numpy.array([255, 255, 255], dtype=numpy.uint8)
41 |
42 | mask_a = cv2.inRange(img, lower_thresh, upper_thresh)
43 | mask_b = 255 * ((img[:, :, 2] - img[:, :, 1]) / 20)
44 | mask_c = 255 * ((numpy.max(img, axis=2) - numpy.min(img, axis=2)) / 20)
45 |
46 | mask_a = mask_a.astype(float)
47 | print(mask_a.dtype)
48 | print(mask_b.dtype)
49 | print(mask_c.dtype)
50 |
51 | msk_rgb = cv2.bitwise_and(mask_a, mask_b)
52 | msk_rgb = cv2.bitwise_and(mask_c, msk_rgb)
53 |
54 | msk_rgb[msk_rgb < 128] = 0
55 | msk_rgb[msk_rgb >= 128] = 255
56 |
57 | if debug:
58 | scripts.display('input', img)
59 | scripts.display('mask_rgb', msk_rgb)
60 |
61 | return msk_rgb.astype(float)
62 |
63 |
64 | def get_ycrcb_mask(img, debug=False):
65 | assert isinstance(img, numpy.ndarray), 'image must be a numpy array'
66 | assert img.ndim == 3, 'skin detection can only work on color images'
67 | logger.debug('getting ycrcb mask')
68 |
69 | lower_thresh = numpy.array([90, 100, 130], dtype=numpy.uint8)#90
70 | upper_thresh = numpy.array([230, 120, 180], dtype=numpy.uint8)#230
71 |
72 | img_ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCR_CB)
73 | msk_ycrcb = cv2.inRange(img_ycrcb, lower_thresh, upper_thresh)
74 |
75 | #msk_ycrcb[msk_ycrcb < 128] = 0
76 | #msk_ycrcb[msk_ycrcb >= 128] = 1
77 |
78 | if debug:
79 | scripts.display('input', img)
80 | scripts.display('mask_ycrcb', msk_ycrcb)
81 |
82 | return msk_ycrcb.astype(float)
83 |
84 |
85 | def grab_cut_mask(img_col, mask, debug=False):
86 | assert isinstance(img_col, numpy.ndarray), 'image must be a numpy array'
87 | assert isinstance(mask, numpy.ndarray), 'mask must be a numpy array'
88 | assert img_col.ndim == 3, 'skin detection can only work on color images'
89 | assert mask.ndim == 2, 'mask must be 2D'
90 |
91 | kernel = numpy.ones((50, 50), numpy.float32) / (50 * 50)
92 | dst = cv2.filter2D(mask, -1, kernel)
93 | dst[dst != 0] = 255
94 | free = numpy.array(cv2.bitwise_not(dst), dtype=numpy.uint8)
95 |
96 | if debug:
97 | scripts.display('not skin', free)
98 | scripts.display('grabcut input', mask)
99 |
100 | grab_mask = numpy.zeros(mask.shape, dtype=numpy.uint8)
101 | grab_mask[:, :] = 2
102 | grab_mask[mask == 255] = 1
103 | grab_mask[free == 255] = 0
104 |
105 | if numpy.unique(grab_mask).tolist() == [0, 1]:
106 | logger.debug('conducting grabcut')
107 | bgdModel = numpy.zeros((1, 65), numpy.float64)
108 | fgdModel = numpy.zeros((1, 65), numpy.float64)
109 |
110 | if img_col.size != 0:
111 | mask, bgdModel, fgdModel = cv2.grabCut(img_col, grab_mask, None, bgdModel, fgdModel, 5,
112 | cv2.GC_INIT_WITH_MASK)
113 | mask = numpy.where((mask == 2) | (mask == 0), 0, 1).astype(numpy.uint8)
114 | else:
115 | logger.warning('img_col is empty')
116 |
117 | return mask
118 |
119 |
120 | def closing(mask):
121 | assert isinstance(mask, numpy.ndarray), 'mask must be a numpy array'
122 | assert mask.ndim == 2, 'mask must be a greyscale image'
123 | logger.debug("closing mask of shape {0}".format(mask.shape))
124 |
125 | kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
126 | mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
127 | kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
128 | mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)
129 |
130 | return mask
131 |
132 |
133 | def process(img, thresh=0.5, debug=False):
134 | assert isinstance(img, numpy.ndarray), 'image must be a numpy array'
135 | assert img.ndim == 3, 'skin detection can only work on color images'
136 | logger.debug("processing image of shape {0}".format(img.shape))
137 |
138 | mask_hsv = get_hsv_mask(img, debug=debug)
139 | mask_rgb = get_rgb_mask(img, debug=debug)
140 | mask_ycrcb = get_ycrcb_mask(img, debug=debug)
141 |
142 | n_masks = 3.0
143 | #cv2.imshow('1',mask_hsv)
144 | #cv2.imshow('2',mask_rgb)
145 | #cv2.imshow('3',mask_ycrcb)
146 | mask = numpy.where((mask_hsv + mask_rgb + mask_ycrcb)/3>255/2,255,0)
147 | #cv2.imshow('4',mask)
148 | #cv2.waitKey(0)
149 | #cv2.destroyAllWindows()
150 |
151 | #mask[mask < thresh] = 0.0
152 | #mask[mask >= thresh] = 255.0
153 | logger.debug('{0}% of the image is skin'.format(int((100.0 / 255.0) * numpy.sum(mask) / mask.size)))
154 |
155 | mask = mask.astype(numpy.uint8)
156 | mask = closing(mask)
157 | mask = grab_cut_mask(img, mask, debug=debug)
158 | kernel = numpy.ones((5,5),numpy.uint8)
159 | mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=11)
160 | #mask = cv2.erode(mask,kernel,iterations = 1)
161 | return mask
162 |
--------------------------------------------------------------------------------
/subject.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/subject.jpg
--------------------------------------------------------------------------------
/target.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheMathWizard/Face-Makeup-by-Example/b28801f29d989469cb30a760312dd918ae1f0e66/target.jpg
--------------------------------------------------------------------------------
/warp.py:
--------------------------------------------------------------------------------
1 | import cv2
2 | import facial_landmark
3 | import imutils
4 | import numpy as np
5 |
6 | def warp(src, dst):
7 | src_points, src_triangles = facial_landmark.triangulate(src)
8 | dst_points, dst_triangles = facial_landmark.triangulate(dst)
9 | warped_image = np.zeros(src.shape, dtype=np.uint8)
10 |
11 | for i,t in enumerate(dst_triangles):
12 | pt1 = (t[0], t[1])
13 | pt2 = (t[2], t[3])
14 | pt3 = (t[4], t[5])
15 | first, second, third = -1,-1,-1
16 | for i2, (x,y) in enumerate(dst_points):
17 | if(x==t[0] and y==t[1]):
18 | #print('triangle '+(i)+' has first point at '+str(i2))
19 | #print(x,y)
20 | #cv2.circle(src,(x,y), 1, (i*2,0,i*3), -1)
21 | first = i2
22 | if(x==t[2] and y==t[3]):
23 | #print('triangle '+str(i)+' has second point at '+str(i2))
24 | #print(x,y)
25 | #cv2.circle(src,(x,y), 1, (i*2,0,i*3), -1)
26 | second = i2
27 | if(x==t[4] and y==t[5]):
28 | #print('triangle '+str(i)+' has third point at '+str(i2))
29 | #print(x,y)
30 | #cv2.circle(src,(x,y), 1, (i*2,0,i*3), -1)
31 | third = i2
32 | if(first>=0 and second>=0 and third>=0):
33 | x1,y1 = src_points[first]
34 | x2,y2 = src_points[second]
35 | x3,y3 = src_points[third]
36 | dx1,dy1 = dst_points[first]
37 | dx2,dy2 = dst_points[second]
38 | dx3,dy3 = dst_points[third]
39 |
40 | #creating mask in destination image
41 | mask = np.zeros(src.shape, dtype=np.uint8)
42 | roi_corners = np.array([[dx1,dy1],[dx2,dy2],[dx3,dy3]], dtype=np.int32)
43 | cv2.fillPoly(mask, [roi_corners], (255,255,255))
44 |
45 | #warping src image to destination image
46 | pts1 = np.float32([[x1,y1],[x2,y2],[x3,y3]])
47 | pts2 = np.float32([[dx1,dy1],[dx2,dy2],[dx3,dy3]])
48 | M = cv2.getAffineTransform(pts1,pts2)
49 | rows,cols,ch = src.shape
50 | res = cv2.warpAffine(src,M,(cols,rows))
51 |
52 | warped_image = cv2.bitwise_or(warped_image,cv2.bitwise_and(mask,res))
53 | #cv2.imshow('masked_image', warped_image)
54 | #cv2.waitKey(0)
55 | '''
56 | cv2.line(src, pt1, pt2, (255, 255, 255), 1, cv2.LINE_AA, 0)
57 | cv2.line(src, pt2, pt3, (255, 255, 255), 1, cv2.LINE_AA, 0)
58 | cv2.line(src, pt3, pt1, (255, 255, 255), 1, cv2.LINE_AA, 0)
59 | '''
60 | #cv2.imshow('res',warped_image)
61 | #cv2.waitKey(0)
62 | return warped_image
63 |
64 | if __name__ == '__main__':
65 | src = cv2.imread('example_01.jpg', 1)
66 | dst = cv2.imread('example_02.jpg', 1)
67 | src = imutils.resize(src, width=500)
68 | dst = imutils.resize(dst, width=500)
--------------------------------------------------------------------------------