├── .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 | 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 | 14 | 15 | 16 | 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 | 117 | 118 | 119 | 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 | 156 | 157 | 158 | 159 | 162 | 163 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 194 | 195 | 211 | 212 | 228 | 229 | 245 | 246 | 262 | 263 | 279 | 280 | 298 | 299 | 317 | 318 | 338 | 339 | 360 | 361 | 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 | 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 | 441 | 444 | 445 | 446 | 448 | 449 | 450 | 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) --------------------------------------------------------------------------------