gavinyuan commited on
Commit
de68d44
1 Parent(s): d252b8a

add: app.py

Browse files
Files changed (1) hide show
  1. app.py +470 -0
app.py ADDED
@@ -0,0 +1,470 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import glob
4
+ import shutil
5
+ from pathlib import Path
6
+ from multiprocessing.pool import Pool
7
+
8
+ import gradio as gr
9
+ import torch
10
+ from torchvision import transforms
11
+
12
+ import cv2
13
+ import numpy as np
14
+ from PIL import Image
15
+ import tqdm
16
+
17
+ # from modules.networks.faceshifter import FSGenerator
18
+ # from inference.alignment import norm_crop, norm_crop_with_M, paste_back
19
+ # from inference.utils import save, get_5_from_98, get_detector, get_lmk
20
+ # from inference.PIPNet.lib.tools import get_lmk_model, demo_image
21
+ # from inference.landmark_smooth import kalman_filter_landmark, savgol_filter_landmark
22
+ # from tricks import Trick
23
+
24
+ # make_abs_path = lambda fn: os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), fn))
25
+ #
26
+ #
27
+ # fs_model_name = 'faceshifter'
28
+ # in_size = 512
29
+ #
30
+ # mouth_net_param = {
31
+ # "use": True,
32
+ # "feature_dim": 128,
33
+ # "crop_param": (28, 56, 84, 112),
34
+ # "weight_path": "../../modules/third_party/arcface/weights/mouth_net_28_56_84_112.pth",
35
+ # }
36
+ # trick = Trick()
37
+ #
38
+ # T = transforms.Compose(
39
+ # [
40
+ # transforms.ToTensor(),
41
+ # transforms.Normalize(0.5, 0.5),
42
+ # ]
43
+ # )
44
+ # tensor2pil_transform = transforms.ToPILImage()
45
+ #
46
+ #
47
+ # def extract_generator(ckpt: str, pt: str):
48
+ # print(f'[extract_generator] loading ckpt...')
49
+ # from trainer.faceshifter.faceshifter_pl import FaceshifterPL512, FaceshifterPL
50
+ # import yaml
51
+ # with open(make_abs_path('../../trainer/faceshifter/config.yaml'), 'r') as f:
52
+ # config = yaml.load(f, Loader=yaml.FullLoader)
53
+ # config['mouth_net'] = mouth_net_param
54
+ #
55
+ # if in_size == 256:
56
+ # net = FaceshifterPL(n_layers=3, num_D=3, config=config)
57
+ # elif in_size == 512:
58
+ # net = FaceshifterPL512(n_layers=3, num_D=3, config=config, verbose=False)
59
+ # else:
60
+ # raise ValueError('Not supported in_size.')
61
+ # checkpoint = torch.load(ckpt, map_location="cpu", )
62
+ # net.load_state_dict(checkpoint["state_dict"], strict=False)
63
+ # net.eval()
64
+ #
65
+ # G = net.generator
66
+ # torch.save(G.state_dict(), pt)
67
+ # print(f'[extract_generator] extracted from {ckpt}, pth saved to {pt}')
68
+ #
69
+ #
70
+ # ''' load model '''
71
+ # if fs_model_name == 'faceshifter':
72
+ # # pt_path = make_abs_path("../ffplus/extracted_ckpt/G_mouth1_t38.pth")
73
+ # # pt_path = make_abs_path("../ffplus/extracted_ckpt/G_mouth1_t512_6.pth")
74
+ # # ckpt_path = "/apdcephfs/share_1290939/gavinyuan/out/triplet512_6/epoch=3-step=128999.ckpt"
75
+ # pt_path = make_abs_path("../ffplus/extracted_ckpt/G_mouth1_t512_4.pth")
76
+ # ckpt_path = "/apdcephfs/share_1290939/gavinyuan/out/triplet512_4/epoch=2-step=185999.ckpt"
77
+ # if not os.path.exists(pt_path) or 't512' in pt_path:
78
+ # extract_generator(ckpt_path, pt_path)
79
+ # fs_model = FSGenerator(
80
+ # make_abs_path("../../modules/third_party/arcface/weights/ms1mv3_arcface_r100_fp16/backbone.pth"),
81
+ # mouth_net_param=mouth_net_param,
82
+ # in_size=in_size,
83
+ # downup=in_size == 512,
84
+ # )
85
+ # fs_model.load_state_dict(torch.load(pt_path, "cpu"), strict=True)
86
+ # fs_model.eval()
87
+ #
88
+ # @torch.no_grad()
89
+ # def infer_batch_to_img(i_s, i_t, post: bool = False):
90
+ # i_r = fs_model(i_s, i_t)[0] # x, id_vector, att
91
+ #
92
+ # if post:
93
+ # target_hair_mask = trick.get_any_mask(i_t, par=[0, 17])
94
+ # target_hair_mask = trick.smooth_mask(target_hair_mask)
95
+ # i_r = target_hair_mask * i_t + (target_hair_mask * (-1) + 1) * i_r
96
+ # i_r = trick.finetune_mouth(i_s, i_t, i_r) if in_size == 256 else i_r
97
+ #
98
+ # img_r = trick.tensor_to_arr(i_r)[0]
99
+ # return img_r
100
+ #
101
+ # elif fs_model_name == 'simswap_triplet' or fs_model_name == 'simswap_vanilla':
102
+ # from modules.networks.simswap import Generator_Adain_Upsample
103
+ # sw_model = Generator_Adain_Upsample(
104
+ # input_nc=3, output_nc=3, latent_size=512, n_blocks=9, deep=False,
105
+ # mouth_net_param=mouth_net_param
106
+ # )
107
+ # if fs_model_name == 'simswap_triplet':
108
+ # pt_path = make_abs_path("../ffplus/extracted_ckpt/G_mouth1_st5.pth")
109
+ # ckpt_path = make_abs_path("/apdcephfs/share_1290939/gavinyuan/out/"
110
+ # "simswap_triplet_5/epoch=12-step=782999.ckpt")
111
+ # elif fs_model_name == 'simswap_vanilla':
112
+ # pt_path = make_abs_path("../ffplus/extracted_ckpt/G_tmp_sv4_off.pth")
113
+ # ckpt_path = make_abs_path("/apdcephfs/share_1290939/gavinyuan/out/"
114
+ # "simswap_vanilla_4/epoch=694-step=1487999.ckpt")
115
+ # else:
116
+ # pt_path = None
117
+ # ckpt_path = None
118
+ # sw_model.load_state_dict(torch.load(pt_path, "cpu"), strict=False)
119
+ # sw_model.eval()
120
+ # fs_model = sw_model
121
+ #
122
+ # from trainer.simswap.simswap_pl import SimSwapPL
123
+ # import yaml
124
+ # with open(make_abs_path('../../trainer/simswap/config.yaml'), 'r') as f:
125
+ # config = yaml.load(f, Loader=yaml.FullLoader)
126
+ # config['mouth_net'] = mouth_net_param
127
+ # net = SimSwapPL(config=config, use_official_arc='off' in pt_path)
128
+ #
129
+ # checkpoint = torch.load(ckpt_path, map_location="cpu")
130
+ # net.load_state_dict(checkpoint["state_dict"], strict=False)
131
+ # net.eval()
132
+ # sw_mouth_net = net.mouth_net # maybe None
133
+ # sw_netArc = net.netArc
134
+ # fs_model = fs_model.cuda()
135
+ # sw_mouth_net = sw_mouth_net.cuda() if sw_mouth_net is not None else sw_mouth_net
136
+ # sw_netArc = sw_netArc.cuda()
137
+ #
138
+ # @torch.no_grad()
139
+ # def infer_batch_to_img(i_s, i_t, post: bool = False):
140
+ # i_r = fs_model(source=i_s, target=i_t, net_arc=sw_netArc, mouth_net=sw_mouth_net,)
141
+ # if post:
142
+ # target_hair_mask = trick.get_any_mask(i_t, par=[0, 17])
143
+ # target_hair_mask = trick.smooth_mask(target_hair_mask)
144
+ # i_r = target_hair_mask * i_t + (target_hair_mask * (-1) + 1) * i_r
145
+ # i_r = i_r.clamp(-1, 1)
146
+ # i_r = trick.tensor_to_arr(i_r)[0]
147
+ # return i_r
148
+ #
149
+ # elif fs_model_name == 'simswap_official':
150
+ # from simswap.image_infer import SimSwapOfficialImageInfer
151
+ # fs_model = SimSwapOfficialImageInfer()
152
+ # pt_path = 'Simswap Official'
153
+ # mouth_net_param = {
154
+ # "use": False
155
+ # }
156
+ #
157
+ # @torch.no_grad()
158
+ # def infer_batch_to_img(i_s, i_t):
159
+ # i_r = fs_model.image_infer(source_tensor=i_s, target_tensor=i_t)
160
+ # i_r = i_r.clamp(-1, 1)
161
+ # return i_r
162
+ #
163
+ # else:
164
+ # raise ValueError('Not supported fs_model_name.')
165
+ #
166
+ #
167
+ # print(f'[demo] model loaded from {pt_path}')
168
+
169
+
170
+ def swap_image(
171
+ source_image,
172
+ target_path,
173
+ out_path,
174
+ transform,
175
+ G,
176
+ align_source="arcface",
177
+ align_target="set1",
178
+ gpu_mode=True,
179
+ paste_back=True,
180
+ use_post=False,
181
+ use_gpen=False,
182
+ in_size=256,
183
+ ):
184
+ name = target_path.split("/")[-1]
185
+ name = "out_" + name
186
+ if isinstance(G, torch.nn.Module):
187
+ G.eval()
188
+ if gpu_mode:
189
+ G = G.cuda()
190
+ source_img = np.array(Image.open(source_image).convert("RGB"))
191
+ net, detector = get_lmk_model()
192
+ lmk = get_5_from_98(demo_image(source_img, net, detector)[0])
193
+ source_img = norm_crop(source_img, lmk, in_size, mode=align_source, borderValue=0.0)
194
+ source_img = transform(source_img).unsqueeze(0)
195
+
196
+ target = np.array(Image.open(target_path).convert("RGB"))
197
+ original_target = target.copy()
198
+ lmk = get_5_from_98(demo_image(target, net, detector)[0])
199
+ target, M = norm_crop_with_M(target, lmk, in_size, mode=align_target, borderValue=0.0)
200
+ target = transform(target).unsqueeze(0)
201
+ if gpu_mode:
202
+ target = target.cuda()
203
+ source_img = source_img.cuda()
204
+
205
+ cv2.imwrite('cropped_source.png', trick.tensor_to_arr(source_img)[0, :, :, ::-1])
206
+ cv2.imwrite('cropped_target.png', trick.tensor_to_arr(target)[0, :, :, ::-1])
207
+
208
+ # both inputs should be 512
209
+ result = infer_batch_to_img(source_img, target, post=use_post)
210
+
211
+ cv2.imwrite('result.png', result[:, :, ::-1])
212
+
213
+ os.makedirs(out_path, exist_ok=True)
214
+ Image.fromarray(result.astype(np.uint8)).save(os.path.join(out_path, name))
215
+ save((result, M, original_target, os.path.join(out_path, "paste_back_" + name), None),
216
+ trick=trick, use_post=use_gpen)
217
+
218
+
219
+ def process_video(
220
+ source_image,
221
+ target_path,
222
+ out_path,
223
+ transform,
224
+ G,
225
+ align_source="arcface",
226
+ align_target="set1",
227
+ gpu_mode=True,
228
+ frames=9999999,
229
+ use_tddfav2=False,
230
+ landmark_smooth="kalman",
231
+ ):
232
+ if isinstance(G, torch.nn.Module):
233
+ G.eval()
234
+ if gpu_mode:
235
+ G = G.cuda()
236
+ ''' Target video to frames (.png) '''
237
+ fps = 25.0
238
+ if not os.path.isdir(target_path):
239
+ vidcap = cv2.VideoCapture(target_path)
240
+ fps = vidcap.get(cv2.CAP_PROP_FPS)
241
+ try:
242
+ for match in glob.glob(os.path.join("./tmp/", "*.png")):
243
+ os.remove(match)
244
+ for match in glob.glob(os.path.join(out_path, "*.png")):
245
+ os.remove(match)
246
+ except Exception as e:
247
+ print(e)
248
+ os.makedirs("./tmp/", exist_ok=True)
249
+ os.system(
250
+ f"ffmpeg -i {target_path} -qscale:v 1 -qmin 1 -qmax 1 -vsync 0 ./tmp/frame_%05d.png"
251
+ )
252
+ target_path = "./tmp/"
253
+ globbed_images = sorted(glob.glob(os.path.join(target_path, "*.png")))
254
+ ''' Get target landmarks '''
255
+ print('[Extracting target landmarks...]')
256
+ if not use_tddfav2:
257
+ align_net, align_detector = get_lmk_model()
258
+ else:
259
+ align_net, align_detector = get_detector(gpu_mode=gpu_mode)
260
+ target_lmks = []
261
+ for frame_path in tqdm.tqdm(globbed_images):
262
+ target = np.array(Image.open(frame_path).convert("RGB"))
263
+ lmk = demo_image(target, align_net, align_detector)
264
+ lmk = lmk[0]
265
+ target_lmks.append(lmk)
266
+ ''' Landmark smoothing '''
267
+ target_lmks = np.array(target_lmks, np.float32) # (#frames, 98, 2)
268
+ if landmark_smooth == 'kalman':
269
+ target_lmks = kalman_filter_landmark(target_lmks,
270
+ process_noise=0.01,
271
+ measure_noise=0.01).astype(np.int)
272
+ elif landmark_smooth == 'savgol':
273
+ target_lmks = savgol_filter_landmark(target_lmks).astype(np.int)
274
+ elif landmark_smooth == 'cancel':
275
+ target_lmks = target_lmks.astype(np.int)
276
+ else:
277
+ raise KeyError('Not supported landmark_smooth choice')
278
+ ''' Crop source image '''
279
+ source_img = np.array(Image.open(source_image).convert("RGB"))
280
+ if not use_tddfav2:
281
+ lmk = get_5_from_98(demo_image(source_img, align_net, align_detector)[0])
282
+ else:
283
+ lmk = get_lmk(source_img, align_net, align_detector)
284
+ source_img = norm_crop(source_img, lmk, in_size, mode=align_source, borderValue=0.0)
285
+ source_img = transform(source_img).unsqueeze(0)
286
+ if gpu_mode:
287
+ source_img = source_img.cuda()
288
+ ''' Process by frames '''
289
+ targets = []
290
+ t_facial_masks = []
291
+ Ms = []
292
+ original_frames = []
293
+ names = []
294
+ count = 0
295
+ for image in tqdm.tqdm(globbed_images):
296
+ names.append(os.path.join(out_path, Path(image).name))
297
+ target = np.array(Image.open(image).convert("RGB"))
298
+ original_frames.append(target)
299
+ ''' Crop target frames '''
300
+ lmk = get_5_from_98(target_lmks[count])
301
+ target, M = norm_crop_with_M(target, lmk, in_size, mode=align_target, borderValue=0.0)
302
+ target = transform(target).unsqueeze(0) # in [-1,1]
303
+ if gpu_mode:
304
+ target = target.cuda()
305
+ ''' Finetune paste masks '''
306
+ target_facial_mask = trick.get_any_mask(target,
307
+ par=[1, 2, 3, 4, 5, 6, 10, 11, 12, 13]).squeeze() # in [0,1]
308
+ target_facial_mask = target_facial_mask.cpu().numpy().astype(np.float)
309
+ target_facial_mask = trick.finetune_mask(target_facial_mask, target_lmks) # in [0,1]
310
+ t_facial_masks.append(target_facial_mask)
311
+ ''' Face swapping '''
312
+ with torch.no_grad():
313
+ if 'faceshifter' in fs_model_name:
314
+ output = G(source_img, target)
315
+ target_hair_mask = trick.get_any_mask(target, par=[0, 17])
316
+ target_hair_mask = trick.smooth_mask(target_hair_mask)
317
+ output = target_hair_mask * target + (target_hair_mask * (-1) + 1) * output
318
+ output = trick.finetune_mouth(source_img, target, output)
319
+ elif 'simswap' in fs_model_name and 'official' not in fs_model_name:
320
+ output = fs_model(source=source_img, target=target,
321
+ net_arc=sw_netArc, mouth_net=sw_mouth_net,)
322
+ if 'vanilla' not in fs_model_name:
323
+ target_hair_mask = trick.get_any_mask(target, par=[0, 17])
324
+ target_hair_mask = trick.smooth_mask(target_hair_mask)
325
+ output = target_hair_mask * target + (target_hair_mask * (-1) + 1) * output
326
+ output = trick.finetune_mouth(source_img, target, output)
327
+ output = output.clamp(-1, 1)
328
+ elif 'simswap_official' in fs_model_name:
329
+ output = fs_model.image_infer(source_tensor=source_img, target_tensor=target)
330
+ output = output.clamp(-1, 1)
331
+ if isinstance(output, tuple):
332
+ target = output[0][0] * 0.5 + 0.5
333
+ else:
334
+ target = output[0] * 0.5 + 0.5
335
+ targets.append(np.array(tensor2pil_transform(target)))
336
+ Ms.append(M)
337
+ count += 1
338
+ if count > frames:
339
+ break
340
+ os.makedirs(out_path, exist_ok=True)
341
+ return targets, t_facial_masks, Ms, original_frames, names, fps
342
+
343
+
344
+ def swap_image_gr(img1, img2, use_post=False, use_gpen=False, gpu_mode=True):
345
+ root_dir = make_abs_path("./online_data")
346
+ req_id = uuid.uuid1().hex
347
+ data_dir = os.path.join(root_dir, req_id)
348
+ os.makedirs(data_dir, exist_ok=True)
349
+ source_path = os.path.join(data_dir, "source.png")
350
+ target_path = os.path.join(data_dir, "target.png")
351
+ filename = "paste_back_out_target.png"
352
+ out_path = os.path.join(data_dir, filename)
353
+ cv2.imwrite(source_path, img1[:, :, ::-1])
354
+ cv2.imwrite(target_path, img2[:, :, ::-1])
355
+ swap_image(
356
+ source_path,
357
+ target_path,
358
+ data_dir,
359
+ T,
360
+ fs_model,
361
+ gpu_mode=gpu_mode,
362
+ align_target='ffhq',
363
+ align_source='ffhq',
364
+ use_post=use_post,
365
+ use_gpen=use_gpen,
366
+ in_size=in_size,
367
+ )
368
+ out = cv2.imread(out_path)[..., ::-1]
369
+ return out
370
+
371
+
372
+ def swap_video_gr(img1, target_path, use_gpu=True, frames=9999999):
373
+ root_dir = make_abs_path("./online_data")
374
+ req_id = uuid.uuid1().hex
375
+ data_dir = os.path.join(root_dir, req_id)
376
+ os.makedirs(data_dir, exist_ok=True)
377
+ source_path = os.path.join(data_dir, "source.png")
378
+ cv2.imwrite(source_path, img1[:, :, ::-1])
379
+ out_dir = os.path.join(data_dir, "out")
380
+ out_name = "output.mp4"
381
+ targets, t_facial_masks, Ms, original_frames, names, fps = process_video(
382
+ source_path,
383
+ target_path,
384
+ out_dir,
385
+ T,
386
+ fs_model,
387
+ gpu_mode=use_gpu,
388
+ frames=frames,
389
+ align_target='ffhq',
390
+ align_source='ffhq',
391
+ use_tddfav2=False,
392
+ )
393
+
394
+ pool_process = 170
395
+ audio = True
396
+ concat = False
397
+
398
+ if pool_process <= 1:
399
+ for target, M, original_target, name, t_facial_mask in tqdm.tqdm(
400
+ zip(targets, Ms, original_frames, names, t_facial_masks)
401
+ ):
402
+ if M is None or target is None:
403
+ Image.fromarray(original_target.astype(np.uint8)).save(name)
404
+ continue
405
+ Image.fromarray(paste_back(np.array(target), M, original_target, t_facial_mask)).save(name)
406
+ else:
407
+ with Pool(pool_process) as pool:
408
+ pool.map(save, zip(targets, Ms, original_frames, names, t_facial_masks))
409
+
410
+ video_save_path = os.path.join(out_dir, out_name)
411
+ if audio:
412
+ print("use audio")
413
+ os.system(
414
+ f"ffmpeg -y -r {fps} -i {out_dir}/frame_%05d.png -i {target_path}"
415
+ f" -map 0:v:0 -map 1:a:0? -c:a copy -c:v libx264 -r {fps} -crf 10 -pix_fmt yuv420p {video_save_path}"
416
+ )
417
+ else:
418
+ print("no audio")
419
+ os.system(
420
+ f"ffmpeg -y -r {fps} -i ./tmp/frame_%05d.png "
421
+ f"-c:v libx264 -r {fps} -crf 10 -pix_fmt yuv420p {video_save_path}"
422
+ )
423
+ # ffmpeg -i left.mp4 -i right.mp4 -filter_complex hstack output.mp4
424
+ if concat:
425
+ concat_video_save_path = os.path.join(out_dir, "concat_" + out_name)
426
+ os.system(
427
+ f"ffmpeg -y -i {target_path} -i {video_save_path} -filter_complex hstack {concat_video_save_path}"
428
+ )
429
+ # delete tmp file
430
+ shutil.rmtree("./tmp/")
431
+ for match in glob.glob(os.path.join(out_dir, "*.png")):
432
+ os.remove(match)
433
+ print(video_save_path)
434
+ return video_save_path
435
+
436
+
437
+ if __name__ == "__main__":
438
+ with gr.Blocks() as demo:
439
+ gr.Markdown("SuperSwap")
440
+
441
+ with gr.Tab("Image"):
442
+ with gr.Row(equal_height=True):
443
+ with gr.Column(scale=3):
444
+ image1_input = gr.Image()
445
+ image2_input = gr.Image()
446
+ use_post = gr.Checkbox(label="后处理")
447
+ use_gpen = gr.Checkbox(label="超分增强")
448
+ with gr.Column(scale=2):
449
+ image_output = gr.Image()
450
+ image_button = gr.Button("换脸")
451
+ with gr.Tab("Video"):
452
+ with gr.Row(equal_height=True):
453
+ with gr.Column(scale=3):
454
+ image3_input = gr.Image()
455
+ video_input = gr.Video()
456
+ with gr.Column(scale=2):
457
+ video_output = gr.Video()
458
+ video_button = gr.Button("换脸")
459
+ image_button.click(
460
+ swap_image_gr,
461
+ inputs=[image1_input, image2_input, use_post, use_gpen],
462
+ outputs=image_output,
463
+ )
464
+ video_button.click(
465
+ swap_video_gr,
466
+ inputs=[image3_input, video_input],
467
+ outputs=video_output,
468
+ )
469
+
470
+ demo.launch(server_name="0.0.0.0", server_port=7860)