PKUWilliamYang commited on
Commit
ac1883f
1 Parent(s): ac4ce84

Upload 8 files

Browse files
utils/__init__.py ADDED
File without changes
utils/common.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from PIL import Image
4
+ import matplotlib.pyplot as plt
5
+
6
+
7
+ # Log images
8
+ def log_input_image(x, opts):
9
+ if opts.label_nc == 0:
10
+ return tensor2im(x)
11
+ elif opts.label_nc == 1:
12
+ return tensor2sketch(x)
13
+ else:
14
+ return tensor2map(x)
15
+
16
+
17
+ def tensor2im(var):
18
+ var = var.cpu().detach().transpose(0, 2).transpose(0, 1).numpy()
19
+ var = ((var + 1) / 2)
20
+ var[var < 0] = 0
21
+ var[var > 1] = 1
22
+ var = var * 255
23
+ return Image.fromarray(var.astype('uint8'))
24
+
25
+
26
+ def tensor2map(var):
27
+ mask = np.argmax(var.data.cpu().numpy(), axis=0)
28
+ colors = get_colors()
29
+ mask_image = np.ones(shape=(mask.shape[0], mask.shape[1], 3))
30
+ for class_idx in np.unique(mask):
31
+ mask_image[mask == class_idx] = colors[class_idx]
32
+ mask_image = mask_image.astype('uint8')
33
+ return Image.fromarray(mask_image)
34
+
35
+
36
+ def tensor2sketch(var):
37
+ im = var[0].cpu().detach().numpy()
38
+ im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR)
39
+ im = (im * 255).astype(np.uint8)
40
+ return Image.fromarray(im)
41
+
42
+
43
+ # Visualization utils
44
+ def get_colors():
45
+ # currently support up to 19 classes (for the celebs-hq-mask dataset)
46
+ colors = [[0, 0, 0], [204, 0, 0], [76, 153, 0], [204, 204, 0], [51, 51, 255], [204, 0, 204], [0, 255, 255],
47
+ [255, 204, 204], [102, 51, 0], [255, 0, 0], [102, 204, 0], [255, 255, 0], [0, 0, 153], [0, 0, 204],
48
+ [255, 51, 153], [0, 204, 204], [0, 51, 0], [255, 153, 51], [0, 204, 0]]
49
+ return colors
50
+
51
+
52
+ def vis_faces(log_hooks):
53
+ display_count = len(log_hooks)
54
+ fig = plt.figure(figsize=(8, 4 * display_count))
55
+ gs = fig.add_gridspec(display_count, 3)
56
+ for i in range(display_count):
57
+ hooks_dict = log_hooks[i]
58
+ fig.add_subplot(gs[i, 0])
59
+ if 'diff_input' in hooks_dict:
60
+ vis_faces_with_id(hooks_dict, fig, gs, i)
61
+ else:
62
+ vis_faces_no_id(hooks_dict, fig, gs, i)
63
+ plt.tight_layout()
64
+ return fig
65
+
66
+
67
+ def vis_faces_with_id(hooks_dict, fig, gs, i):
68
+ plt.imshow(hooks_dict['input_face'])
69
+ plt.title('Input\nOut Sim={:.2f}'.format(float(hooks_dict['diff_input'])))
70
+ fig.add_subplot(gs[i, 1])
71
+ plt.imshow(hooks_dict['target_face'])
72
+ plt.title('Target\nIn={:.2f}, Out={:.2f}'.format(float(hooks_dict['diff_views']),
73
+ float(hooks_dict['diff_target'])))
74
+ fig.add_subplot(gs[i, 2])
75
+ plt.imshow(hooks_dict['output_face'])
76
+ plt.title('Output\n Target Sim={:.2f}'.format(float(hooks_dict['diff_target'])))
77
+
78
+
79
+ def vis_faces_no_id(hooks_dict, fig, gs, i):
80
+ plt.imshow(hooks_dict['input_face'], cmap="gray")
81
+ plt.title('Input')
82
+ fig.add_subplot(gs[i, 1])
83
+ plt.imshow(hooks_dict['target_face'])
84
+ plt.title('Target')
85
+ fig.add_subplot(gs[i, 2])
86
+ plt.imshow(hooks_dict['output_face'])
87
+ plt.title('Output')
utils/data_utils.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Code adopted from pix2pixHD:
3
+ https://github.com/NVIDIA/pix2pixHD/blob/master/data/image_folder.py
4
+ """
5
+ import os
6
+
7
+ IMG_EXTENSIONS = [
8
+ '.jpg', '.JPG', '.jpeg', '.JPEG',
9
+ '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP', '.tiff'
10
+ ]
11
+
12
+
13
+ def is_image_file(filename):
14
+ return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)
15
+
16
+
17
+ def make_dataset(dir):
18
+ images = []
19
+ assert os.path.isdir(dir), '%s is not a valid directory' % dir
20
+ for root, _, fnames in sorted(os.walk(dir)):
21
+ for fname in fnames:
22
+ if is_image_file(fname):
23
+ path = os.path.join(root, fname)
24
+ images.append(path)
25
+ return images
utils/inference_utils.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import matplotlib.pyplot as plt
3
+ from PIL import Image
4
+ import cv2
5
+ import random
6
+ import math
7
+ import argparse
8
+ import torch
9
+ from torch.utils import data
10
+ from torch.nn import functional as F
11
+ from torch import autograd
12
+ from torch.nn import init
13
+ import torchvision.transforms as transforms
14
+ from scripts.align_all_parallel import get_landmark
15
+
16
+ def visualize(img_arr, dpi):
17
+ plt.figure(figsize=(10,10),dpi=dpi)
18
+ plt.imshow(((img_arr.detach().cpu().numpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8))
19
+ plt.axis('off')
20
+ plt.show()
21
+
22
+ def save_image(img, filename):
23
+ tmp = ((img.detach().cpu().numpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8)
24
+ cv2.imwrite(filename, cv2.cvtColor(tmp, cv2.COLOR_RGB2BGR))
25
+
26
+ def load_image(filename):
27
+ transform = transforms.Compose([
28
+ transforms.ToTensor(),
29
+ transforms.Normalize(mean=[0.5, 0.5, 0.5],std=[0.5,0.5,0.5]),
30
+ ])
31
+
32
+ img = Image.open(filename)
33
+ img = transform(img)
34
+ return img.unsqueeze(dim=0)
35
+
36
+ def get_video_crop_parameter(filepath, predictor, padding=[256,256,256,256]):
37
+ if type(filepath) == str:
38
+ img = dlib.load_rgb_image(filepath)
39
+ else:
40
+ img = filepath
41
+ lm = get_landmark(img, predictor)
42
+ if lm is None:
43
+ return None
44
+ lm_chin = lm[0 : 17] # left-right
45
+ lm_eyebrow_left = lm[17 : 22] # left-right
46
+ lm_eyebrow_right = lm[22 : 27] # left-right
47
+ lm_nose = lm[27 : 31] # top-down
48
+ lm_nostrils = lm[31 : 36] # top-down
49
+ lm_eye_left = lm[36 : 42] # left-clockwise
50
+ lm_eye_right = lm[42 : 48] # left-clockwise
51
+ lm_mouth_outer = lm[48 : 60] # left-clockwise
52
+ lm_mouth_inner = lm[60 : 68] # left-clockwise
53
+
54
+ scale = 64. / (np.mean(lm_eye_right[:,0])-np.mean(lm_eye_left[:,0]))
55
+ center = ((np.mean(lm_eye_right, axis=0)+np.mean(lm_eye_left, axis=0)) / 2) * scale
56
+ h, w = round(img.shape[0] * scale), round(img.shape[1] * scale)
57
+ left = max(round(center[0] - padding[0]), 0) // 8 * 8
58
+ right = min(round(center[0] + padding[1]), w) // 8 * 8
59
+ top = max(round(center[1] - padding[2]), 0) // 8 * 8
60
+ bottom = min(round(center[1] + padding[3]), h) // 8 * 8
61
+ return h,w,top,bottom,left,right,scale
62
+
63
+ def tensor2cv2(img):
64
+ tmp = ((img.cpu().numpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8)
65
+ return cv2.cvtColor(tmp, cv2.COLOR_RGB2BGR)
66
+
67
+ def noise_regularize(noises):
68
+ loss = 0
69
+
70
+ for noise in noises:
71
+ size = noise.shape[2]
72
+
73
+ while True:
74
+ loss = (
75
+ loss
76
+ + (noise * torch.roll(noise, shifts=1, dims=3)).mean().pow(2)
77
+ + (noise * torch.roll(noise, shifts=1, dims=2)).mean().pow(2)
78
+ )
79
+
80
+ if size <= 8:
81
+ break
82
+
83
+ #noise = noise.reshape([-1, 1, size // 2, 2, size // 2, 2])
84
+ #noise = noise.mean([3, 5])
85
+ noise = F.interpolate(noise, scale_factor=0.5, mode='bilinear')
86
+ size //= 2
87
+
88
+ return loss
89
+
90
+
91
+ def noise_normalize_(noises):
92
+ for noise in noises:
93
+ mean = noise.mean()
94
+ std = noise.std()
95
+
96
+ noise.data.add_(-mean).div_(std)
97
+
98
+
99
+ def get_lr(t, initial_lr, rampdown=0.25, rampup=0.05):
100
+ lr_ramp = min(1, (1 - t) / rampdown)
101
+ lr_ramp = 0.5 - 0.5 * math.cos(lr_ramp * math.pi)
102
+ lr_ramp = lr_ramp * min(1, t / rampup)
103
+
104
+ return initial_lr * lr_ramp
105
+
106
+
107
+ def latent_noise(latent, strength):
108
+ noise = torch.randn_like(latent) * strength
109
+
110
+ return latent + noise
111
+
112
+
113
+ def make_image(tensor):
114
+ return (
115
+ tensor.detach()
116
+ .clamp_(min=-1, max=1)
117
+ .add(1)
118
+ .div_(2)
119
+ .mul(255)
120
+ .type(torch.uint8)
121
+ .permute(0, 2, 3, 1)
122
+ .to("cpu")
123
+ .numpy()
124
+ )
125
+
126
+
127
+ # from pix2pixeHD
128
+ # Converts a one-hot tensor into a colorful label map
129
+ def tensor2label(label_tensor, n_label, imtype=np.uint8):
130
+ if n_label == 0:
131
+ return tensor2im(label_tensor, imtype)
132
+ label_tensor = label_tensor.cpu().float()
133
+ if label_tensor.size()[0] > 1:
134
+ label_tensor = label_tensor.max(0, keepdim=True)[1]
135
+ label_tensor = Colorize(n_label)(label_tensor)
136
+ label_numpy = np.transpose(label_tensor.numpy(), (1, 2, 0))
137
+ return label_numpy.astype(imtype)
138
+
139
+ def uint82bin(n, count=8):
140
+ """returns the binary of integer n, count refers to amount of bits"""
141
+ return ''.join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
142
+
143
+ def labelcolormap(N):
144
+ if N == 35: # cityscape
145
+ cmap = np.array([( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), ( 0, 0, 0), (111, 74, 0), ( 81, 0, 81),
146
+ (128, 64,128), (244, 35,232), (250,170,160), (230,150,140), ( 70, 70, 70), (102,102,156), (190,153,153),
147
+ (180,165,180), (150,100,100), (150,120, 90), (153,153,153), (153,153,153), (250,170, 30), (220,220, 0),
148
+ (107,142, 35), (152,251,152), ( 70,130,180), (220, 20, 60), (255, 0, 0), ( 0, 0,142), ( 0, 0, 70),
149
+ ( 0, 60,100), ( 0, 0, 90), ( 0, 0,110), ( 0, 80,100), ( 0, 0,230), (119, 11, 32), ( 0, 0,142)],
150
+ dtype=np.uint8)
151
+ else:
152
+ cmap = np.zeros((N, 3), dtype=np.uint8)
153
+ for i in range(N):
154
+ r, g, b = 0, 0, 0
155
+ id = i
156
+ for j in range(7):
157
+ str_id = uint82bin(id)
158
+ r = r ^ (np.uint8(str_id[-1]) << (7-j))
159
+ g = g ^ (np.uint8(str_id[-2]) << (7-j))
160
+ b = b ^ (np.uint8(str_id[-3]) << (7-j))
161
+ id = id >> 3
162
+ cmap[i, 0] = r
163
+ cmap[i, 1] = g
164
+ cmap[i, 2] = b
165
+ return cmap
166
+
167
+ class Colorize(object):
168
+ def __init__(self, n=35):
169
+ self.cmap = labelcolormap(n)
170
+ self.cmap = torch.from_numpy(self.cmap[:n])
171
+
172
+ def __call__(self, gray_image):
173
+ size = gray_image.size()
174
+ color_image = torch.ByteTensor(3, size[1], size[2]).fill_(0)
175
+
176
+ for label in range(0, len(self.cmap)):
177
+ mask = (label == gray_image[0]).cpu()
178
+ color_image[0][mask] = self.cmap[label][0]
179
+ color_image[1][mask] = self.cmap[label][1]
180
+ color_image[2][mask] = self.cmap[label][2]
181
+
182
+ return color_image
utils/train_utils.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ def aggregate_loss_dict(agg_loss_dict):
3
+ mean_vals = {}
4
+ for output in agg_loss_dict:
5
+ for key in output:
6
+ mean_vals[key] = mean_vals.setdefault(key, []) + [output[key]]
7
+ for key in mean_vals:
8
+ if len(mean_vals[key]) > 0:
9
+ mean_vals[key] = sum(mean_vals[key]) / len(mean_vals[key])
10
+ else:
11
+ print('{} has no value'.format(key))
12
+ mean_vals[key] = 0
13
+ return mean_vals
utils/wandb_utils.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ import os
3
+ import numpy as np
4
+ import wandb
5
+
6
+ from utils import common
7
+
8
+
9
+ class WBLogger:
10
+
11
+ def __init__(self, opts):
12
+ wandb_run_name = os.path.basename(opts.exp_dir)
13
+ wandb.init(project="pixel2style2pixel", config=vars(opts), name=wandb_run_name)
14
+
15
+ @staticmethod
16
+ def log_best_model():
17
+ wandb.run.summary["best-model-save-time"] = datetime.datetime.now()
18
+
19
+ @staticmethod
20
+ def log(prefix, metrics_dict, global_step):
21
+ log_dict = {f'{prefix}_{key}': value for key, value in metrics_dict.items()}
22
+ log_dict["global_step"] = global_step
23
+ wandb.log(log_dict)
24
+
25
+ @staticmethod
26
+ def log_dataset_wandb(dataset, dataset_name, n_images=16):
27
+ idxs = np.random.choice(a=range(len(dataset)), size=n_images, replace=False)
28
+ data = [wandb.Image(dataset.source_paths[idx]) for idx in idxs]
29
+ wandb.log({f"{dataset_name} Data Samples": data})
30
+
31
+ @staticmethod
32
+ def log_images_to_wandb(x, y, y_hat, id_logs, prefix, step, opts):
33
+ im_data = []
34
+ column_names = ["Source", "Target", "Output"]
35
+ if id_logs is not None:
36
+ column_names.append("ID Diff Output to Target")
37
+ for i in range(len(x)):
38
+ cur_im_data = [
39
+ wandb.Image(common.log_input_image(x[i], opts)),
40
+ wandb.Image(common.tensor2im(y[i])),
41
+ wandb.Image(common.tensor2im(y_hat[i])),
42
+ ]
43
+ if id_logs is not None:
44
+ cur_im_data.append(id_logs[i]["diff_target"])
45
+ im_data.append(cur_im_data)
46
+ outputs_table = wandb.Table(data=im_data, columns=column_names)
47
+ wandb.log({f"{prefix.title()} Step {step} Output Samples": outputs_table})
webUI/app_task.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from huggingface_hub import hf_hub_download
3
+ import numpy as np
4
+ import gradio as gr
5
+
6
+
7
+ def create_demo_sr(process):
8
+ with gr.Blocks() as demo:
9
+ with gr.Row():
10
+ gr.Markdown('## Face Super Resolution')
11
+ with gr.Row():
12
+ with gr.Column():
13
+ input_image = gr.Image(source='upload', type='filepath')
14
+ model_type = gr.Radio(label='Model Type', choices=['SR for 32x','SR for 4x-48x'], value='SR for 32x')
15
+ resize_scale = gr.Slider(label='Resize Scale',
16
+ minimum=4,
17
+ maximum=48,
18
+ value=32,
19
+ step=4)
20
+ run_button = gr.Button(label='Run')
21
+ gr.Examples(
22
+ examples =[['pexels-daniel-xavier-1239291.jpg', 'SR for 32x', 32],
23
+ ['ILip77SbmOE.png', 'SR for 32x', 32],
24
+ ['ILip77SbmOE.png', 'SR for 4x-48x', 48],
25
+ ],
26
+ inputs = [input_image, model_type, resize_scale],
27
+ )
28
+ with gr.Column():
29
+ #lrinput = gr.Image(label='Low-resolution input',type='numpy', interactive=False)
30
+ #result = gr.Image(label='Output',type='numpy', interactive=False)
31
+ result = gr.Gallery(label='LR input and Output',
32
+ elem_id='gallery').style(grid=2,
33
+ height='auto')
34
+
35
+ inputs = [
36
+ input_image,
37
+ resize_scale,
38
+ model_type,
39
+ ]
40
+ run_button.click(fn=process,
41
+ inputs=inputs,
42
+ outputs=[result],
43
+ api_name='sr')
44
+ return demo
45
+
46
+ def create_demo_s2f(process):
47
+ with gr.Blocks() as demo:
48
+ with gr.Row():
49
+ gr.Markdown('## Sketch-to-Face Translation')
50
+ with gr.Row():
51
+ with gr.Column():
52
+ input_image = gr.Image(source='upload', type='filepath')
53
+ gr.Markdown("""Note: Input will be cropped if larger than 512x512.""")
54
+ seed = gr.Slider(label='Seed for appearance',
55
+ minimum=0,
56
+ maximum=2147483647,
57
+ step=1,
58
+ randomize=True)
59
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
60
+ run_button = gr.Button(label='Run')
61
+ gr.Examples(
62
+ examples =[['234_sketch.jpg', 1024]],
63
+ inputs = [input_image, seed],
64
+ )
65
+ with gr.Column():
66
+ result = gr.Image(label='Output',type='numpy', interactive=False)
67
+
68
+ inputs = [
69
+ input_image, seed
70
+ ]
71
+ run_button.click(fn=process,
72
+ inputs=inputs,
73
+ outputs=[result],
74
+ api_name='s2f')
75
+ return demo
76
+
77
+
78
+ def create_demo_m2f(process):
79
+ with gr.Blocks() as demo:
80
+ with gr.Row():
81
+ gr.Markdown('## Mask-to-Face Translation')
82
+ with gr.Row():
83
+ with gr.Column():
84
+ input_image = gr.Image(source='upload', type='filepath')
85
+ input_type = gr.Radio(label='Input Type', choices=['color image','parsing mask'], value='color image')
86
+ seed = gr.Slider(label='Seed for appearance',
87
+ minimum=0,
88
+ maximum=2147483647,
89
+ step=1,
90
+ randomize=True)
91
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
92
+ run_button = gr.Button(label='Run')
93
+ gr.Examples(
94
+ examples =[['ILip77SbmOE.png', 'color image', 4], ['ILip77SbmOE_mask.png', 'parsing mask', 4]],
95
+ inputs = [input_image, input_type, seed],
96
+ )
97
+ with gr.Column():
98
+ #vizmask = gr.Image(label='Visualized mask',type='numpy', interactive=False)
99
+ #result = gr.Image(label='Output',type='numpy', interactive=False)
100
+ result = gr.Gallery(label='Visualized mask and Output',
101
+ elem_id='gallery').style(grid=2,
102
+ height='auto')
103
+
104
+ inputs = [
105
+ input_image, input_type, seed
106
+ ]
107
+ run_button.click(fn=process,
108
+ inputs=inputs,
109
+ outputs=[result],
110
+ api_name='m2f')
111
+ return demo
112
+
113
+ def create_demo_editing(process):
114
+ with gr.Blocks() as demo:
115
+ with gr.Row():
116
+ gr.Markdown('## Video Face Editing (for image input)')
117
+ with gr.Row():
118
+ with gr.Column():
119
+ input_image = gr.Image(source='upload', type='filepath')
120
+ model_type = gr.Radio(label='Editing Type', choices=['reduce age','light hair color'], value='color image')
121
+ scale_factor = gr.Slider(label='editing degree (-2~2)',
122
+ minimum=-2,
123
+ maximum=2,
124
+ value=1,
125
+ step=0.1)
126
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
127
+ run_button = gr.Button(label='Run')
128
+ gr.Examples(
129
+ examples =[['ILip77SbmOE.png', 'reduce age', -2],
130
+ ['ILip77SbmOE.png', 'light hair color', 1]],
131
+ inputs = [input_image, model_type, scale_factor],
132
+ )
133
+ with gr.Column():
134
+ result = gr.Image(label='Output',type='numpy', interactive=False)
135
+
136
+ inputs = [
137
+ input_image, scale_factor, model_type
138
+ ]
139
+ run_button.click(fn=process,
140
+ inputs=inputs,
141
+ outputs=[result],
142
+ api_name='editing')
143
+ return demo
144
+
145
+ def create_demo_toonify(process):
146
+ with gr.Blocks() as demo:
147
+ with gr.Row():
148
+ gr.Markdown('## Video Face Toonification (for image input)')
149
+ with gr.Row():
150
+ with gr.Column():
151
+ input_image = gr.Image(source='upload', type='filepath')
152
+ style_type = gr.Radio(label='Style Type', choices=['Pixar','Cartoon','Arcane'], value='Pixar')
153
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
154
+ run_button = gr.Button(label='Run')
155
+ gr.Examples(
156
+ examples =[['ILip77SbmOE.png', 'Pixar'], ['ILip77SbmOE.png', 'Cartoon'], ['ILip77SbmOE.png', 'Arcane']],
157
+ inputs = [input_image, style_type],
158
+ )
159
+ with gr.Column():
160
+ result = gr.Image(label='Output',type='numpy', interactive=False)
161
+
162
+ inputs = [
163
+ input_image, style_type
164
+ ]
165
+ run_button.click(fn=process,
166
+ inputs=inputs,
167
+ outputs=[result],
168
+ api_name='toonify')
169
+ return demo
170
+
171
+
172
+ def create_demo_vediting(process, max_frame_num = 4):
173
+ with gr.Blocks() as demo:
174
+ with gr.Row():
175
+ gr.Markdown('## Video Face Editing (for video input)')
176
+ with gr.Row():
177
+ with gr.Column():
178
+ input_video = gr.Video(source='upload', mirror_webcam=False, type='filepath')
179
+ model_type = gr.Radio(label='Editing Type', choices=['reduce age','light hair color'], value='color image')
180
+ scale_factor = gr.Slider(label='editing degree (-2~2)',
181
+ minimum=-2,
182
+ maximum=2,
183
+ value=1,
184
+ step=0.1)
185
+ frame_num = gr.Slider(label='Number of frames to edit (full video editing is not allowed so as not to slow down the demo, \
186
+ but you can duplicate the Space to modify the number limit from 4 to a large value)',
187
+ minimum=1,
188
+ maximum=max_frame_num,
189
+ value=2,
190
+ step=1)
191
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
192
+ run_button = gr.Button(label='Run')
193
+ gr.Examples(
194
+ examples =[['684.mp4', 'reduce age', 1.5, 2],
195
+ ['684.mp4', 'light hair color', 0.7, 2]],
196
+ inputs = [input_video, model_type, scale_factor],
197
+ )
198
+ with gr.Column():
199
+ viz_result = gr.Gallery(label='Several edited frames', elem_id='gallery').style(grid=2, height='auto')
200
+ result = gr.Video(label='Output', type='mp4', interactive=False)
201
+
202
+ inputs = [
203
+ input_video, scale_factor, model_type, frame_num
204
+ ]
205
+ run_button.click(fn=process,
206
+ inputs=inputs,
207
+ outputs=[viz_result, result],
208
+ api_name='vediting')
209
+ return demo
210
+
211
+ def create_demo_vtoonify(process, max_frame_num = 4):
212
+ with gr.Blocks() as demo:
213
+ with gr.Row():
214
+ gr.Markdown('## Video Face Toonification (for video input)')
215
+ with gr.Row():
216
+ with gr.Column():
217
+ input_video = gr.Video(source='upload', mirror_webcam=False, type='filepath')
218
+ style_type = gr.Radio(label='Style Type', choices=['Pixar','Cartoon','Arcane'], value='Pixar')
219
+ frame_num = gr.Slider(label='Number of frames to toonify (full video toonification is not allowed so as not to slow down the demo, \
220
+ but you can duplicate the Space to modify the number limit from 4 to a large value)',
221
+ minimum=1,
222
+ maximum=max_frame_num,
223
+ value=2,
224
+ step=1)
225
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
226
+ run_button = gr.Button(label='Run')
227
+ gr.Examples(
228
+ examples =[['529_2.mp4', 'Arcane'],
229
+ ['pexels-anthony-shkraba-production-8136210.mp4', 'Pixar'],
230
+ ['684.mp4', 'Cartoon']],
231
+ inputs = [input_video, style_type],
232
+ )
233
+ with gr.Column():
234
+ viz_result = gr.Gallery(label='Several toonified frames', elem_id='gallery').style(grid=2, height='auto')
235
+ result = gr.Video(label='Output', type='mp4', interactive=False)
236
+
237
+ inputs = [
238
+ input_video, style_type, frame_num
239
+ ]
240
+ run_button.click(fn=process,
241
+ inputs=inputs,
242
+ outputs=[viz_result, result],
243
+ api_name='vtoonify')
244
+ return demo
245
+
246
+ def create_demo_inversion(process, allow_optimization=False):
247
+ with gr.Blocks() as demo:
248
+ with gr.Row():
249
+ gr.Markdown('## StyleGANEX Inversion for Editing')
250
+ with gr.Row():
251
+ with gr.Column():
252
+ input_image = gr.Image(source='upload', type='filepath')
253
+ optimize = gr.Radio(label='Whether optimize latent (latent optimization is not allowed so as not to slow down the demo, \
254
+ but you can duplicate the Space to modify the option or directly upload an optimized latent file. \
255
+ The file can be computed by inversion.py from the github page or colab)', choices=['No optimization','Latent optimization'],
256
+ value='No optimization', interactive=allow_optimization)
257
+ input_latent = gr.File(label='Optimized latent code (optional)', file_types=[".pt"])
258
+ editing_options = gr.Dropdown(['None', 'Style Mixing',
259
+ 'Attribute Editing: smile',
260
+ 'Attribute Editing: open_eye',
261
+ 'Attribute Editing: open_mouth',
262
+ 'Attribute Editing: pose',
263
+ 'Attribute Editing: reduce_age',
264
+ 'Attribute Editing: glasses',
265
+ 'Attribute Editing: light_hair_color',
266
+ 'Attribute Editing: slender',
267
+ 'Domain Transfer: disney_princess',
268
+ 'Domain Transfer: vintage_comics',
269
+ 'Domain Transfer: pixar',
270
+ 'Domain Transfer: edvard_munch',
271
+ 'Domain Transfer: modigliani',
272
+ ],
273
+ label="editing options",
274
+ value='None')
275
+ scale_factor = gr.Slider(label='editing degree (-2~2) for Attribute Editing',
276
+ minimum=-2,
277
+ maximum=2,
278
+ value=2,
279
+ step=0.1)
280
+ seed = gr.Slider(label='Appearance Seed for Style Mixing',
281
+ minimum=0,
282
+ maximum=2147483647,
283
+ step=1,
284
+ randomize=True)
285
+ #input_info = gr.Textbox(label='Process Information', interactive=False, value='n.a.')
286
+ run_button = gr.Button(label='Run')
287
+ gr.Examples(
288
+ examples =[['ILip77SbmOE.png', 'ILip77SbmOE_inversion.pt', 'Domain Transfer: vintage_comics'],
289
+ ['ILip77SbmOE.png', 'ILip77SbmOE_inversion.pt', 'Attribute Editing: smile'],
290
+ ['ILip77SbmOE.png', 'ILip77SbmOE_inversion.pt', 'Style Mixing'],
291
+ ],
292
+ inputs = [input_image, input_latent, editing_options],
293
+ )
294
+ with gr.Column():
295
+ result = gr.Image(label='Inversion output',type='numpy', interactive=False)
296
+ editing_result = gr.Image(label='Editing output',type='numpy', interactive=False)
297
+
298
+ inputs = [
299
+ input_image, optimize, input_latent, editing_options, scale_factor, seed
300
+ ]
301
+ run_button.click(fn=process,
302
+ inputs=inputs,
303
+ outputs=[result, editing_result],
304
+ api_name='inversion')
305
+ return demo
webUI/styleganex_model.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import numpy as np
3
+ import gradio as gr
4
+
5
+ import os
6
+ import pathlib
7
+ import gc
8
+ import torch
9
+ import dlib
10
+ import cv2
11
+ import PIL
12
+ from tqdm import tqdm
13
+ import numpy as np
14
+ import torch.nn.functional as F
15
+ import torchvision
16
+ from torchvision import transforms, utils
17
+ from argparse import Namespace
18
+ from datasets import augmentations
19
+ from huggingface_hub import hf_hub_download
20
+ from scripts.align_all_parallel import align_face
21
+ from latent_optimization import latent_optimization
22
+ from utils.inference_utils import save_image, load_image, visualize, get_video_crop_parameter, tensor2cv2, tensor2label, labelcolormap
23
+ from models.psp import pSp
24
+ from models.bisenet.model import BiSeNet
25
+ from models.stylegan2.model import Generator
26
+
27
+ class Model():
28
+ def __init__(self, device):
29
+ super().__init__()
30
+
31
+ self.device = device
32
+ self.task_name = None
33
+ self.editing_w = None
34
+ self.pspex = None
35
+ self.landmarkpredictor = dlib.shape_predictor(hf_hub_download('PKUWilliamYang/VToonify', 'models/shape_predictor_68_face_landmarks.dat'))
36
+ self.transform = transforms.Compose([
37
+ transforms.ToTensor(),
38
+ transforms.Normalize(mean=[0.5, 0.5, 0.5],std=[0.5,0.5,0.5]),
39
+ ])
40
+ self.to_tensor = transforms.Compose([
41
+ transforms.ToTensor(),
42
+ transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
43
+ ])
44
+ self.maskpredictor = BiSeNet(n_classes=19)
45
+ self.maskpredictor.load_state_dict(torch.load(hf_hub_download('PKUWilliamYang/VToonify', 'models/faceparsing.pth'), map_location='cpu'))
46
+ self.maskpredictor.to(self.device).eval()
47
+ self.parameters = {}
48
+ self.parameters['inversion'] = {'path':'pretrained_models/styleganex_inversion.pt', 'image_path':'./data/ILip77SbmOE.png'}
49
+ self.parameters['sr-32'] = {'path':'pretrained_models/styleganex_sr32.pt', 'image_path':'./data/pexels-daniel-xavier-1239291.jpg'}
50
+ self.parameters['sr'] = {'path':'pretrained_models/styleganex_sr.pt', 'image_path':'./data/pexels-daniel-xavier-1239291.jpg'}
51
+ self.parameters['sketch2face'] = {'path':'pretrained_models/styleganex_sketch2face.pt', 'image_path':'./data/234_sketch.jpg'}
52
+ self.parameters['mask2face'] = {'path':'pretrained_models/styleganex_mask2face.pt', 'image_path':'./data/540.jpg'}
53
+ self.parameters['edit_age'] = {'path':'pretrained_models/styleganex_edit_age.pt', 'image_path':'./data/390.mp4'}
54
+ self.parameters['edit_hair'] = {'path':'pretrained_models/styleganex_edit_hair.pt', 'image_path':'./data/390.mp4'}
55
+ self.parameters['toonify_pixar'] = {'path':'pretrained_models/styleganex_toonify_pixar.pt', 'image_path':'./data/pexels-anthony-shkraba-production-8136210.mp4'}
56
+ self.parameters['toonify_cartoon'] = {'path':'pretrained_models/styleganex_toonify_cartoon.pt', 'image_path':'./data/pexels-anthony-shkraba-production-8136210.mp4'}
57
+ self.parameters['toonify_arcane'] = {'path':'pretrained_models/styleganex_toonify_arcane.pt', 'image_path':'./data/pexels-anthony-shkraba-production-8136210.mp4'}
58
+ self.print_log = True
59
+ self.editing_dicts = torch.load(hf_hub_download('PKUWilliamYang/StyleGANEX', 'direction_dics.pt'))
60
+ self.generator = Generator(1024, 512, 8)
61
+ self.model_type = None
62
+ self.error_info = 'Error: no face detected! \
63
+ StyleGANEX uses dlib.get_frontal_face_detector but sometimes it fails to detect a face. \
64
+ You can try several times or use other images until a face is detected, \
65
+ then switch back to the original image.'
66
+
67
+ def load_model(self, task_name: str) -> None:
68
+ if task_name == self.task_name:
69
+ return
70
+ if self.pspex is not None:
71
+ del self.pspex
72
+ torch.cuda.empty_cache()
73
+ gc.collect()
74
+ path = self.parameters[task_name]['path']
75
+ local_path = hf_hub_download('PKUWilliamYang/StyleGANEX', path)
76
+ ckpt = torch.load(local_path, map_location='cpu')
77
+ opts = ckpt['opts']
78
+ opts['checkpoint_path'] = local_path
79
+ opts['device'] = self.device
80
+ opts = Namespace(**opts)
81
+ self.pspex = pSp(opts, ckpt).to(self.device).eval()
82
+ self.pspex.latent_avg = self.pspex.latent_avg.to(self.device)
83
+ if 'editing_w' in ckpt.keys():
84
+ self.editing_w = ckpt['editing_w'].clone().to(self.device)
85
+ self.task_name = task_name
86
+ torch.cuda.empty_cache()
87
+ gc.collect()
88
+
89
+ def load_G_model(self, model_type: str) -> None:
90
+ if model_type == self.model_type:
91
+ return
92
+ torch.cuda.empty_cache()
93
+ gc.collect()
94
+ local_path = hf_hub_download('rinong/stylegan-nada-models', model_type+'.pt')
95
+ self.generator.load_state_dict(torch.load(local_path, map_location='cpu')['g_ema'], strict=False)
96
+ self.generator.to(self.device).eval()
97
+ self.model_type = model_type
98
+ torch.cuda.empty_cache()
99
+ gc.collect()
100
+
101
+ def tensor2np(self, img):
102
+ tmp = ((img.cpu().numpy().transpose(1, 2, 0) + 1.0) * 127.5).astype(np.uint8)
103
+ return tmp
104
+
105
+ def process_sr(self, input_image: str, resize_scale: int, model: str) -> list[np.ndarray]:
106
+ #false_image = np.zeros((256,256,3), np.uint8)
107
+ #info = 'Error: no face detected! Please retry or change the photo.'
108
+
109
+ if input_image is None:
110
+ #return [false_image, false_image], 'Error: fail to load empty file.'
111
+ raise gr.Error("Error: fail to load empty file.")
112
+ frame = cv2.imread(input_image)
113
+ if frame is None:
114
+ #return [false_image, false_image], 'Error: fail to load the image.'
115
+ raise gr.Error("Error: fail to load the image.")
116
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
117
+
118
+ if model is None or model == 'SR for 32x':
119
+ task_name = 'sr-32'
120
+ resize_scale = 32
121
+ else:
122
+ task_name = 'sr'
123
+
124
+ with torch.no_grad():
125
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
126
+ if paras is None:
127
+ #return [false_image, false_image], info
128
+ raise gr.Error(self.error_info)
129
+ h,w,top,bottom,left,right,scale = paras
130
+ H, W = int(bottom-top), int(right-left)
131
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
132
+ x1 = PIL.Image.fromarray(np.uint8(frame))
133
+ x1 = augmentations.BilinearResize(factors=[resize_scale//4])(x1)
134
+ x1_up = x1.resize((W, H))
135
+ x2_up = align_face(np.array(x1_up), self.landmarkpredictor)
136
+ if x2_up is None:
137
+ #return [false_image, false_image], 'Error: no face detected! Please retry or change the photo.'
138
+ raise gr.Error(self.error_info)
139
+ x1_up = transforms.ToTensor()(x1_up).unsqueeze(dim=0).to(self.device) * 2 - 1
140
+ x2_up = self.transform(x2_up).unsqueeze(dim=0).to(self.device)
141
+ if self.print_log: print('image loaded')
142
+ self.load_model(task_name)
143
+ if self.print_log: print('model %s loaded'%(task_name))
144
+ y_hat = torch.clamp(self.pspex(x1=x1_up, x2=x2_up, use_skip=self.pspex.opts.use_skip, resize=False), -1, 1)
145
+
146
+ return [self.tensor2np(x1_up[0]), self.tensor2np(y_hat[0])]
147
+
148
+
149
+ def process_s2f(self, input_image: str, seed: int) -> np.ndarray:
150
+ task_name = 'sketch2face'
151
+ with torch.no_grad():
152
+ x1 = transforms.ToTensor()(PIL.Image.open(input_image)).unsqueeze(0).to(self.device)
153
+ if x1.shape[2] > 513:
154
+ x1 = x1[:,:,(x1.shape[2]//2-256)//8*8:(x1.shape[2]//2+256)//8*8]
155
+ if x1.shape[3] > 513:
156
+ x1 = x1[:,:,:,(x1.shape[3]//2-256)//8*8:(x1.shape[3]//2+256)//8*8]
157
+ x1 = x1[:,0:1] # uploaded files will be transformed to 3-channel RGB image!
158
+ if self.print_log: print('image loaded')
159
+ self.load_model(task_name)
160
+ if self.print_log: print('model %s loaded'%(task_name))
161
+ self.pspex.train()
162
+ torch.manual_seed(seed)
163
+ y_hat = self.pspex(x1=x1, resize=False, latent_mask=[8,9,10,11,12,13,14,15,16,17], use_skip=self.pspex.opts.use_skip,
164
+ inject_latent= self.pspex.decoder.style(torch.randn(1, 512).to(self.device)).unsqueeze(1).repeat(1,18,1) * 0.7)
165
+ y_hat = torch.clamp(y_hat, -1, 1)
166
+ self.pspex.eval()
167
+ return self.tensor2np(y_hat[0])
168
+
169
+ def process_m2f(self, input_image: str, input_type: str, seed: int) -> list[np.ndarray]:
170
+ #false_image = np.zeros((256,256,3), np.uint8)
171
+ if input_image is None:
172
+ raise gr.Error('Error: fail to load empty file.' )
173
+ #return [false_image, false_image], 'Error: fail to load empty file.'
174
+ task_name = 'mask2face'
175
+ with torch.no_grad():
176
+ if input_type == 'parsing mask':
177
+ x1 = PIL.Image.open(input_image).getchannel(0) # uploaded files will be transformed to 3-channel RGB image!
178
+ x1 = augmentations.ToOneHot(19)(x1)
179
+ x1 = transforms.ToTensor()(x1).unsqueeze(dim=0).float().to(self.device)
180
+ #print(x1.shape)
181
+ else:
182
+ frame = cv2.imread(input_image)
183
+ if frame is None:
184
+ #return [false_image, false_image], 'Error: fail to load the image.'
185
+ raise gr.Error('Error: fail to load the image.' )
186
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
187
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
188
+ if paras is None:
189
+ #return [false_image, false_image], 'Error: no face detected! Please retry or change the photo.'
190
+ raise gr.Error(self.error_info)
191
+ h,w,top,bottom,left,right,scale = paras
192
+ H, W = int(bottom-top), int(right-left)
193
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
194
+ # convert face image to segmentation mask
195
+ x1 = self.to_tensor(frame).unsqueeze(0).to(self.device)
196
+ # upsample image for precise segmentation
197
+ x1 = F.interpolate(x1, scale_factor=2, mode='bilinear')
198
+ x1 = self.maskpredictor(x1)[0]
199
+ x1 = F.interpolate(x1, scale_factor=0.5).argmax(dim=1)
200
+ x1 = F.one_hot(x1, num_classes=19).permute(0, 3, 1, 2).float().to(self.device)
201
+
202
+ if x1.shape[2] > 513:
203
+ x1 = x1[:,:,(x1.shape[2]//2-256)//8*8:(x1.shape[2]//2+256)//8*8]
204
+ if x1.shape[3] > 513:
205
+ x1 = x1[:,:,:,(x1.shape[3]//2-256)//8*8:(x1.shape[3]//2+256)//8*8]
206
+
207
+ x1_viz = (tensor2label(x1[0], 19) / 192 * 256).astype(np.uint8)
208
+
209
+ if self.print_log: print('image loaded')
210
+ self.load_model(task_name)
211
+ if self.print_log: print('model %s loaded'%(task_name))
212
+ self.pspex.train()
213
+ torch.manual_seed(seed)
214
+ y_hat = self.pspex(x1=x1, resize=False, latent_mask=[8,9,10,11,12,13,14,15,16,17], use_skip=self.pspex.opts.use_skip,
215
+ inject_latent= self.pspex.decoder.style(torch.randn(1, 512).to(self.device)).unsqueeze(1).repeat(1,18,1) * 0.7)
216
+ y_hat = torch.clamp(y_hat, -1, 1)
217
+ self.pspex.eval()
218
+ return [x1_viz, self.tensor2np(y_hat[0])]
219
+
220
+
221
+ def process_editing(self, input_image: str, scale_factor: float, model_type: str) -> np.ndarray:
222
+ #false_image = np.zeros((256,256,3), np.uint8)
223
+ #info = 'Error: no face detected! Please retry or change the photo.'
224
+
225
+ if input_image is None:
226
+ #return false_image, false_image, 'Error: fail to load empty file.'
227
+ raise gr.Error('Error: fail to load empty file.')
228
+ frame = cv2.imread(input_image)
229
+ if frame is None:
230
+ #return false_image, false_image, 'Error: fail to load the image.'
231
+ raise gr.Error('Error: fail to load the image.')
232
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
233
+
234
+ if model_type is None or model_type == 'reduce age':
235
+ task_name = 'edit_age'
236
+ else:
237
+ task_name = 'edit_hair'
238
+
239
+ with torch.no_grad():
240
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
241
+ if paras is None:
242
+ #return false_image, false_image, info
243
+ raise gr.Error(self.error_info)
244
+ h,w,top,bottom,left,right,scale = paras
245
+ H, W = int(bottom-top), int(right-left)
246
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
247
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
248
+ x2 = align_face(frame, self.landmarkpredictor)
249
+ if x2 is None:
250
+ #return false_image, 'Error: no face detected! Please retry or change the photo.'
251
+ raise gr.Error(self.error_info)
252
+ x2 = self.transform(x2).unsqueeze(dim=0).to(self.device)
253
+ if self.print_log: print('image loaded')
254
+ self.load_model(task_name)
255
+ if self.print_log: print('model %s loaded'%(task_name))
256
+ y_hat = self.pspex(x1=x1, x2=x2, use_skip=self.pspex.opts.use_skip, zero_noise=True,
257
+ resize=False, editing_w= - scale_factor* self.editing_w[0:1])
258
+ y_hat = torch.clamp(y_hat, -1, 1)
259
+
260
+ return self.tensor2np(y_hat[0])
261
+
262
+ def process_vediting(self, input_video: str, scale_factor: float, model_type: str, frame_num: int) -> tuple[list[np.ndarray], str]:
263
+ #false_image = np.zeros((256,256,3), np.uint8)
264
+ #info = 'Error: no face detected! Please retry or change the video.'
265
+
266
+ if input_video is None:
267
+ #return [false_image], 'default.mp4', 'Error: fail to load empty file.'
268
+ raise gr.Error('Error: fail to load empty file.')
269
+ video_cap = cv2.VideoCapture(input_video)
270
+ success, frame = video_cap.read()
271
+ if success is False:
272
+ #return [false_image], 'default.mp4', 'Error: fail to load the video.'
273
+ raise gr.Error('Error: fail to load the video.')
274
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
275
+
276
+ if model_type is None or model_type == 'reduce age':
277
+ task_name = 'edit_age'
278
+ else:
279
+ task_name = 'edit_hair'
280
+
281
+ with torch.no_grad():
282
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
283
+ if paras is None:
284
+ #return [false_image], 'default.mp4', info
285
+ raise gr.Error(self.error_info)
286
+ h,w,top,bottom,left,right,scale = paras
287
+ H, W = int(bottom-top), int(right-left)
288
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
289
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
290
+ x2 = align_face(frame, self.landmarkpredictor)
291
+ if x2 is None:
292
+ #return [false_image], 'default.mp4', info
293
+ raise gr.Error(self.error_info)
294
+ x2 = self.transform(x2).unsqueeze(dim=0).to(self.device)
295
+ if self.print_log: print('first frame loaded')
296
+ self.load_model(task_name)
297
+ if self.print_log: print('model %s loaded'%(task_name))
298
+
299
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
300
+ videoWriter = cv2.VideoWriter('output.mp4', fourcc, video_cap.get(5), (4*W, 4*H))
301
+
302
+ viz_frames = []
303
+ for i in range(frame_num):
304
+ if i > 0:
305
+ success, frame = video_cap.read()
306
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
307
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
308
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
309
+ y_hat = self.pspex(x1=x1, x2=x2, use_skip=self.pspex.opts.use_skip, zero_noise=True,
310
+ resize=False, editing_w= - scale_factor * self.editing_w[0:1])
311
+ y_hat = torch.clamp(y_hat, -1, 1)
312
+ videoWriter.write(tensor2cv2(y_hat[0].cpu()))
313
+ if i < min(frame_num, 4):
314
+ viz_frames += [self.tensor2np(y_hat[0])]
315
+
316
+ videoWriter.release()
317
+
318
+ return viz_frames, 'output.mp4'
319
+
320
+
321
+ def process_toonify(self, input_image: str, style_type: str) -> np.ndarray:
322
+ #false_image = np.zeros((256,256,3), np.uint8)
323
+ #info = 'Error: no face detected! Please retry or change the photo.'
324
+
325
+ if input_image is None:
326
+ raise gr.Error('Error: fail to load empty file.')
327
+ #return false_image, false_image, 'Error: fail to load empty file.'
328
+ frame = cv2.imread(input_image)
329
+ if frame is None:
330
+ raise gr.Error('Error: fail to load the image.')
331
+ #return false_image, false_image, 'Error: fail to load the image.'
332
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
333
+
334
+ if style_type is None or style_type == 'Pixar':
335
+ task_name = 'toonify_pixar'
336
+ elif style_type == 'Cartoon':
337
+ task_name = 'toonify_cartoon'
338
+ else:
339
+ task_name = 'toonify_arcane'
340
+
341
+ with torch.no_grad():
342
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
343
+ if paras is None:
344
+ raise gr.Error(self.error_info)
345
+ #return false_image, false_image, info
346
+ h,w,top,bottom,left,right,scale = paras
347
+ H, W = int(bottom-top), int(right-left)
348
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
349
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
350
+ x2 = align_face(frame, self.landmarkpredictor)
351
+ if x2 is None:
352
+ raise gr.Error(self.error_info)
353
+ #return false_image, 'Error: no face detected! Please retry or change the photo.'
354
+ x2 = self.transform(x2).unsqueeze(dim=0).to(self.device)
355
+ if self.print_log: print('image loaded')
356
+ self.load_model(task_name)
357
+ if self.print_log: print('model %s loaded'%(task_name))
358
+ y_hat = self.pspex(x1=x1, x2=x2, use_skip=self.pspex.opts.use_skip, zero_noise=True, resize=False)
359
+ y_hat = torch.clamp(y_hat, -1, 1)
360
+
361
+ return self.tensor2np(y_hat[0]), 'Done!'
362
+
363
+
364
+ def process_vtoonify(self, input_video: str, style_type: str, frame_num: int) -> tuple[list[np.ndarray], str]:
365
+ #false_image = np.zeros((256,256,3), np.uint8)
366
+ #info = 'Error: no face detected! Please retry or change the video.'
367
+
368
+ if input_video is None:
369
+ raise gr.Error('Error: fail to load empty file.')
370
+ #return [false_image], 'default.mp4', 'Error: fail to load empty file.'
371
+ video_cap = cv2.VideoCapture(input_video)
372
+ success, frame = video_cap.read()
373
+ if success is False:
374
+ raise gr.Error('Error: fail to load the video.')
375
+ #return [false_image], 'default.mp4', 'Error: fail to load the video.'
376
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
377
+
378
+ if style_type is None or style_type == 'Pixar':
379
+ task_name = 'toonify_pixar'
380
+ elif style_type == 'Cartoon':
381
+ task_name = 'toonify_cartoon'
382
+ else:
383
+ task_name = 'toonify_arcane'
384
+
385
+ with torch.no_grad():
386
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
387
+ if paras is None:
388
+ raise gr.Error(self.error_info)
389
+ #return [false_image], 'default.mp4', info
390
+ h,w,top,bottom,left,right,scale = paras
391
+ H, W = int(bottom-top), int(right-left)
392
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
393
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
394
+ x2 = align_face(frame, self.landmarkpredictor)
395
+ if x2 is None:
396
+ raise gr.Error(self.error_info)
397
+ #return [false_image], 'default.mp4', info
398
+ x2 = self.transform(x2).unsqueeze(dim=0).to(self.device)
399
+ if self.print_log: print('first frame loaded')
400
+ self.load_model(task_name)
401
+ if self.print_log: print('model %s loaded'%(task_name))
402
+
403
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
404
+ videoWriter = cv2.VideoWriter('output.mp4', fourcc, video_cap.get(5), (4*W, 4*H))
405
+
406
+ viz_frames = []
407
+ for i in range(frame_num):
408
+ if i > 0:
409
+ success, frame = video_cap.read()
410
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
411
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
412
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
413
+ y_hat = self.pspex(x1=x1, x2=x2, use_skip=self.pspex.opts.use_skip, zero_noise=True, resize=False)
414
+ y_hat = torch.clamp(y_hat, -1, 1)
415
+ videoWriter.write(tensor2cv2(y_hat[0].cpu()))
416
+ if i < min(frame_num, 4):
417
+ viz_frames += [self.tensor2np(y_hat[0])]
418
+
419
+ videoWriter.release()
420
+
421
+ return viz_frames, 'output.mp4'
422
+
423
+
424
+ def process_inversion(self, input_image: str, optimize: str, input_latent: file-object, editing_options: str,
425
+ scale_factor: float, seed: int) -> tuple[np.ndarray, np.ndarray]:
426
+ #false_image = np.zeros((256,256,3), np.uint8)
427
+ #info = 'Error: no face detected! Please retry or change the photo.'
428
+
429
+ if input_image is None:
430
+ raise gr.Error('Error: fail to load empty file.')
431
+ #return false_image, false_image, 'Error: fail to load empty file.'
432
+ frame = cv2.imread(input_image)
433
+ if frame is None:
434
+ raise gr.Error('Error: fail to load the image.')
435
+ #return false_image, false_image, 'Error: fail to load the image.'
436
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
437
+
438
+ task_name = 'inversion'
439
+ self.load_model(task_name)
440
+ if self.print_log: print('model %s loaded'%(task_name))
441
+ if input_latent is not None:
442
+ if '.pt' not in input_latent.name:
443
+ raise gr.Error('Error: the latent format is wrong')
444
+ #return false_image, false_image, 'Error: the latent format is wrong'
445
+ latents = torch.load(input_latent.name)
446
+ if 'wplus' not in latents.keys() or 'f' not in latents.keys():
447
+ raise gr.Error('Error: the latent format is wrong')
448
+ #return false_image, false_image, 'Error: the latent format is wrong'
449
+ wplus = latents['wplus'].to(self.device) # w+
450
+ f = [latents['f'][0].to(self.device)] # f
451
+ elif optimize == 'Latent optimization':
452
+ wplus, f, _, _, _ = latent_optimization(frame, self.pspex, self.landmarkpredictor,
453
+ step=500, device=self.device)
454
+ else:
455
+ with torch.no_grad():
456
+ paras = get_video_crop_parameter(frame, self.landmarkpredictor)
457
+ if paras is None:
458
+ raise gr.Error(self.error_info)
459
+ #return false_image, false_image, info
460
+ h,w,top,bottom,left,right,scale = paras
461
+ H, W = int(bottom-top), int(right-left)
462
+ frame = cv2.resize(frame, (w, h))[top:bottom, left:right]
463
+ x1 = self.transform(frame).unsqueeze(0).to(self.device)
464
+ x2 = align_face(frame, self.landmarkpredictor)
465
+ if x2 is None:
466
+ raise gr.Error(self.error_info)
467
+ #return false_image, false_image, 'Error: no face detected! Please retry or change the photo.'
468
+ x2 = self.transform(x2).unsqueeze(dim=0).to(self.device)
469
+ if self.print_log: print('image loaded')
470
+ wplus = self.pspex.encoder(x2) + self.pspex.latent_avg.unsqueeze(0)
471
+ _, f = self.pspex.encoder(x1, return_feat=True)
472
+
473
+ with torch.no_grad():
474
+ y_hat, _ = self.pspex.decoder([wplus], input_is_latent=True, first_layer_feature=f)
475
+ y_hat = torch.clamp(y_hat, -1, 1)
476
+
477
+ if 'Style Mixing' in editing_options:
478
+ torch.manual_seed(seed)
479
+ wplus[:, 8:] = self.pspex.decoder.style(torch.randn(1, 512).to(self.device)).unsqueeze(1).repeat(1,10,1) * 0.7
480
+ y_hat_edit, _ = self.pspex.decoder([wplus], input_is_latent=True, first_layer_feature=f)
481
+ elif 'Attribute Editing' in editing_options:
482
+ editing_w = self.editing_dicts[editing_options[19:]].to(self.device)
483
+ y_hat_edit, _ = self.pspex.decoder([wplus+scale_factor*editing_w], input_is_latent=True, first_layer_feature=f)
484
+ elif 'Domain Transfer' in editing_options:
485
+ self.load_G_model(editing_options[17:])
486
+ if self.print_log: print('model %s loaded'%(editing_options[17:]))
487
+ y_hat_edit, _ = self.generator([wplus], input_is_latent=True, first_layer_feature=f)
488
+ else:
489
+ y_hat_edit = y_hat
490
+ y_hat_edit = torch.clamp(y_hat_edit, -1, 1)
491
+
492
+ return self.tensor2np(y_hat[0]), self.tensor2np(y_hat_edit[0])