Catmeow commited on
Commit
23c7824
β€’
1 Parent(s): 2be1c01

Upload 6 files

Browse files
.gitattributes CHANGED
@@ -31,3 +31,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
31
  *.zip filter=lfs diff=lfs merge=lfs -text
32
  *.zst filter=lfs diff=lfs merge=lfs -text
33
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
31
  *.zip filter=lfs diff=lfs merge=lfs -text
32
  *.zst filter=lfs diff=lfs merge=lfs -text
33
  *tfevents* filter=lfs diff=lfs merge=lfs -text
34
+ shape_predictor_5_face_landmarks.dat filter=lfs diff=lfs merge=lfs -text
Example01.jpg ADDED
Example02.jpg ADDED
app.py ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from doctest import Example
2
+ import os
3
+ os.system("pip install dlib")
4
+ import sys
5
+ import face_detection
6
+ import PIL
7
+ from PIL import Image, ImageOps, ImageFile
8
+ import numpy as np
9
+ import cv2 as cv
10
+ import torch
11
+ import gradio as gr
12
+
13
+ torch.set_grad_enabled(False)
14
+
15
+ device = "cuda" if torch.cuda.is_available() else "cpu"
16
+ model = torch.hub.load("bryandlee/animegan2-pytorch:main", "generator", device=device).eval()
17
+ model2 = torch.hub.load("AK391/animegan2-pytorch:main", "generator", pretrained="face_paint_512_v1", device=device).eval()
18
+ face2paint = torch.hub.load("bryandlee/animegan2-pytorch:main", "face2paint", device=device)
19
+ image_format = "png" #@param ["jpeg", "png"]
20
+
21
+ def unsharp_mask(image, kernel_size=(5, 5), sigma=1.0, amount=2.0, threshold=0):
22
+ """Return a sharpened version of the image, using an unsharp mask."""
23
+ blurred = cv.GaussianBlur(image, kernel_size, sigma)
24
+ sharpened = float(amount + 1) * image - float(amount) * blurred
25
+ sharpened = np.maximum(sharpened, np.zeros(sharpened.shape))
26
+ sharpened = np.minimum(sharpened, 255 * np.ones(sharpened.shape))
27
+ sharpened = sharpened.round().astype(np.uint8)
28
+ if threshold > 0:
29
+ low_contrast_mask = np.absolute(image - blurred) < threshold
30
+ np.copyto(sharpened, image, where=low_contrast_mask)
31
+ return sharpened
32
+
33
+ def normPRED(d):
34
+ ma = np.max(d)
35
+ mi = np.min(d)
36
+
37
+ dn = (d-mi)/(ma-mi)
38
+
39
+ return dn
40
+
41
+ def array_to_np(array_in):
42
+ array_in = normPRED(array_in)
43
+ array_in = np.squeeze(255.0*(array_in))
44
+ array_in = np.transpose(array_in, (1, 2, 0))
45
+ return array_in
46
+
47
+ def array_to_image(array_in):
48
+ array_in = normPRED(array_in)
49
+ array_in = np.squeeze(255.0*(array_in))
50
+ array_in = np.transpose(array_in, (1, 2, 0))
51
+ im = Image.fromarray(array_in.astype(np.uint8))
52
+ return im
53
+
54
+
55
+ def image_as_array(image_in):
56
+ image_in = np.array(image_in, np.float32)
57
+ tmpImg = np.zeros((image_in.shape[0],image_in.shape[1],3))
58
+ image_in = image_in/np.max(image_in)
59
+ if image_in.shape[2]==1:
60
+ tmpImg[:,:,0] = (image_in[:,:,0]-0.485)/0.229
61
+ tmpImg[:,:,1] = (image_in[:,:,0]-0.485)/0.229
62
+ tmpImg[:,:,2] = (image_in[:,:,0]-0.485)/0.229
63
+ else:
64
+ tmpImg[:,:,0] = (image_in[:,:,0]-0.485)/0.229
65
+ tmpImg[:,:,1] = (image_in[:,:,1]-0.456)/0.224
66
+ tmpImg[:,:,2] = (image_in[:,:,2]-0.406)/0.225
67
+
68
+ tmpImg = tmpImg.transpose((2, 0, 1))
69
+ image_out = np.expand_dims(tmpImg, 0)
70
+ return image_out
71
+
72
+ # detect a face
73
+ def find_aligned_face(image_in, size=400):
74
+ aligned_image, n_faces, quad = face_detection.align(image_in, face_index=0, output_size=size)
75
+ return aligned_image, n_faces, quad
76
+
77
+ # clip the face, return array
78
+ def align_first_face(image_in, size=400):
79
+ aligned_image, n_faces, quad = find_aligned_face(image_in,size=size)
80
+ if n_faces == 0:
81
+ try:
82
+ image_in = ImageOps.exif_transpose(image_in)
83
+ except:
84
+ print("exif problem, not rotating")
85
+ image_in = image_in.resize((size, size))
86
+ im_array = image_as_array(image_in)
87
+ else:
88
+ im_array = image_as_array(aligned_image)
89
+
90
+ return im_array
91
+
92
+ def img_concat_h(im1, im2):
93
+ dst = Image.new('RGB', (im1.width + im2.width, im1.height))
94
+ dst.paste(im1, (0, 0))
95
+ dst.paste(im2, (im1.width, 0))
96
+ return dst
97
+
98
+ def paintface(img: Image.Image,size: int) -> Image.Image:
99
+ aligned_img = align_first_face(img,size)
100
+ if aligned_img is None:
101
+ output=None
102
+ else:
103
+ im_in = array_to_image(aligned_img).convert("RGB")
104
+ im_out1 = face2paint(model, im_in, side_by_side=False)
105
+ im_out2 = face2paint(model2, im_in, side_by_side=False)
106
+
107
+ output = img_concat_h(im_out1, im_out2)
108
+ return output
109
+
110
+ def generate(img):
111
+ out = paintface(img, 400)
112
+ return out
113
+
114
+
115
+ title = "Face from Photo into paint"
116
+ description = "Upload a photo, this Ai will detect and transfer only the face into cartoon/anime painting style. Good for Avatar painting style."
117
+ article = "None"
118
+
119
+ Example=[['Example01.jpg'],['Example02.jpg']]
120
+
121
+ demo = gr.Interface(
122
+ generate,
123
+ inputs = [gr.Image(type="pil", label="Upload a photo, Ai will detect main face and paint into cartoon style")],
124
+ outputs= [gr.Image(type="pil", label="Output two results from different models")],
125
+ title=title,
126
+ description=description,
127
+ article=article,
128
+ examples=Example,
129
+ enable_queue=True,
130
+ allow_flagging=False
131
+ )
132
+
133
+ demo.launch()
face_detection.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2021 Justin Pinkney
2
+
3
+ import dlib
4
+ import numpy as np
5
+ import os
6
+ from PIL import Image
7
+ from PIL import ImageOps
8
+ from scipy.ndimage import gaussian_filter
9
+ import cv2
10
+
11
+
12
+ MODEL_PATH = "shape_predictor_5_face_landmarks.dat"
13
+ detector = dlib.get_frontal_face_detector()
14
+
15
+
16
+ def align(image_in, face_index=0, output_size=256):
17
+ try:
18
+ image_in = ImageOps.exif_transpose(image_in)
19
+ except:
20
+ print("exif problem, not rotating")
21
+
22
+ landmarks = list(get_landmarks(image_in))
23
+ n_faces = len(landmarks)
24
+ face_index = min(n_faces-1, face_index)
25
+ if n_faces == 0:
26
+ aligned_image = image_in
27
+ quad = None
28
+ else:
29
+ aligned_image, quad = image_align(image_in, landmarks[face_index], output_size=output_size)
30
+
31
+ return aligned_image, n_faces, quad
32
+
33
+
34
+ def composite_images(quad, img, output):
35
+ """Composite an image into and output canvas according to transformed co-ords"""
36
+ output = output.convert("RGBA")
37
+ img = img.convert("RGBA")
38
+ input_size = img.size
39
+ src = np.array(((0, 0), (0, input_size[1]), input_size, (input_size[0], 0)), dtype=np.float32)
40
+ dst = np.float32(quad)
41
+ mtx = cv2.getPerspectiveTransform(dst, src)
42
+ img = img.transform(output.size, Image.PERSPECTIVE, mtx.flatten(), Image.BILINEAR)
43
+ output.alpha_composite(img)
44
+
45
+ return output.convert("RGB")
46
+
47
+
48
+ def get_landmarks(image):
49
+ """Get landmarks from PIL image"""
50
+ shape_predictor = dlib.shape_predictor(MODEL_PATH)
51
+
52
+ max_size = max(image.size)
53
+ reduction_scale = int(max_size/512)
54
+ if reduction_scale == 0:
55
+ reduction_scale = 1
56
+ downscaled = image.reduce(reduction_scale)
57
+ img = np.array(downscaled)
58
+ detections = detector(img, 0)
59
+
60
+ for detection in detections:
61
+ try:
62
+ face_landmarks = [(reduction_scale*item.x, reduction_scale*item.y) for item in shape_predictor(img, detection).parts()]
63
+ yield face_landmarks
64
+ except Exception as e:
65
+ print(e)
66
+
67
+
68
+ def image_align(src_img, face_landmarks, output_size=512, transform_size=2048, enable_padding=True, x_scale=1, y_scale=1, em_scale=0.1, alpha=False):
69
+ # Align function modified from ffhq-dataset
70
+ # See https://github.com/NVlabs/ffhq-dataset for license
71
+
72
+ lm = np.array(face_landmarks)
73
+ lm_eye_left = lm[2:3] # left-clockwise
74
+ lm_eye_right = lm[0:1] # left-clockwise
75
+
76
+ # Calculate auxiliary vectors.
77
+ eye_left = np.mean(lm_eye_left, axis=0)
78
+ eye_right = np.mean(lm_eye_right, axis=0)
79
+ eye_avg = (eye_left + eye_right) * 0.5
80
+ eye_to_eye = 0.71*(eye_right - eye_left)
81
+ mouth_avg = lm[4]
82
+ eye_to_mouth = 1.35*(mouth_avg - eye_avg)
83
+
84
+ # Choose oriented crop rectangle.
85
+ x = eye_to_eye.copy()
86
+ x /= np.hypot(*x)
87
+ x *= max(np.hypot(*eye_to_eye) * 2.0, np.hypot(*eye_to_mouth) * 1.8)
88
+ x *= x_scale
89
+ y = np.flipud(x) * [-y_scale, y_scale]
90
+ c = eye_avg + eye_to_mouth * em_scale
91
+ quad = np.stack([c - x - y, c - x + y, c + x + y, c + x - y])
92
+ quad_orig = quad.copy()
93
+ qsize = np.hypot(*x) * 2
94
+
95
+ img = src_img.convert('RGBA').convert('RGB')
96
+
97
+ # Shrink.
98
+ shrink = int(np.floor(qsize / output_size * 0.5))
99
+ if shrink > 1:
100
+ rsize = (int(np.rint(float(img.size[0]) / shrink)), int(np.rint(float(img.size[1]) / shrink)))
101
+ img = img.resize(rsize, Image.ANTIALIAS)
102
+ quad /= shrink
103
+ qsize /= shrink
104
+
105
+ # Crop.
106
+ border = max(int(np.rint(qsize * 0.1)), 3)
107
+ crop = (int(np.floor(min(quad[:,0]))), int(np.floor(min(quad[:,1]))), int(np.ceil(max(quad[:,0]))), int(np.ceil(max(quad[:,1]))))
108
+ crop = (max(crop[0] - border, 0), max(crop[1] - border, 0), min(crop[2] + border, img.size[0]), min(crop[3] + border, img.size[1]))
109
+ if crop[2] - crop[0] < img.size[0] or crop[3] - crop[1] < img.size[1]:
110
+ img = img.crop(crop)
111
+ quad -= crop[0:2]
112
+
113
+ # Pad.
114
+ pad = (int(np.floor(min(quad[:,0]))), int(np.floor(min(quad[:,1]))), int(np.ceil(max(quad[:,0]))), int(np.ceil(max(quad[:,1]))))
115
+ pad = (max(-pad[0] + border, 0), max(-pad[1] + border, 0), max(pad[2] - img.size[0] + border, 0), max(pad[3] - img.size[1] + border, 0))
116
+ if enable_padding and max(pad) > border - 4:
117
+ pad = np.maximum(pad, int(np.rint(qsize * 0.3)))
118
+ img = np.pad(np.float32(img), ((pad[1], pad[3]), (pad[0], pad[2]), (0, 0)), 'reflect')
119
+ h, w, _ = img.shape
120
+ y, x, _ = np.ogrid[:h, :w, :1]
121
+ mask = np.maximum(1.0 - np.minimum(np.float32(x) / pad[0], np.float32(w-1-x) / pad[2]), 1.0 - np.minimum(np.float32(y) / pad[1], np.float32(h-1-y) / pad[3]))
122
+ blur = qsize * 0.02
123
+ img += (gaussian_filter(img, [blur, blur, 0]) - img) * np.clip(mask * 3.0 + 1.0, 0.0, 1.0)
124
+ img += (np.median(img, axis=(0,1)) - img) * np.clip(mask, 0.0, 1.0)
125
+ img = np.uint8(np.clip(np.rint(img), 0, 255))
126
+ if alpha:
127
+ mask = 1-np.clip(3.0 * mask, 0.0, 1.0)
128
+ mask = np.uint8(np.clip(np.rint(mask*255), 0, 255))
129
+ img = np.concatenate((img, mask), axis=2)
130
+ img = Image.fromarray(img, 'RGBA')
131
+ else:
132
+ img = Image.fromarray(img, 'RGB')
133
+ quad += pad[:2]
134
+
135
+ # Transform.
136
+ img = img.transform((transform_size, transform_size), Image.QUAD, (quad + 0.5).flatten(), Image.BILINEAR)
137
+ if output_size < transform_size:
138
+ img = img.resize((output_size, output_size), Image.ANTIALIAS)
139
+
140
+ return img, quad_orig
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ numpy
2
+ opencv-python-headless
3
+ Pillow
4
+ scikit-image
5
+ torch
6
+ torchvision
7
+ scipy
8
+ cmake
shape_predictor_5_face_landmarks.dat ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c4b1e9804792707d3a405c2c16a80a20269e6675021f64a41d30fffafbc41888
3
+ size 9150489