From 9931d9921a92b6c760d082cbc95b92bacace1a72 Mon Sep 17 00:00:00 2001 From: Travor Liu Date: Sun, 28 Jul 2019 11:34:21 +0800 Subject: [PATCH] Allow using pictures as decorations * face_decor.py: Only preserve decorating eyebrows * rotate.py: Demonstration of rotating pictures * pic_on_pic.py: Test of putting 4-channel pictures on RGB background * thuglife_resized.png: Square version of Thug Life's sunglasses, in order to avoid stripping in rotation * face_thuglife.py: Created. To put sunglasses in either pictures or in the view of webcam --- face_decor.py | 7 ++-- face_thuglife.py | 95 +++++++++++++++++++++++++++++++++++++++++++ pic_on_pic.py | 33 +++++++++++++++ rotate.py | 26 ++++++++++++ thuglife_resized.png | Bin 0 -> 3465 bytes 5 files changed, 158 insertions(+), 3 deletions(-) create mode 100755 face_thuglife.py create mode 100755 pic_on_pic.py create mode 100755 rotate.py create mode 100644 thuglife_resized.png diff --git a/face_decor.py b/face_decor.py index 73ac67c..78f51a7 100755 --- a/face_decor.py +++ b/face_decor.py @@ -19,9 +19,10 @@ def do_decor(frame): top_lip=np.array(face_landmarks["top_lip"],np.int32) bottom_lip=np.array(face_landmarks["bottom_lip"],np.int32) frame=cv2.fillPoly(frame,[k*eyebrow_l,k*eyebrow_r],(0xFF,0xFF,0xFF)) - frame=cv2.fillPoly(frame,[k*nose],(0,0x80,0xFF)) - frame=cv2.fillPoly(frame,[k*bottom_lip],(0,0,0xFF)) - frame=cv2.fillPoly(frame,[k*top_lip],(0,0xFF,0)) + #frame=cv2.fillPoly(frame,[k*nose],(0,0x80,0xFF)) + mouth_color=(0x3E,0x51,0xFC) + #frame=cv2.fillPoly(frame,[k*bottom_lip],mouth_color) + #frame=cv2.fillPoly(frame,[k*top_lip],mouth_color) return frame while webcam.isOpened(): diff --git a/face_thuglife.py b/face_thuglife.py new file mode 100755 index 0000000..b88a0b3 --- /dev/null +++ b/face_thuglife.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +import numpy as np +import face_recognition as fr +from os.path import exists,splitext +import sys +import cv2 + +thuglife_file="./thuglife_resized.png" +assert(exists(thuglife_file)) +# The second argument cv2.IMREAD_UNCHANGED is extremely important because it +# supports reading pictures using alpha channel +thuglife=cv2.imread("thuglife_resized.png",cv2.IMREAD_UNCHANGED) + +assert(thuglife.shape[2]==4) # Must have alpha channel + +# determines whether to print debug messages in the session or not +debug=False + +k=1 + +def put_sunglasses(frame,pic,mid,angle): + h,w=pic.shape[0:2] + coeff=0.63 + y=int(mid[1]-h/2) + x=int(mid[0]-coeff*w) + M=cv2.getRotationMatrix2D((w/2,h/2),angle,1) + pic=cv2.warpAffine(pic,M,(w,h)) + a,b=y+h,x+w + roi=frame[y:a,x:b] + for i,row in enumerate(pic): + for j,pix in enumerate(row): + try: + alpha=pix[3]/0xFF + vec=np.array([alpha,1-alpha]) + mat=np.array([pix[0:3],roi[i][j]],dtype=np.int8).T + mixed=mat.dot(vec) + assert(len(mixed.shape)==1 and mixed.shape[0]==3) + roi[i][j]=mixed + except IndexError: + continue + frame[y:a,x:b]=roi + return frame + +def mark_eye(frame): + small=cv2.resize(frame,(0,0),fx=1/k,fy=1/k) + rgb_frame=small[:,:,::-1] + all_landmarks=fr.face_landmarks(rgb_frame) + slopes=[] + mids=[] + for i,landmarks in enumerate(all_landmarks): + pt1=np.array(landmarks["left_eye"][0],dtype=np.int32) + pt2=np.array(landmarks["right_eye"][3],dtype=np.int32) + mids.append(np.array(k*(pt1+pt2)/2,dtype=np.int32)) + diff=pt2-pt1 + width=int(k*np.sqrt(np.sum(np.square(diff)))) + ratio=width/thuglife.shape[1]*3/2 + slopes.append(diff[1]/diff[0]) + #frame=cv2.line(frame,(pt1[0]*k,pt1[1]*k),(pt2[0]*k,pt2[1]*k), + # (0xFF,0,0),2,8) + #frame=cv2.rectangle(frame,(pt1[0]*k,pt1[1]*k),(pt2[0]*k,pt2[1]*k), + # (0,0xFF,0),2) + pic=cv2.resize(thuglife,(0,0),fx=ratio,fy=ratio) + frame=put_sunglasses(frame,pic,mids[i],-np.arctan(slopes[i])/np.pi*180) + if debug and len(slopes)>0: + print("Slopes: {}".format(slopes)) + print("Angles: {}".format(-np.arctan(slopes)/np.pi*180)) + print("Midpoints: {}".format([m.tolist() for m in mids])) + return frame + +def decor_img(name): + img=cv2.imread(name) + img=mark_eye(img) + out="%s_thuglife.png" % splitext(name)[0] + print("Saved to %s" % out) + cv2.imwrite(out,img) + +argc=len(sys.argv) +if argc>=2: + if not exists(sys.argv[1]): + print("%s: No such file or directory" % sys.argv[1]) + exit(-1) + decor_img(sys.argv[1]) + exit(0) + +webcam=cv2.VideoCapture(0) + +while webcam.isOpened(): + ret,frame=webcam.read() + if ret==False: + break + frame=cv2.flip(frame,1) + frame=mark_eye(frame) + cv2.imshow("Thug Life",frame) + if cv2.waitKey(1) & 0xFF == ord('q'): + break diff --git a/pic_on_pic.py b/pic_on_pic.py new file mode 100755 index 0000000..a75b61f --- /dev/null +++ b/pic_on_pic.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +import cv2 +import numpy as np +import face_recognition as fr + +background=cv2.imread("known_people/Barack_Obama.jpg",cv2.IMREAD_COLOR) +foreground=cv2.imread("thuglife.png",cv2.IMREAD_UNCHANGED) + +[face]=fr.face_locations(background[:,:,::-1]) +top,right,left,bottom=face + +assert(foreground.shape[2]==4) # Assume foreground image has alpha channel + +height=foreground.shape[0] +width=foreground.shape[1] + +a=top +b=left + +c=a+height +d=b+width + +roi=background[a:c,b:d] +for i,row in enumerate(foreground): + for j,pix in enumerate(row): + alpha=pix[3]/0xFF + mixed=pix[0:3]*alpha+roi[i][j]*(1-alpha).astype(np.int8) + roi[i][j]=mixed + +background[a:c,b:d]=roi + +cv2.imshow("Mixed",background) +cv2.waitKey(0) diff --git a/rotate.py b/rotate.py new file mode 100755 index 0000000..763fcc7 --- /dev/null +++ b/rotate.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +import sys +import cv2 +import numpy as np + +argc=len(sys.argv) +if argc<3: + print("usage: %s image_file counter_clockwise" % sys.argv[0]) + exit(-1) + +degree=float(sys.argv[2]) +rad=degree*np.pi/180.0 + +image=cv2.imread(sys.argv[1],cv2.IMREAD_UNCHANGED) + +height,width=image.shape[0:2] # Get the shape + +print("w=%d,h=%d" % (width,height)) + +M=cv2.getRotationMatrix2D((width/2,height/2),degree,1) + + +image=cv2.warpAffine(image,M,(width,height)) + +cv2.imshow("Rotated",image) +cv2.waitKey(0) diff --git a/thuglife_resized.png b/thuglife_resized.png new file mode 100644 index 0000000000000000000000000000000000000000..80d1065487ad211232a325facf846f15f81cfe3d GIT binary patch literal 3465 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 literal 0 HcmV?d00001