Spaces:
Runtime error
Runtime error
add all files except ckpt files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- FONT/.idea/.gitignore +8 -0
- FONT/.idea/EAMM-main.iml +15 -0
- FONT/.idea/deployment.xml +15 -0
- FONT/.idea/inspectionProfiles/Project_Default.xml +22 -0
- FONT/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- FONT/.idea/misc.xml +4 -0
- FONT/.idea/modules.xml +8 -0
- FONT/.idea/sshConfigs.xml +8 -0
- FONT/3DDFA_V2/demo.py +246 -0
- FONT/3DDFA_V2/utils/pose.py +284 -0
- FONT/LICENSE +21 -0
- FONT/M003_template.npy +3 -0
- FONT/README.md +67 -0
- FONT/audio.py +145 -0
- FONT/augmentation.py +430 -0
- FONT/config/MEAD_emo_video_aug_delta_4_crop_random_crop.yaml +105 -0
- FONT/config/train_part1.yaml +83 -0
- FONT/config/train_part1_fine_tune.yaml +81 -0
- FONT/config/train_part1_fine_tune_hdtf.yaml +81 -0
- FONT/config/train_part1_hdtf.yaml +83 -0
- FONT/config/train_part2.yaml +93 -0
- FONT/demo.py +632 -0
- FONT/filter1.py +48 -0
- FONT/frames_dataset.py +539 -0
- FONT/frames_dataset_liujin.py +727 -0
- FONT/gradio_demo.py +560 -0
- FONT/hparams.py +101 -0
- FONT/logger.py +225 -0
- FONT/modules/conv.py +44 -0
- FONT/modules/dense_motion.py +113 -0
- FONT/modules/discriminator.py +95 -0
- FONT/modules/frames_dataset.py +451 -0
- FONT/modules/function.py +75 -0
- FONT/modules/generator.py +97 -0
- FONT/modules/keypoint_detector.py +260 -0
- FONT/modules/model.py +597 -0
- FONT/modules/model1.py +539 -0
- FONT/modules/model_delta_map.py +500 -0
- FONT/modules/model_gen.py +516 -0
- FONT/modules/ops.py +77 -0
- FONT/modules/stylegan2.py +923 -0
- FONT/modules/util.py +2008 -0
- FONT/ops.py +96 -0
- FONT/process_data.py +143 -0
- FONT/requirements.txt +16 -0
- FONT/result/all.mov +3 -0
- FONT/result/all_with_pose.mov +3 -0
- FONT/run.py +137 -0
- FONT/script_backup.sh +17 -0
- FONT/script_finetune_liujin.sh +1 -0
FONT/.idea/.gitignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Default ignored files
|
| 2 |
+
/shelf/
|
| 3 |
+
/workspace.xml
|
| 4 |
+
# Datasource local storage ignored files
|
| 5 |
+
/../../../../../:\PG5\projects\EAMM-main\.idea/dataSources/
|
| 6 |
+
/dataSources.local.xml
|
| 7 |
+
# Editor-based HTTP Client requests
|
| 8 |
+
/httpRequests/
|
FONT/.idea/EAMM-main.iml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<module type="PYTHON_MODULE" version="4">
|
| 3 |
+
<component name="NewModuleRootManager">
|
| 4 |
+
<content url="file://$MODULE_DIR$" />
|
| 5 |
+
<orderEntry type="inheritedJdk" />
|
| 6 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
| 7 |
+
</component>
|
| 8 |
+
<component name="PyDocumentationSettings">
|
| 9 |
+
<option name="format" value="GOOGLE" />
|
| 10 |
+
<option name="myDocStringFormat" value="Google" />
|
| 11 |
+
</component>
|
| 12 |
+
<component name="TestRunnerService">
|
| 13 |
+
<option name="PROJECT_TEST_RUNNER" value="pytest" />
|
| 14 |
+
</component>
|
| 15 |
+
</module>
|
FONT/.idea/deployment.xml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="PublishConfigData" autoUpload="Always" serverName="10.26.128.77" remoteFilesAllowedToDisappearOnAutoupload="false">
|
| 4 |
+
<serverData>
|
| 5 |
+
<paths name="10.26.128.77">
|
| 6 |
+
<serverdata>
|
| 7 |
+
<mappings>
|
| 8 |
+
<mapping deploy="/data/liujin/EAMM-main" local="$PROJECT_DIR$" web="/" />
|
| 9 |
+
</mappings>
|
| 10 |
+
</serverdata>
|
| 11 |
+
</paths>
|
| 12 |
+
</serverData>
|
| 13 |
+
<option name="myAutoUpload" value="ALWAYS" />
|
| 14 |
+
</component>
|
| 15 |
+
</project>
|
FONT/.idea/inspectionProfiles/Project_Default.xml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<component name="InspectionProjectProfileManager">
|
| 2 |
+
<profile version="1.0">
|
| 3 |
+
<option name="myName" value="Project Default" />
|
| 4 |
+
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
| 5 |
+
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
| 6 |
+
<option name="ignoredPackages">
|
| 7 |
+
<value>
|
| 8 |
+
<list size="7">
|
| 9 |
+
<item index="0" class="java.lang.String" itemvalue="tensorboard" />
|
| 10 |
+
<item index="1" class="java.lang.String" itemvalue="opencv-python" />
|
| 11 |
+
<item index="2" class="java.lang.String" itemvalue="librosa" />
|
| 12 |
+
<item index="3" class="java.lang.String" itemvalue="torch" />
|
| 13 |
+
<item index="4" class="java.lang.String" itemvalue="dill" />
|
| 14 |
+
<item index="5" class="java.lang.String" itemvalue="torchvision" />
|
| 15 |
+
<item index="6" class="java.lang.String" itemvalue="dominate" />
|
| 16 |
+
</list>
|
| 17 |
+
</value>
|
| 18 |
+
</option>
|
| 19 |
+
</inspection_tool>
|
| 20 |
+
<inspection_tool class="TsLint" enabled="true" level="WARNING" enabled_by_default="true" />
|
| 21 |
+
</profile>
|
| 22 |
+
</component>
|
FONT/.idea/inspectionProfiles/profiles_settings.xml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<component name="InspectionProjectProfileManager">
|
| 2 |
+
<settings>
|
| 3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
| 4 |
+
<version value="1.0" />
|
| 5 |
+
</settings>
|
| 6 |
+
</component>
|
FONT/.idea/misc.xml
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (base)" project-jdk-type="Python SDK" />
|
| 4 |
+
</project>
|
FONT/.idea/modules.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="ProjectModuleManager">
|
| 4 |
+
<modules>
|
| 5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/EAMM-main.iml" filepath="$PROJECT_DIR$/.idea/EAMM-main.iml" />
|
| 6 |
+
</modules>
|
| 7 |
+
</component>
|
| 8 |
+
</project>
|
FONT/.idea/sshConfigs.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<project version="4">
|
| 3 |
+
<component name="SshConfigs">
|
| 4 |
+
<configs>
|
| 5 |
+
<sshConfig authType="PASSWORD" host="10.26.128.77" id="e53dc234-7f07-4f76-84e5-ddf5b28db632" port="22" nameFormat="DESCRIPTIVE" username="user" useOpenSSHConfig="true" />
|
| 6 |
+
</configs>
|
| 7 |
+
</component>
|
| 8 |
+
</project>
|
FONT/3DDFA_V2/demo.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding: utf-8
|
| 2 |
+
|
| 3 |
+
__author__ = 'cleardusk'
|
| 4 |
+
|
| 5 |
+
import sys
|
| 6 |
+
import argparse
|
| 7 |
+
import cv2
|
| 8 |
+
import yaml
|
| 9 |
+
import os
|
| 10 |
+
import time
|
| 11 |
+
from FaceBoxes import FaceBoxes
|
| 12 |
+
from TDDFA import TDDFA
|
| 13 |
+
from utils.render import render
|
| 14 |
+
#from utils.render_ctypes import render # faster
|
| 15 |
+
from utils.depth import depth
|
| 16 |
+
from utils.pncc import pncc
|
| 17 |
+
from utils.uv import uv_tex
|
| 18 |
+
from utils.pose import viz_pose, get_pose
|
| 19 |
+
from utils.serialization import ser_to_ply, ser_to_obj
|
| 20 |
+
from utils.functions import draw_landmarks, get_suffix
|
| 21 |
+
from utils.tddfa_util import str2bool
|
| 22 |
+
import numpy as np
|
| 23 |
+
from tqdm import tqdm
|
| 24 |
+
import copy
|
| 25 |
+
|
| 26 |
+
import concurrent.futures
|
| 27 |
+
from multiprocessing import Pool
|
| 28 |
+
|
| 29 |
+
def main(args,img, save_path, pose_path):
|
| 30 |
+
# begin = time.time()
|
| 31 |
+
cfg = yaml.load(open(args.config), Loader=yaml.SafeLoader)
|
| 32 |
+
|
| 33 |
+
# Init FaceBoxes and TDDFA, recommend using onnx flag
|
| 34 |
+
if args.onnx:
|
| 35 |
+
import os
|
| 36 |
+
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
|
| 37 |
+
os.environ['OMP_NUM_THREADS'] = '4'
|
| 38 |
+
|
| 39 |
+
from FaceBoxes.FaceBoxes_ONNX import FaceBoxes_ONNX
|
| 40 |
+
from TDDFA_ONNX import TDDFA_ONNX
|
| 41 |
+
|
| 42 |
+
face_boxes = FaceBoxes_ONNX()
|
| 43 |
+
tddfa = TDDFA_ONNX(**cfg)
|
| 44 |
+
else:
|
| 45 |
+
gpu_mode = args.mode == 'gpu'
|
| 46 |
+
tddfa = TDDFA(gpu_mode=gpu_mode, **cfg)
|
| 47 |
+
face_boxes = FaceBoxes()
|
| 48 |
+
|
| 49 |
+
# Given a still image path and load to BGR channel
|
| 50 |
+
# img = cv2.imread(img_path) #args.img_fp
|
| 51 |
+
|
| 52 |
+
# Detect faces, get 3DMM params and roi boxes
|
| 53 |
+
boxes = face_boxes(img)
|
| 54 |
+
n = len(boxes)
|
| 55 |
+
if n == 0:
|
| 56 |
+
print(f'No face detected, exit')
|
| 57 |
+
# sys.exit(-1)
|
| 58 |
+
return None
|
| 59 |
+
print(f'Detect {n} faces')
|
| 60 |
+
|
| 61 |
+
param_lst, roi_box_lst = tddfa(img, boxes)
|
| 62 |
+
#detection time
|
| 63 |
+
# detect_time = time.time()-begin
|
| 64 |
+
# print('detection time: '+str(detect_time), file=open('/mnt/lustre/jixinya/Home/3DDFA_V2/pose.txt', 'a'))
|
| 65 |
+
# Visualization and serialization
|
| 66 |
+
dense_flag = args.opt in ('2d_dense', '3d', 'depth', 'pncc', 'uv_tex', 'ply', 'obj')
|
| 67 |
+
# old_suffix = get_suffix(img_path)
|
| 68 |
+
old_suffix = 'png'
|
| 69 |
+
new_suffix = f'.{args.opt}' if args.opt in ('ply', 'obj') else '.jpg'
|
| 70 |
+
|
| 71 |
+
wfp = f'examples/results/{args.img_fp.split("/")[-1].replace(old_suffix, "")}_{args.opt}' + new_suffix
|
| 72 |
+
|
| 73 |
+
ver_lst = tddfa.recon_vers(param_lst, roi_box_lst, dense_flag=dense_flag)
|
| 74 |
+
|
| 75 |
+
if args.opt == '2d_sparse':
|
| 76 |
+
draw_landmarks(img, ver_lst, show_flag=args.show_flag, dense_flag=dense_flag, wfp=wfp)
|
| 77 |
+
elif args.opt == '2d_dense':
|
| 78 |
+
draw_landmarks(img, ver_lst, show_flag=args.show_flag, dense_flag=dense_flag, wfp=wfp)
|
| 79 |
+
elif args.opt == '3d':
|
| 80 |
+
render(img, ver_lst, tddfa.tri, alpha=0.6, show_flag=args.show_flag, wfp=wfp)
|
| 81 |
+
elif args.opt == 'depth':
|
| 82 |
+
|
| 83 |
+
# if `with_bf_flag` is False, the background is black
|
| 84 |
+
depth(img, ver_lst, tddfa.tri, show_flag=args.show_flag, wfp=wfp, with_bg_flag=True)
|
| 85 |
+
elif args.opt == 'pncc':
|
| 86 |
+
pncc(img, ver_lst, tddfa.tri, show_flag=args.show_flag, wfp=wfp, with_bg_flag=True)
|
| 87 |
+
elif args.opt == 'uv_tex':
|
| 88 |
+
uv_tex(img, ver_lst, tddfa.tri, show_flag=args.show_flag, wfp=wfp)
|
| 89 |
+
elif args.opt == 'pose':
|
| 90 |
+
all_pose = get_pose(img, param_lst, ver_lst, show_flag=args.show_flag, wfp=save_path, wnp = pose_path)
|
| 91 |
+
elif args.opt == 'ply':
|
| 92 |
+
ser_to_ply(ver_lst, tddfa.tri, height=img.shape[0], wfp=wfp)
|
| 93 |
+
elif args.opt == 'obj':
|
| 94 |
+
ser_to_obj(img, ver_lst, tddfa.tri, height=img.shape[0], wfp=wfp)
|
| 95 |
+
else:
|
| 96 |
+
raise ValueError(f'Unknown opt {args.opt}')
|
| 97 |
+
|
| 98 |
+
return all_pose
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def process_word(i):
|
| 103 |
+
path = '/media/xinya/Backup Plus/sense_shixi_data/new_crop/MEAD_fomm_video_6/'
|
| 104 |
+
save = '/media/xinya/Backup Plus/sense_shixi_data/new_crop/MEAD_fomm_pose_im/'
|
| 105 |
+
pose = '/media/xinya/Backup Plus/sense_shixi_data/new_crop/MEAD_fomm_pose/'
|
| 106 |
+
start = time.time()
|
| 107 |
+
Dir = os.listdir(path)
|
| 108 |
+
Dir.sort()
|
| 109 |
+
word = Dir[i]
|
| 110 |
+
wpath = os.path.join(path, word)
|
| 111 |
+
print(wpath)
|
| 112 |
+
pathDir = os.listdir(wpath)
|
| 113 |
+
pose_file = os.path.join(pose,word)
|
| 114 |
+
if not os.path.exists(pose_file):
|
| 115 |
+
os.makedirs(pose_file)
|
| 116 |
+
|
| 117 |
+
for j in range(len(pathDir)):
|
| 118 |
+
name = pathDir[j]
|
| 119 |
+
# save_file = os.path.join(save,word,name)
|
| 120 |
+
# if not os.path.exists(save_file):
|
| 121 |
+
# os.makedirs(save_file)
|
| 122 |
+
fpath = os.path.join(wpath,name)
|
| 123 |
+
image_all = []
|
| 124 |
+
videoCapture = cv2.VideoCapture(fpath)
|
| 125 |
+
|
| 126 |
+
success, frame = videoCapture.read()
|
| 127 |
+
|
| 128 |
+
n = 0
|
| 129 |
+
while success :
|
| 130 |
+
image_all.append(frame)
|
| 131 |
+
n = n + 1
|
| 132 |
+
success, frame = videoCapture.read()
|
| 133 |
+
|
| 134 |
+
# fDir = os.listdir(fpath)
|
| 135 |
+
pose_all = np.zeros((len(image_all),7))
|
| 136 |
+
for k in range(len(image_all)):
|
| 137 |
+
# index = fDir[k].split('.')[0]
|
| 138 |
+
# img_path = os.path.join(fpath,str(k)+'.png')
|
| 139 |
+
|
| 140 |
+
# pose_all[k] = main(args,image_all[k], os.path.join(save_file,str(k)+'.jpg'), None)
|
| 141 |
+
pose_all[k] = main(args,image_all[k], None, None)
|
| 142 |
+
np.save(os.path.join(pose,word,name.split('.')[0]+'.npy'),pose_all)
|
| 143 |
+
st = time.time()-start
|
| 144 |
+
print(str(i)+' '+word+' '+str(j)+' '+name+' '+str(k)+'time: '+str(st), file=open('/media/thea/Backup Plus/sense_shixi_data/new_crop/pose_mead6.txt', 'a'))
|
| 145 |
+
print(i,word,j,name,k)
|
| 146 |
+
|
| 147 |
+
if __name__ == '__main__':
|
| 148 |
+
parser = argparse.ArgumentParser(description='The demo of still image of 3DDFA_V2')
|
| 149 |
+
parser.add_argument('-c', '--config', type=str, default='configs/mb1_120x120.yml')
|
| 150 |
+
parser.add_argument('-f', '--img_fp', type=str, default='examples/inputs/0.png')
|
| 151 |
+
parser.add_argument('-m', '--mode', type=str, default='cpu', help='gpu or cpu mode')
|
| 152 |
+
parser.add_argument('-o', '--opt', type=str, default='pose',
|
| 153 |
+
choices=['2d_sparse', '2d_dense', '3d', 'depth', 'pncc', 'uv_tex', 'pose', 'ply', 'obj'])
|
| 154 |
+
parser.add_argument('--show_flag', type=str2bool, default='False', help='whether to show the visualization result')
|
| 155 |
+
parser.add_argument('--onnx', action='store_true', default=False)
|
| 156 |
+
|
| 157 |
+
args = parser.parse_args()
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
# filepath = 'test/image/'
|
| 162 |
+
# pathDir = os.listdir(filepath)
|
| 163 |
+
# for i in range(len(pathDir)):
|
| 164 |
+
# image= cv2.imread(os.path.join(filepath,pathDir[i]))
|
| 165 |
+
# pose = main(args,image, None, None).reshape(1,7)
|
| 166 |
+
#
|
| 167 |
+
# np.save('test/pose/'+pathDir[i].split('.')[0]+'.npy',pose)
|
| 168 |
+
# print(i,pathDir[i])
|
| 169 |
+
|
| 170 |
+
test_image_path = "/data/liujin/dataset/LRW/lipread_frames/ABOUT/train/ABOUT_00001/000000.jpg"
|
| 171 |
+
image = cv2.imread(test_image_path)
|
| 172 |
+
pose = main(args, image, None, None).reshape(1, 7)
|
| 173 |
+
print(pose)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
'''
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def main(args):
|
| 185 |
+
cfg = yaml.load(open(args.config), Loader=yaml.SafeLoader)
|
| 186 |
+
|
| 187 |
+
# Init FaceBoxes and TDDFA, recommend using onnx flag
|
| 188 |
+
if args.onnx:
|
| 189 |
+
import os
|
| 190 |
+
os.environ['KMP_DUPLICATE_LIB_OK'] = 'True'
|
| 191 |
+
os.environ['OMP_NUM_THREADS'] = '4'
|
| 192 |
+
|
| 193 |
+
from FaceBoxes.FaceBoxes_ONNX import FaceBoxes_ONNX
|
| 194 |
+
from TDDFA_ONNX import TDDFA_ONNX
|
| 195 |
+
|
| 196 |
+
face_boxes = FaceBoxes_ONNX()
|
| 197 |
+
tddfa = TDDFA_ONNX(**cfg)
|
| 198 |
+
else:
|
| 199 |
+
gpu_mode = args.mode == 'gpu'
|
| 200 |
+
tddfa = TDDFA(gpu_mode=gpu_mode, **cfg)
|
| 201 |
+
face_boxes = FaceBoxes()
|
| 202 |
+
|
| 203 |
+
# Given a still image path and load to BGR channel
|
| 204 |
+
img = cv2.imread(args.img_fp)
|
| 205 |
+
|
| 206 |
+
# Detect faces, get 3DMM params and roi boxes
|
| 207 |
+
boxes = face_boxes(img)
|
| 208 |
+
n = len(boxes)
|
| 209 |
+
if n == 0:
|
| 210 |
+
print(f'No face detected, exit')
|
| 211 |
+
sys.exit(-1)
|
| 212 |
+
print(f'Detect {n} faces')
|
| 213 |
+
|
| 214 |
+
param_lst, roi_box_lst = tddfa(img, boxes)
|
| 215 |
+
|
| 216 |
+
# Visualization and serialization
|
| 217 |
+
dense_flag = args.opt in ('2d_dense', '3d', 'depth', 'pncc', 'uv_tex', 'ply', 'obj')
|
| 218 |
+
old_suffix = get_suffix(args.img_fp)
|
| 219 |
+
new_suffix = f'.{args.opt}' if args.opt in ('ply', 'obj') else '.jpg'
|
| 220 |
+
|
| 221 |
+
wfp = f'examples/results/{args.img_fp.split("/")[-1].replace(old_suffix, "")}_{args.opt}' + new_suffix
|
| 222 |
+
|
| 223 |
+
ver_lst = tddfa.recon_vers(param_lst, roi_box_lst, dense_flag=dense_flag)
|
| 224 |
+
|
| 225 |
+
if args.opt == '2d_sparse':
|
| 226 |
+
draw_landmarks(img, ver_lst, show_flag=args.show_flag, dense_flag=dense_flag, wfp=wfp)
|
| 227 |
+
elif args.opt == '2d_dense':
|
| 228 |
+
draw_landmarks(img, ver_lst, show_flag=args.show_flag, dense_flag=dense_flag, wfp=wfp)
|
| 229 |
+
elif args.opt == '3d':
|
| 230 |
+
render(img, ver_lst, tddfa.tri, alpha=0.6, show_flag=args.show_flag, wfp=wfp)
|
| 231 |
+
elif args.opt == 'depth':
|
| 232 |
+
# if `with_bf_flag` is False, the background is black
|
| 233 |
+
depth(img, ver_lst, tddfa.tri, show_flag=args.show_flag, wfp=wfp, with_bg_flag=True)
|
| 234 |
+
elif args.opt == 'pncc':
|
| 235 |
+
pncc(img, ver_lst, tddfa.tri, show_flag=args.show_flag, wfp=wfp, with_bg_flag=True)
|
| 236 |
+
elif args.opt == 'uv_tex':
|
| 237 |
+
uv_tex(img, ver_lst, tddfa.tri, show_flag=args.show_flag, wfp=wfp)
|
| 238 |
+
elif args.opt == 'pose':
|
| 239 |
+
viz_pose(img, param_lst, ver_lst, show_flag=args.show_flag, wfp=wfp)
|
| 240 |
+
elif args.opt == 'ply':
|
| 241 |
+
ser_to_ply(ver_lst, tddfa.tri, height=img.shape[0], wfp=wfp)
|
| 242 |
+
elif args.opt == 'obj':
|
| 243 |
+
ser_to_obj(img, ver_lst, tddfa.tri, height=img.shape[0], wfp=wfp)
|
| 244 |
+
else:
|
| 245 |
+
raise ValueError(f'Unknown opt {args.opt}')
|
| 246 |
+
'''
|
FONT/3DDFA_V2/utils/pose.py
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding: utf-8
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
Reference: https://github.com/YadiraF/PRNet/blob/master/utils/estimate_pose.py
|
| 5 |
+
|
| 6 |
+
Calculating pose from the output 3DMM parameters, you can also try to use solvePnP to perform estimation
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
__author__ = 'cleardusk'
|
| 10 |
+
|
| 11 |
+
import cv2
|
| 12 |
+
import numpy as np
|
| 13 |
+
from math import cos, sin, atan2, asin, sqrt
|
| 14 |
+
|
| 15 |
+
from .functions import calc_hypotenuse, plot_image
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def P2sRt(P):
|
| 19 |
+
""" decompositing camera matrix P.
|
| 20 |
+
Args:
|
| 21 |
+
P: (3, 4). Affine Camera Matrix.
|
| 22 |
+
Returns:
|
| 23 |
+
s: scale factor.
|
| 24 |
+
R: (3, 3). rotation matrix.
|
| 25 |
+
t2d: (2,). 2d translation.
|
| 26 |
+
"""
|
| 27 |
+
t3d = P[:, 3]
|
| 28 |
+
R1 = P[0:1, :3]
|
| 29 |
+
R2 = P[1:2, :3]
|
| 30 |
+
s = (np.linalg.norm(R1) + np.linalg.norm(R2)) / 2.0
|
| 31 |
+
r1 = R1 / np.linalg.norm(R1)
|
| 32 |
+
r2 = R2 / np.linalg.norm(R2)
|
| 33 |
+
r3 = np.cross(r1, r2)
|
| 34 |
+
|
| 35 |
+
R = np.concatenate((r1, r2, r3), 0)
|
| 36 |
+
return s, R, t3d
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def matrix2angle(R):
|
| 40 |
+
""" compute three Euler angles from a Rotation Matrix. Ref: http://www.gregslabaugh.net/publications/euler.pdf
|
| 41 |
+
refined by: https://stackoverflow.com/questions/43364900/rotation-matrix-to-euler-angles-with-opencv
|
| 42 |
+
todo: check and debug
|
| 43 |
+
Args:
|
| 44 |
+
R: (3,3). rotation matrix
|
| 45 |
+
Returns:
|
| 46 |
+
x: yaw
|
| 47 |
+
y: pitch
|
| 48 |
+
z: roll
|
| 49 |
+
"""
|
| 50 |
+
if R[2, 0] > 0.998:
|
| 51 |
+
z = 0
|
| 52 |
+
x = np.pi / 2
|
| 53 |
+
y = z + atan2(-R[0, 1], -R[0, 2])
|
| 54 |
+
elif R[2, 0] < -0.998:
|
| 55 |
+
z = 0
|
| 56 |
+
x = -np.pi / 2
|
| 57 |
+
y = -z + atan2(R[0, 1], R[0, 2])
|
| 58 |
+
else:
|
| 59 |
+
x = asin(R[2, 0])
|
| 60 |
+
y = atan2(R[2, 1] / cos(x), R[2, 2] / cos(x))
|
| 61 |
+
z = atan2(R[1, 0] / cos(x), R[0, 0] / cos(x))
|
| 62 |
+
|
| 63 |
+
return x, y, z
|
| 64 |
+
|
| 65 |
+
def angle2matrix(theta):
|
| 66 |
+
""" compute three Euler angles from a Rotation Matrix. Ref: http://www.gregslabaugh.net/publications/euler.pdf
|
| 67 |
+
refined by: https://stackoverflow.com/questions/43364900/rotation-matrix-to-euler-angles-with-opencv
|
| 68 |
+
todo: check and debug
|
| 69 |
+
Args:
|
| 70 |
+
R: (3,3). rotation matrix
|
| 71 |
+
Returns:
|
| 72 |
+
x: yaw
|
| 73 |
+
y: pitch
|
| 74 |
+
z: roll
|
| 75 |
+
"""
|
| 76 |
+
R_x = np.array([[1, 0, 0 ],
|
| 77 |
+
|
| 78 |
+
[0, cos(theta[1]), -sin(theta[1]) ],
|
| 79 |
+
|
| 80 |
+
[0, sin(theta[1]), cos(theta[1]) ]
|
| 81 |
+
|
| 82 |
+
])
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
R_y = np.array([[cos(theta[0]), 0, sin(-theta[0]) ],
|
| 87 |
+
|
| 88 |
+
[0, 1, 0 ],
|
| 89 |
+
|
| 90 |
+
[-sin(-theta[0]), 0, cos(theta[0]) ]
|
| 91 |
+
|
| 92 |
+
])
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
R_z = np.array([[cos(theta[2]), -sin(theta[2]), 0],
|
| 97 |
+
|
| 98 |
+
[sin(theta[2]), cos(theta[2]), 0],
|
| 99 |
+
|
| 100 |
+
[0, 0, 1]
|
| 101 |
+
|
| 102 |
+
])
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
R = np.dot(R_z, np.dot( R_y, R_x ))
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
return R
|
| 111 |
+
|
| 112 |
+
def angle2matrix_3ddfa(angles):
|
| 113 |
+
''' get rotation matrix from three rotation angles(radian). The same as in 3DDFA.
|
| 114 |
+
Args:
|
| 115 |
+
angles: [3,]. x, y, z angles
|
| 116 |
+
x: pitch.
|
| 117 |
+
y: yaw.
|
| 118 |
+
z: roll.
|
| 119 |
+
Returns:
|
| 120 |
+
R: 3x3. rotation matrix.
|
| 121 |
+
'''
|
| 122 |
+
# x, y, z = np.deg2rad(angles[0]), np.deg2rad(angles[1]), np.deg2rad(angles[2])
|
| 123 |
+
x, y, z = angles[1], angles[0], angles[2]
|
| 124 |
+
|
| 125 |
+
# x
|
| 126 |
+
Rx=np.array([[1, 0, 0],
|
| 127 |
+
[0, cos(x), sin(x)],
|
| 128 |
+
[0, -sin(x), cos(x)]])
|
| 129 |
+
# y
|
| 130 |
+
Ry=np.array([[ cos(y), 0, -sin(y)],
|
| 131 |
+
[ 0, 1, 0],
|
| 132 |
+
[sin(y), 0, cos(y)]])
|
| 133 |
+
# z
|
| 134 |
+
Rz=np.array([[cos(z), sin(z), 0],
|
| 135 |
+
[-sin(z), cos(z), 0],
|
| 136 |
+
[ 0, 0, 1]])
|
| 137 |
+
R = Rx.dot(Ry).dot(Rz)
|
| 138 |
+
return R.astype(np.float32)
|
| 139 |
+
|
| 140 |
+
def calc_pose(param):
|
| 141 |
+
P = param[:12].reshape(3, -1) # camera matrix
|
| 142 |
+
s, R, t3d = P2sRt(P)
|
| 143 |
+
P = np.concatenate((R, t3d.reshape(3, -1)), axis=1) # without scale
|
| 144 |
+
pose = matrix2angle(R)
|
| 145 |
+
pose = [p * 180 / np.pi for p in pose]
|
| 146 |
+
|
| 147 |
+
return P, pose
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def build_camera_box(rear_size=90):
|
| 151 |
+
point_3d = []
|
| 152 |
+
rear_depth = 0
|
| 153 |
+
point_3d.append((-rear_size, -rear_size, rear_depth))
|
| 154 |
+
point_3d.append((-rear_size, rear_size, rear_depth))
|
| 155 |
+
point_3d.append((rear_size, rear_size, rear_depth))
|
| 156 |
+
point_3d.append((rear_size, -rear_size, rear_depth))
|
| 157 |
+
point_3d.append((-rear_size, -rear_size, rear_depth))
|
| 158 |
+
|
| 159 |
+
front_size = int(4 / 3 * rear_size)
|
| 160 |
+
front_depth = int(4 / 3 * rear_size)
|
| 161 |
+
point_3d.append((-front_size, -front_size, front_depth))
|
| 162 |
+
point_3d.append((-front_size, front_size, front_depth))
|
| 163 |
+
point_3d.append((front_size, front_size, front_depth))
|
| 164 |
+
point_3d.append((front_size, -front_size, front_depth))
|
| 165 |
+
point_3d.append((-front_size, -front_size, front_depth))
|
| 166 |
+
point_3d = np.array(point_3d, dtype=np.float32).reshape(-1, 3)
|
| 167 |
+
|
| 168 |
+
return point_3d
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def plot_pose_box(img, P, ver, color=(40, 255, 0), line_width=2):
|
| 172 |
+
""" Draw a 3D box as annotation of pose.
|
| 173 |
+
Ref:https://github.com/yinguobing/head-pose-estimation/blob/master/pose_estimator.py
|
| 174 |
+
Args:
|
| 175 |
+
img: the input image
|
| 176 |
+
P: (3, 4). Affine Camera Matrix.
|
| 177 |
+
kpt: (2, 68) or (3, 68)
|
| 178 |
+
"""
|
| 179 |
+
llength = calc_hypotenuse(ver)
|
| 180 |
+
point_3d = build_camera_box(llength)
|
| 181 |
+
# Map to 2d image points
|
| 182 |
+
point_3d_homo = np.hstack((point_3d, np.ones([point_3d.shape[0], 1]))) # n x 4
|
| 183 |
+
point_2d = point_3d_homo.dot(P.T)[:, :2]
|
| 184 |
+
|
| 185 |
+
point_2d[:, 1] = - point_2d[:, 1]
|
| 186 |
+
point_2d[:, :2] = point_2d[:, :2] - np.mean(point_2d[:4, :2], 0) + np.mean(ver[:2, :27], 1)
|
| 187 |
+
point_2d = np.int32(point_2d.reshape(-1, 2))
|
| 188 |
+
|
| 189 |
+
# Draw all the lines
|
| 190 |
+
cv2.polylines(img, [point_2d], True, color, line_width, cv2.LINE_AA)
|
| 191 |
+
cv2.line(img, tuple(point_2d[1]), tuple(
|
| 192 |
+
point_2d[6]), color, line_width, cv2.LINE_AA)
|
| 193 |
+
cv2.line(img, tuple(point_2d[2]), tuple(
|
| 194 |
+
point_2d[7]), color, line_width, cv2.LINE_AA)
|
| 195 |
+
cv2.line(img, tuple(point_2d[3]), tuple(
|
| 196 |
+
point_2d[8]), color, line_width, cv2.LINE_AA)
|
| 197 |
+
|
| 198 |
+
return img
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def viz_pose(img, param_lst, ver_lst, show_flag=False, wfp=None):
|
| 202 |
+
for param, ver in zip(param_lst, ver_lst):
|
| 203 |
+
P, pose = calc_pose(param)
|
| 204 |
+
img = plot_pose_box(img, P, ver)
|
| 205 |
+
# print(P[:, :3])
|
| 206 |
+
print(f'yaw: {pose[0]:.1f}, pitch: {pose[1]:.1f}, roll: {pose[2]:.1f}')
|
| 207 |
+
|
| 208 |
+
if wfp is not None:
|
| 209 |
+
cv2.imwrite(wfp, img)
|
| 210 |
+
print(f'Save visualization result to {wfp}')
|
| 211 |
+
|
| 212 |
+
if show_flag:
|
| 213 |
+
plot_image(img)
|
| 214 |
+
|
| 215 |
+
return img
|
| 216 |
+
|
| 217 |
+
def pose_6(param):
|
| 218 |
+
P = param[:12].reshape(3, -1) # camera matrix
|
| 219 |
+
s, R, t3d = P2sRt(P)
|
| 220 |
+
P = np.concatenate((R, t3d.reshape(3, -1)), axis=1) # without scale
|
| 221 |
+
pose = matrix2angle(R)
|
| 222 |
+
print(t3d)
|
| 223 |
+
R1 = angle2matrix(pose)
|
| 224 |
+
print(R)
|
| 225 |
+
print(R1)
|
| 226 |
+
pose = [p * 180 / np.pi for p in pose]
|
| 227 |
+
|
| 228 |
+
return s, pose, t3d, P
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def smooth_pose(img, param_lst, ver_lst, pose_new, show_flag=False, wfp=None, wnp = None):
|
| 232 |
+
for param, ver in zip(param_lst, ver_lst):
|
| 233 |
+
t3d = np.array([pose_new[4],pose_new[5],pose_new[6]])
|
| 234 |
+
|
| 235 |
+
theta = np.array([pose_new[0],pose_new[1],pose_new[2]])
|
| 236 |
+
theta = [p * np.pi / 180 for p in theta]
|
| 237 |
+
R = angle2matrix(theta)
|
| 238 |
+
P = np.concatenate((R, t3d.reshape(3, -1)), axis=1)
|
| 239 |
+
img = plot_pose_box(img, P, ver)
|
| 240 |
+
# print(P,P.shape,t3d)
|
| 241 |
+
print(P,pose_new)
|
| 242 |
+
print(f'yaw: {theta[0]:.1f}, pitch: {theta[1]:.1f}, roll: {theta[2]:.1f}')
|
| 243 |
+
all_pose = [0]
|
| 244 |
+
all_pose = np.array(all_pose)
|
| 245 |
+
|
| 246 |
+
if wfp is not None:
|
| 247 |
+
cv2.imwrite(wfp, img)
|
| 248 |
+
print(f'Save visualization result to {wfp}')
|
| 249 |
+
|
| 250 |
+
if wnp is not None:
|
| 251 |
+
np.save(wnp, all_pose)
|
| 252 |
+
print(f'Save visualization result to {wfp}')
|
| 253 |
+
|
| 254 |
+
if show_flag:
|
| 255 |
+
plot_image(img)
|
| 256 |
+
|
| 257 |
+
return img
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def get_pose(img, param_lst, ver_lst, show_flag=False, wfp=None, wnp = None):
|
| 264 |
+
for param, ver in zip(param_lst, ver_lst):
|
| 265 |
+
s, pose, t3d, P = pose_6(param)
|
| 266 |
+
img = plot_pose_box(img, P, ver)
|
| 267 |
+
# print(P,P.shape,t3d)
|
| 268 |
+
print(f'yaw: {pose[0]:.1f}, pitch: {pose[1]:.1f}, roll: {pose[2]:.1f}')
|
| 269 |
+
all_pose = [pose[0],pose[1],pose[2],s,t3d[0],t3d[1],t3d[2]]
|
| 270 |
+
all_pose = np.array(all_pose)
|
| 271 |
+
|
| 272 |
+
if wfp is not None:
|
| 273 |
+
cv2.imwrite(wfp, img)
|
| 274 |
+
print(f'Save visualization result to {wfp}')
|
| 275 |
+
|
| 276 |
+
if wnp is not None:
|
| 277 |
+
np.save(wnp, all_pose)
|
| 278 |
+
print(f'Save visualization result to {wfp}')
|
| 279 |
+
|
| 280 |
+
if show_flag:
|
| 281 |
+
plot_image(img)
|
| 282 |
+
|
| 283 |
+
return all_pose
|
| 284 |
+
|
FONT/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2022 jixinya
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
FONT/M003_template.npy
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f7d9af24074def1fa92e8e83c2dc2a926f7d1d40054580e84472711797b3918d
|
| 3 |
+
size 1216
|
FONT/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# EAMM: One-Shot Emotional Talking Face via Audio-Based Emotion-Aware Motion Model [SIGGRAPH 2022 Conference]
|
| 2 |
+
|
| 3 |
+
Xinya Ji, [Hang Zhou](https://hangz-nju-cuhk.github.io/), Kaisiyuan Wang, [Qianyi Wu](https://wuqianyi.top/), [Wayne Wu](http://wywu.github.io/), [Feng Xu](http://xufeng.site/), [Xun Cao](https://cite.nju.edu.cn/People/Faculty/20190621/i5054.html)
|
| 4 |
+
|
| 5 |
+
[[Project]](https://jixinya.github.io/projects/EAMM/) [[Paper]](https://arxiv.org/abs/2205.15278)
|
| 6 |
+
|
| 7 |
+

|
| 8 |
+
|
| 9 |
+
Given a single portrait image, we can synthesize emotional talking faces, where mouth movements match the input audio and facial emotion dynamics follow the emotion source video.
|
| 10 |
+
|
| 11 |
+
## Installation
|
| 12 |
+
|
| 13 |
+
We train and test based on Python3.6 and Pytorch. To install the dependencies run:
|
| 14 |
+
|
| 15 |
+
```
|
| 16 |
+
pip install -r requirements.txt
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
## Testing
|
| 20 |
+
|
| 21 |
+
- Download the pre-trained models and data under the following link: [google-drive](https://drive.google.com/file/d/1IL9LjH3JegyMqJABqMxrX3StAq_v8Gtp/view?usp=sharing) and put the file in corresponding places.
|
| 22 |
+
|
| 23 |
+
- Run the demo:
|
| 24 |
+
|
| 25 |
+
`python demo.py --source_image path/to/image --driving_video path/to/emotion_video --pose_file path/to/pose --in_file path/to/audio --emotion emotion_type`
|
| 26 |
+
|
| 27 |
+
- Prepare testing data:
|
| 28 |
+
|
| 29 |
+
prepare source_image -- crop_image in process_data.py
|
| 30 |
+
|
| 31 |
+
prepare driving_video -- crop_image_tem in process_data.py
|
| 32 |
+
|
| 33 |
+
prepare pose -- detect pose using [3DDFA_V2](https://github.com/cleardusk/3DDFA_V2)
|
| 34 |
+
|
| 35 |
+
## Training
|
| 36 |
+
|
| 37 |
+
- Dataset process: Coming soon.
|
| 38 |
+
|
| 39 |
+
- Step 1 : Train the Audio2Facial-Dynamics Module using LRW dataset
|
| 40 |
+
|
| 41 |
+
`python run.py --config config/train_part1.yaml --mode train_part1 --checkpoint log/124_52000.pth.tar `
|
| 42 |
+
|
| 43 |
+
- Step 2 : Fine-tune the Audio2Facial-Dynamics Module after getting stable results from step1
|
| 44 |
+
|
| 45 |
+
`python run.py --config config/train_part1_fine_tune.yaml --mode train_part1_fine_tune --checkpoint log/124_52000.pth.tar --audio_chechpoint checkpoint/from/step_1`
|
| 46 |
+
|
| 47 |
+
- Setp 3 : Train the Implicit Emotion Displacement Learner
|
| 48 |
+
|
| 49 |
+
`python run.py --config config/train_part2.yaml --mode train_part2 --checkpoint log/124_52000.pth.tar --audio_chechpoint checkpoint/from/step_2`
|
| 50 |
+
|
| 51 |
+
## Citation
|
| 52 |
+
|
| 53 |
+
```
|
| 54 |
+
@inproceedings{10.1145/3528233.3530745,
|
| 55 |
+
author = {Ji, Xinya and Zhou, Hang and Wang, Kaisiyuan and Wu, Qianyi and Wu, Wayne and Xu, Feng and Cao, Xun},
|
| 56 |
+
title = {EAMM: One-Shot Emotional Talking Face via Audio-Based Emotion-Aware Motion Model},
|
| 57 |
+
year = {2022},
|
| 58 |
+
isbn = {9781450393379},
|
| 59 |
+
url = {https://doi.org/10.1145/3528233.3530745},
|
| 60 |
+
doi = {10.1145/3528233.3530745},
|
| 61 |
+
booktitle = {ACM SIGGRAPH 2022 Conference Proceedings},
|
| 62 |
+
series = {SIGGRAPH '22}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
```
|
| 67 |
+
|
FONT/audio.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import librosa
|
| 2 |
+
import librosa.filters
|
| 3 |
+
import numpy as np
|
| 4 |
+
# import tensorflow as tf
|
| 5 |
+
from scipy import signal
|
| 6 |
+
from scipy.io import wavfile
|
| 7 |
+
# from dataset.hparams import hparams as hp
|
| 8 |
+
from hparams import hparams as hp
|
| 9 |
+
import scipy, cv2, os, sys, argparse
|
| 10 |
+
|
| 11 |
+
def load_wav(path, sr):
|
| 12 |
+
return librosa.core.load(path, sr=sr)[0]
|
| 13 |
+
|
| 14 |
+
def save_wav(wav, path, sr):
|
| 15 |
+
wav *= 32767 / max(0.01, np.max(np.abs(wav)))
|
| 16 |
+
#proposed by @dsmiller
|
| 17 |
+
wavfile.write(path, sr, wav.astype(np.int16))
|
| 18 |
+
|
| 19 |
+
def save_wavenet_wav(wav, path, sr):
|
| 20 |
+
librosa.output.write_wav(path, wav, sr=sr)
|
| 21 |
+
|
| 22 |
+
def preemphasis(wav, k, preemphasize=True):
|
| 23 |
+
if preemphasize:
|
| 24 |
+
return signal.lfilter([1, -k], [1], wav)
|
| 25 |
+
return wav
|
| 26 |
+
|
| 27 |
+
def inv_preemphasis(wav, k, inv_preemphasize=True):
|
| 28 |
+
if inv_preemphasize:
|
| 29 |
+
return signal.lfilter([1], [1, -k], wav)
|
| 30 |
+
return wav
|
| 31 |
+
|
| 32 |
+
def get_hop_size():
|
| 33 |
+
hop_size = hp.hop_size
|
| 34 |
+
if hop_size is None:
|
| 35 |
+
assert hp.frame_shift_ms is not None
|
| 36 |
+
hop_size = int(hp.frame_shift_ms / 1000 * hp.sample_rate)
|
| 37 |
+
return hop_size
|
| 38 |
+
|
| 39 |
+
def linearspectrogram(wav):
|
| 40 |
+
D = _stft(preemphasis(wav, hp.preemphasis, hp.preemphasize))
|
| 41 |
+
S = _amp_to_db(np.abs(D)) - hp.ref_level_db
|
| 42 |
+
|
| 43 |
+
if hp.signal_normalization:
|
| 44 |
+
return _normalize(S)
|
| 45 |
+
return S
|
| 46 |
+
|
| 47 |
+
def melspectrogram(wav):
|
| 48 |
+
D = _stft(preemphasis(wav, hp.preemphasis, hp.preemphasize))
|
| 49 |
+
S = _amp_to_db(_linear_to_mel(np.abs(D))) - hp.ref_level_db
|
| 50 |
+
|
| 51 |
+
if hp.signal_normalization:
|
| 52 |
+
return _normalize(S)
|
| 53 |
+
return S
|
| 54 |
+
|
| 55 |
+
def _lws_processor():
|
| 56 |
+
import lws
|
| 57 |
+
return lws.lws(hp.n_fft, get_hop_size(), fftsize=hp.win_size, mode="speech")
|
| 58 |
+
|
| 59 |
+
def _stft(y):
|
| 60 |
+
if hp.use_lws:
|
| 61 |
+
return _lws_processor(hp).stft(y).T
|
| 62 |
+
else:
|
| 63 |
+
return librosa.stft(y=y, n_fft=hp.n_fft, hop_length=get_hop_size(), win_length=hp.win_size)
|
| 64 |
+
|
| 65 |
+
##########################################################
|
| 66 |
+
#Those are only correct when using lws!!! (This was messing with Wavenet quality for a long time!)
|
| 67 |
+
def num_frames(length, fsize, fshift):
|
| 68 |
+
"""Compute number of time frames of spectrogram
|
| 69 |
+
"""
|
| 70 |
+
pad = (fsize - fshift)
|
| 71 |
+
if length % fshift == 0:
|
| 72 |
+
M = (length + pad * 2 - fsize) // fshift + 1
|
| 73 |
+
else:
|
| 74 |
+
M = (length + pad * 2 - fsize) // fshift + 2
|
| 75 |
+
return M
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def pad_lr(x, fsize, fshift):
|
| 79 |
+
"""Compute left and right padding
|
| 80 |
+
"""
|
| 81 |
+
M = num_frames(len(x), fsize, fshift)
|
| 82 |
+
pad = (fsize - fshift)
|
| 83 |
+
T = len(x) + 2 * pad
|
| 84 |
+
r = (M - 1) * fshift + fsize - T
|
| 85 |
+
return pad, pad + r
|
| 86 |
+
##########################################################
|
| 87 |
+
#Librosa correct padding
|
| 88 |
+
def librosa_pad_lr(x, fsize, fshift):
|
| 89 |
+
return 0, (x.shape[0] // fshift + 1) * fshift - x.shape[0]
|
| 90 |
+
|
| 91 |
+
# Conversions
|
| 92 |
+
_mel_basis = None
|
| 93 |
+
|
| 94 |
+
def _linear_to_mel(spectogram):
|
| 95 |
+
global _mel_basis
|
| 96 |
+
if _mel_basis is None:
|
| 97 |
+
_mel_basis = _build_mel_basis()
|
| 98 |
+
return np.dot(_mel_basis, spectogram)
|
| 99 |
+
|
| 100 |
+
def _build_mel_basis():
|
| 101 |
+
assert hp.fmax <= hp.sample_rate // 2
|
| 102 |
+
return librosa.filters.mel(hp.sample_rate, hp.n_fft, n_mels=hp.num_mels,
|
| 103 |
+
fmin=hp.fmin, fmax=hp.fmax)
|
| 104 |
+
|
| 105 |
+
def _amp_to_db(x):
|
| 106 |
+
min_level = np.exp(hp.min_level_db / 20 * np.log(10))
|
| 107 |
+
return 20 * np.log10(np.maximum(min_level, x))
|
| 108 |
+
|
| 109 |
+
def _db_to_amp(x):
|
| 110 |
+
return np.power(10.0, (x) * 0.05)
|
| 111 |
+
|
| 112 |
+
def _normalize(S):
|
| 113 |
+
if hp.allow_clipping_in_normalization:
|
| 114 |
+
if hp.symmetric_mels:
|
| 115 |
+
return np.clip((2 * hp.max_abs_value) * ((S - hp.min_level_db) / (-hp.min_level_db)) - hp.max_abs_value,
|
| 116 |
+
-hp.max_abs_value, hp.max_abs_value)
|
| 117 |
+
else:
|
| 118 |
+
return np.clip(hp.max_abs_value * ((S - hp.min_level_db) / (-hp.min_level_db)), 0, hp.max_abs_value)
|
| 119 |
+
|
| 120 |
+
assert S.max() <= 0 and S.min() - hp.min_level_db >= 0
|
| 121 |
+
if hp.symmetric_mels:
|
| 122 |
+
return (2 * hp.max_abs_value) * ((S - hp.min_level_db) / (-hp.min_level_db)) - hp.max_abs_value
|
| 123 |
+
else:
|
| 124 |
+
return hp.max_abs_value * ((S - hp.min_level_db) / (-hp.min_level_db))
|
| 125 |
+
|
| 126 |
+
def _denormalize(D):
|
| 127 |
+
if hp.allow_clipping_in_normalization:
|
| 128 |
+
if hp.symmetric_mels:
|
| 129 |
+
return (((np.clip(D, -hp.max_abs_value,
|
| 130 |
+
hp.max_abs_value) + hp.max_abs_value) * -hp.min_level_db / (2 * hp.max_abs_value))
|
| 131 |
+
+ hp.min_level_db)
|
| 132 |
+
else:
|
| 133 |
+
return ((np.clip(D, 0, hp.max_abs_value) * -hp.min_level_db / hp.max_abs_value) + hp.min_level_db)
|
| 134 |
+
|
| 135 |
+
if hp.symmetric_mels:
|
| 136 |
+
return (((D + hp.max_abs_value) * -hp.min_level_db / (2 * hp.max_abs_value)) + hp.min_level_db)
|
| 137 |
+
else:
|
| 138 |
+
return ((D * -hp.min_level_db / hp.max_abs_value) + hp.min_level_db)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
if __name__ == "__main__":
|
| 142 |
+
audio_path = "/data/liujin/dataset/LRW/lipread_wav/ABOUT/train/ABOUT_00001.wav"
|
| 143 |
+
wav = audio.load_wav(audio_path, 16000)
|
| 144 |
+
mel = audio.melspectrogram(wav)
|
| 145 |
+
print(mel.shape)
|
FONT/augmentation.py
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Code from https://github.com/hassony2/torch_videovision
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import numbers
|
| 6 |
+
import math
|
| 7 |
+
import random
|
| 8 |
+
import numpy as np
|
| 9 |
+
import PIL
|
| 10 |
+
import cv2
|
| 11 |
+
from skimage.transform import resize, rotate, AffineTransform, warp
|
| 12 |
+
from skimage.util import pad
|
| 13 |
+
import torchvision
|
| 14 |
+
|
| 15 |
+
import warnings
|
| 16 |
+
|
| 17 |
+
from skimage import img_as_ubyte, img_as_float
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def crop_clip(clip, min_h, min_w, h, w):
|
| 21 |
+
if isinstance(clip[0], np.ndarray):
|
| 22 |
+
cropped = [img[min_h:min_h + h, min_w:min_w + w, :] for img in clip]
|
| 23 |
+
|
| 24 |
+
elif isinstance(clip[0], PIL.Image.Image):
|
| 25 |
+
cropped = [
|
| 26 |
+
img.crop((min_w, min_h, min_w + w, min_h + h)) for img in clip
|
| 27 |
+
]
|
| 28 |
+
else:
|
| 29 |
+
raise TypeError('Expected numpy.ndarray or PIL.Image' +
|
| 30 |
+
'but got list of {0}'.format(type(clip[0])))
|
| 31 |
+
return cropped
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def pad_clip(clip, h, w):
|
| 35 |
+
im_h, im_w = clip[0].shape[:2]
|
| 36 |
+
pad_h = (0, 0) if h < im_h else ((h - im_h) // 2, (h - im_h + 1) // 2)
|
| 37 |
+
pad_w = (0, 0) if w < im_w else ((w - im_w) // 2, (w - im_w + 1) // 2)
|
| 38 |
+
|
| 39 |
+
return pad(clip, ((0, 0), pad_h, pad_w, (0, 0)), mode='edge')
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def resize_clip(clip, size, interpolation='bilinear'):
|
| 43 |
+
if isinstance(clip[0], np.ndarray):
|
| 44 |
+
if isinstance(size, numbers.Number):
|
| 45 |
+
im_h, im_w, im_c = clip[0].shape
|
| 46 |
+
# Min spatial dim already matches minimal size
|
| 47 |
+
if (im_w <= im_h and im_w == size) or (im_h <= im_w
|
| 48 |
+
and im_h == size):
|
| 49 |
+
return clip
|
| 50 |
+
new_h, new_w = get_resize_sizes(im_h, im_w, size)
|
| 51 |
+
size = (new_w, new_h)
|
| 52 |
+
else:
|
| 53 |
+
size = size[1], size[0]
|
| 54 |
+
|
| 55 |
+
scaled = [
|
| 56 |
+
resize(img, size, order=1 if interpolation == 'bilinear' else 0, preserve_range=True,
|
| 57 |
+
mode='constant', anti_aliasing=True) for img in clip
|
| 58 |
+
]
|
| 59 |
+
elif isinstance(clip[0], PIL.Image.Image):
|
| 60 |
+
if isinstance(size, numbers.Number):
|
| 61 |
+
im_w, im_h = clip[0].size
|
| 62 |
+
# Min spatial dim already matches minimal size
|
| 63 |
+
if (im_w <= im_h and im_w == size) or (im_h <= im_w
|
| 64 |
+
and im_h == size):
|
| 65 |
+
return clip
|
| 66 |
+
new_h, new_w = get_resize_sizes(im_h, im_w, size)
|
| 67 |
+
size = (new_w, new_h)
|
| 68 |
+
else:
|
| 69 |
+
size = size[1], size[0]
|
| 70 |
+
if interpolation == 'bilinear':
|
| 71 |
+
pil_inter = PIL.Image.NEAREST
|
| 72 |
+
else:
|
| 73 |
+
pil_inter = PIL.Image.BILINEAR
|
| 74 |
+
scaled = [img.resize(size, pil_inter) for img in clip]
|
| 75 |
+
else:
|
| 76 |
+
raise TypeError('Expected numpy.ndarray or PIL.Image' +
|
| 77 |
+
'but got list of {0}'.format(type(clip[0])))
|
| 78 |
+
return scaled
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def get_resize_sizes(im_h, im_w, size):
|
| 82 |
+
if im_w < im_h:
|
| 83 |
+
ow = size
|
| 84 |
+
oh = int(size * im_h / im_w)
|
| 85 |
+
else:
|
| 86 |
+
oh = size
|
| 87 |
+
ow = int(size * im_w / im_h)
|
| 88 |
+
return oh, ow
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class RandomFlip(object):
|
| 92 |
+
def __init__(self, time_flip=False, horizontal_flip=False):
|
| 93 |
+
self.time_flip = time_flip
|
| 94 |
+
self.horizontal_flip = horizontal_flip
|
| 95 |
+
|
| 96 |
+
def __call__(self, clip):
|
| 97 |
+
if random.random() < 0.5 and self.time_flip:
|
| 98 |
+
return clip[::-1]
|
| 99 |
+
if random.random() < 0.5 and self.horizontal_flip:
|
| 100 |
+
return [np.fliplr(img) for img in clip]
|
| 101 |
+
|
| 102 |
+
return clip
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class RandomResize(object):
|
| 106 |
+
"""Resizes a list of (H x W x C) numpy.ndarray to the final size
|
| 107 |
+
The larger the original image is, the more times it takes to
|
| 108 |
+
interpolate
|
| 109 |
+
Args:
|
| 110 |
+
interpolation (str): Can be one of 'nearest', 'bilinear'
|
| 111 |
+
defaults to nearest
|
| 112 |
+
size (tuple): (widht, height)
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
def __init__(self, ratio=(3. / 4., 4. / 3.), interpolation='nearest'):
|
| 116 |
+
self.ratio = ratio
|
| 117 |
+
self.interpolation = interpolation
|
| 118 |
+
|
| 119 |
+
def __call__(self, clip):
|
| 120 |
+
scaling_factor = random.uniform(self.ratio[0], self.ratio[1])
|
| 121 |
+
|
| 122 |
+
if isinstance(clip[0], np.ndarray):
|
| 123 |
+
im_h, im_w, im_c = clip[0].shape
|
| 124 |
+
elif isinstance(clip[0], PIL.Image.Image):
|
| 125 |
+
im_w, im_h = clip[0].size
|
| 126 |
+
|
| 127 |
+
new_w = int(im_w * scaling_factor)
|
| 128 |
+
new_h = int(im_h * scaling_factor)
|
| 129 |
+
new_size = (new_w, new_h)
|
| 130 |
+
resized = resize_clip(
|
| 131 |
+
clip, new_size, interpolation=self.interpolation)
|
| 132 |
+
|
| 133 |
+
return resized
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
class RandomCrop(object):
|
| 137 |
+
"""Extract random crop at the same location for a list of videos
|
| 138 |
+
Args:
|
| 139 |
+
size (sequence or int): Desired output size for the
|
| 140 |
+
crop in format (h, w)
|
| 141 |
+
"""
|
| 142 |
+
|
| 143 |
+
def __init__(self, size):
|
| 144 |
+
if isinstance(size, numbers.Number):
|
| 145 |
+
size = (size, size)
|
| 146 |
+
|
| 147 |
+
self.size = size
|
| 148 |
+
|
| 149 |
+
def __call__(self, clip):
|
| 150 |
+
"""
|
| 151 |
+
Args:
|
| 152 |
+
img (PIL.Image or numpy.ndarray): List of videos to be cropped
|
| 153 |
+
in format (h, w, c) in numpy.ndarray
|
| 154 |
+
Returns:
|
| 155 |
+
PIL.Image or numpy.ndarray: Cropped list of videos
|
| 156 |
+
"""
|
| 157 |
+
h, w = self.size
|
| 158 |
+
if isinstance(clip[0], np.ndarray):
|
| 159 |
+
im_h, im_w, im_c = clip[0].shape
|
| 160 |
+
elif isinstance(clip[0], PIL.Image.Image):
|
| 161 |
+
im_w, im_h = clip[0].size
|
| 162 |
+
else:
|
| 163 |
+
raise TypeError('Expected numpy.ndarray or PIL.Image' +
|
| 164 |
+
'but got list of {0}'.format(type(clip[0])))
|
| 165 |
+
|
| 166 |
+
clip = pad_clip(clip, h, w)
|
| 167 |
+
im_h, im_w = clip.shape[1:3]
|
| 168 |
+
x1 = 0 if h == im_h else random.randint(0, im_w - w)
|
| 169 |
+
y1 = 0 if w == im_w else random.randint(0, im_h - h)
|
| 170 |
+
cropped = crop_clip(clip, y1, x1, h, w)
|
| 171 |
+
|
| 172 |
+
return cropped
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class MouthCrop(object):
|
| 176 |
+
"""Extract random crop at the same location for a list of videos
|
| 177 |
+
Args:
|
| 178 |
+
size (sequence or int): Desired output size for the
|
| 179 |
+
crop in format (h, w)
|
| 180 |
+
"""
|
| 181 |
+
|
| 182 |
+
def __init__(self, center_x, center_y, mask_width, mask_height):
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
self.center_x = center_x
|
| 186 |
+
self.center_y = center_y
|
| 187 |
+
self.mask_width = mask_width
|
| 188 |
+
self.mask_height = mask_height
|
| 189 |
+
|
| 190 |
+
def __call__(self, clip):
|
| 191 |
+
"""
|
| 192 |
+
Args:
|
| 193 |
+
img (PIL.Image or numpy.ndarray): List of videos to be cropped
|
| 194 |
+
in format (h, w, c) in numpy.ndarray
|
| 195 |
+
Returns:
|
| 196 |
+
PIL.Image or numpy.ndarray: Cropped list of videos
|
| 197 |
+
"""
|
| 198 |
+
start_x = self.center_x - int(self.mask_width/2)
|
| 199 |
+
start_y = self.center_y - int(self.mask_height/2)
|
| 200 |
+
end_x = start_x + self.mask_width
|
| 201 |
+
end_y = start_y + self.mask_height
|
| 202 |
+
# mask is all white
|
| 203 |
+
# mask = 255*np.ones((mask_height, mask_width, 3), dtype=np.uint8)
|
| 204 |
+
# mask is uniform noise
|
| 205 |
+
cropped = []
|
| 206 |
+
for i in range(len(clip)):
|
| 207 |
+
mask = np.random.rand(self.mask_height, self.mask_width, 3)
|
| 208 |
+
img = clip[i].copy()
|
| 209 |
+
img[start_y:end_y, start_x:end_x, :] = mask
|
| 210 |
+
|
| 211 |
+
cropped.append(img)
|
| 212 |
+
cropped = np.array(cropped)
|
| 213 |
+
return cropped
|
| 214 |
+
|
| 215 |
+
class RandomRotation(object):
|
| 216 |
+
"""Rotate entire clip randomly by a random angle within
|
| 217 |
+
given bounds
|
| 218 |
+
Args:
|
| 219 |
+
degrees (sequence or int): Range of degrees to select from
|
| 220 |
+
If degrees is a number instead of sequence like (min, max),
|
| 221 |
+
the range of degrees, will be (-degrees, +degrees).
|
| 222 |
+
"""
|
| 223 |
+
|
| 224 |
+
def __init__(self, degrees):
|
| 225 |
+
if isinstance(degrees, numbers.Number):
|
| 226 |
+
if degrees < 0:
|
| 227 |
+
raise ValueError('If degrees is a single number,'
|
| 228 |
+
'must be positive')
|
| 229 |
+
degrees = (-degrees, degrees)
|
| 230 |
+
else:
|
| 231 |
+
if len(degrees) != 2:
|
| 232 |
+
raise ValueError('If degrees is a sequence,'
|
| 233 |
+
'it must be of len 2.')
|
| 234 |
+
|
| 235 |
+
self.degrees = degrees
|
| 236 |
+
|
| 237 |
+
def __call__(self, clip):
|
| 238 |
+
"""
|
| 239 |
+
Args:
|
| 240 |
+
img (PIL.Image or numpy.ndarray): List of videos to be cropped
|
| 241 |
+
in format (h, w, c) in numpy.ndarray
|
| 242 |
+
Returns:
|
| 243 |
+
PIL.Image or numpy.ndarray: Cropped list of videos
|
| 244 |
+
"""
|
| 245 |
+
angle = random.uniform(self.degrees[0], self.degrees[1])
|
| 246 |
+
if isinstance(clip[0], np.ndarray):
|
| 247 |
+
rotated = [rotate(image=img, angle=angle, preserve_range=True) for img in clip]
|
| 248 |
+
elif isinstance(clip[0], PIL.Image.Image):
|
| 249 |
+
rotated = [img.rotate(angle) for img in clip]
|
| 250 |
+
else:
|
| 251 |
+
raise TypeError('Expected numpy.ndarray or PIL.Image' +
|
| 252 |
+
'but got list of {0}'.format(type(clip[0])))
|
| 253 |
+
|
| 254 |
+
return rotated
|
| 255 |
+
|
| 256 |
+
class RandomPerspective(object):
|
| 257 |
+
"""Rotate entire clip randomly by a random angle within
|
| 258 |
+
given bounds
|
| 259 |
+
Args:
|
| 260 |
+
degrees (sequence or int): Range of degrees to select from
|
| 261 |
+
If degrees is a number instead of sequence like (min, max),
|
| 262 |
+
the range of degrees, will be (-degrees, +degrees).
|
| 263 |
+
"""
|
| 264 |
+
|
| 265 |
+
def __init__(self, pers_num, enlarge_num):
|
| 266 |
+
self.pers_num = pers_num
|
| 267 |
+
self.enlarge_num = enlarge_num
|
| 268 |
+
|
| 269 |
+
def __call__(self, clip):
|
| 270 |
+
"""
|
| 271 |
+
Args:
|
| 272 |
+
img (PIL.Image or numpy.ndarray): List of videos to be cropped
|
| 273 |
+
in format (h, w, c) in numpy.ndarray
|
| 274 |
+
Returns:
|
| 275 |
+
PIL.Image or numpy.ndarray: Cropped list of videos
|
| 276 |
+
"""
|
| 277 |
+
out = clip
|
| 278 |
+
for i in range(len(clip)):
|
| 279 |
+
self.pers_size = np.random.randint(20, self.pers_num) * pow(-1, np.random.randint(2))
|
| 280 |
+
self.enlarge_size = np.random.randint(20, self.enlarge_num) * pow(-1, np.random.randint(2))
|
| 281 |
+
h, w, c = clip[i].shape
|
| 282 |
+
crop_size=256
|
| 283 |
+
dst = np.array([
|
| 284 |
+
[-self.enlarge_size, -self.enlarge_size],
|
| 285 |
+
[-self.enlarge_size + self.pers_size, w + self.enlarge_size],
|
| 286 |
+
[h + self.enlarge_size, -self.enlarge_size],
|
| 287 |
+
[h + self.enlarge_size - self.pers_size, w + self.enlarge_size],], dtype=np.float32)
|
| 288 |
+
src = np.array([[-self.enlarge_size, -self.enlarge_size], [-self.enlarge_size, w + self.enlarge_size],
|
| 289 |
+
[h + self.enlarge_size, -self.enlarge_size], [h + self.enlarge_size, w + self.enlarge_size]]).astype(np.float32())
|
| 290 |
+
M = cv2.getPerspectiveTransform(src, dst)
|
| 291 |
+
warped = cv2.warpPerspective(clip[i], M, (crop_size, crop_size), borderMode=cv2.BORDER_REPLICATE)
|
| 292 |
+
out[i] = warped
|
| 293 |
+
|
| 294 |
+
return out
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
class ColorJitter(object):
|
| 298 |
+
"""Randomly change the brightness, contrast and saturation and hue of the clip
|
| 299 |
+
Args:
|
| 300 |
+
brightness (float): How much to jitter brightness. brightness_factor
|
| 301 |
+
is chosen uniformly from [max(0, 1 - brightness), 1 + brightness].
|
| 302 |
+
contrast (float): How much to jitter contrast. contrast_factor
|
| 303 |
+
is chosen uniformly from [max(0, 1 - contrast), 1 + contrast].
|
| 304 |
+
saturation (float): How much to jitter saturation. saturation_factor
|
| 305 |
+
is chosen uniformly from [max(0, 1 - saturation), 1 + saturation].
|
| 306 |
+
hue(float): How much to jitter hue. hue_factor is chosen uniformly from
|
| 307 |
+
[-hue, hue]. Should be >=0 and <= 0.5.
|
| 308 |
+
"""
|
| 309 |
+
|
| 310 |
+
def __init__(self, brightness=0, contrast=0, saturation=0, hue=0):
|
| 311 |
+
self.brightness = brightness
|
| 312 |
+
self.contrast = contrast
|
| 313 |
+
self.saturation = saturation
|
| 314 |
+
self.hue = hue
|
| 315 |
+
|
| 316 |
+
def get_params(self, brightness, contrast, saturation, hue):
|
| 317 |
+
if brightness > 0:
|
| 318 |
+
brightness_factor = random.uniform(
|
| 319 |
+
max(0, 1 - brightness), 1 + brightness)
|
| 320 |
+
else:
|
| 321 |
+
brightness_factor = None
|
| 322 |
+
|
| 323 |
+
if contrast > 0:
|
| 324 |
+
contrast_factor = random.uniform(
|
| 325 |
+
max(0, 1 - contrast), 1 + contrast)
|
| 326 |
+
else:
|
| 327 |
+
contrast_factor = None
|
| 328 |
+
|
| 329 |
+
if saturation > 0:
|
| 330 |
+
saturation_factor = random.uniform(
|
| 331 |
+
max(0, 1 - saturation), 1 + saturation)
|
| 332 |
+
else:
|
| 333 |
+
saturation_factor = None
|
| 334 |
+
|
| 335 |
+
if hue > 0:
|
| 336 |
+
hue_factor = random.uniform(-hue, hue)
|
| 337 |
+
else:
|
| 338 |
+
hue_factor = None
|
| 339 |
+
return brightness_factor, contrast_factor, saturation_factor, hue_factor
|
| 340 |
+
|
| 341 |
+
def __call__(self, clip):
|
| 342 |
+
"""
|
| 343 |
+
Args:
|
| 344 |
+
clip (list): list of PIL.Image
|
| 345 |
+
Returns:
|
| 346 |
+
list PIL.Image : list of transformed PIL.Image
|
| 347 |
+
"""
|
| 348 |
+
if isinstance(clip[0], np.ndarray):
|
| 349 |
+
brightness, contrast, saturation, hue = self.get_params(
|
| 350 |
+
self.brightness, self.contrast, self.saturation, self.hue)
|
| 351 |
+
|
| 352 |
+
# Create img transform function sequence
|
| 353 |
+
img_transforms = []
|
| 354 |
+
if brightness is not None:
|
| 355 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_brightness(img, brightness))
|
| 356 |
+
if saturation is not None:
|
| 357 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_saturation(img, saturation))
|
| 358 |
+
if hue is not None:
|
| 359 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_hue(img, hue))
|
| 360 |
+
if contrast is not None:
|
| 361 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_contrast(img, contrast))
|
| 362 |
+
random.shuffle(img_transforms)
|
| 363 |
+
img_transforms = [img_as_ubyte, torchvision.transforms.ToPILImage()] + img_transforms + [np.array,
|
| 364 |
+
img_as_float]
|
| 365 |
+
|
| 366 |
+
with warnings.catch_warnings():
|
| 367 |
+
warnings.simplefilter("ignore")
|
| 368 |
+
jittered_clip = []
|
| 369 |
+
for img in clip:
|
| 370 |
+
jittered_img = img
|
| 371 |
+
for func in img_transforms:
|
| 372 |
+
jittered_img = func(jittered_img)
|
| 373 |
+
jittered_clip.append(jittered_img.astype('float32'))
|
| 374 |
+
elif isinstance(clip[0], PIL.Image.Image):
|
| 375 |
+
brightness, contrast, saturation, hue = self.get_params(
|
| 376 |
+
self.brightness, self.contrast, self.saturation, self.hue)
|
| 377 |
+
|
| 378 |
+
# Create img transform function sequence
|
| 379 |
+
img_transforms = []
|
| 380 |
+
if brightness is not None:
|
| 381 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_brightness(img, brightness))
|
| 382 |
+
if saturation is not None:
|
| 383 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_saturation(img, saturation))
|
| 384 |
+
if hue is not None:
|
| 385 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_hue(img, hue))
|
| 386 |
+
if contrast is not None:
|
| 387 |
+
img_transforms.append(lambda img: torchvision.transforms.functional.adjust_contrast(img, contrast))
|
| 388 |
+
random.shuffle(img_transforms)
|
| 389 |
+
|
| 390 |
+
# Apply to all videos
|
| 391 |
+
jittered_clip = []
|
| 392 |
+
for img in clip:
|
| 393 |
+
for func in img_transforms:
|
| 394 |
+
jittered_img = func(img)
|
| 395 |
+
jittered_clip.append(jittered_img)
|
| 396 |
+
|
| 397 |
+
else:
|
| 398 |
+
raise TypeError('Expected numpy.ndarray or PIL.Image' +
|
| 399 |
+
'but got list of {0}'.format(type(clip[0])))
|
| 400 |
+
return jittered_clip
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
class AllAugmentationTransform:
|
| 404 |
+
def __init__(self, crop_mouth_param = None, resize_param=None, rotation_param=None, perspective_param=None, flip_param=None, crop_param=None, jitter_param=None):
|
| 405 |
+
self.transforms = []
|
| 406 |
+
if crop_mouth_param is not None:
|
| 407 |
+
self.transforms.append(MouthCrop(**crop_mouth_param))
|
| 408 |
+
|
| 409 |
+
if flip_param is not None:
|
| 410 |
+
self.transforms.append(RandomFlip(**flip_param))
|
| 411 |
+
|
| 412 |
+
if rotation_param is not None:
|
| 413 |
+
self.transforms.append(RandomRotation(**rotation_param))
|
| 414 |
+
|
| 415 |
+
if perspective_param is not None:
|
| 416 |
+
self.transforms.append(RandomPerspective(**perspective_param))
|
| 417 |
+
|
| 418 |
+
if resize_param is not None:
|
| 419 |
+
self.transforms.append(RandomResize(**resize_param))
|
| 420 |
+
|
| 421 |
+
if crop_param is not None:
|
| 422 |
+
self.transforms.append(RandomCrop(**crop_param))
|
| 423 |
+
|
| 424 |
+
if jitter_param is not None:
|
| 425 |
+
self.transforms.append(ColorJitter(**jitter_param))
|
| 426 |
+
|
| 427 |
+
def __call__(self, clip):
|
| 428 |
+
for t in self.transforms:
|
| 429 |
+
clip = t(clip)
|
| 430 |
+
return clip
|
FONT/config/MEAD_emo_video_aug_delta_4_crop_random_crop.yaml
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset_params:
|
| 2 |
+
root_dir: /mnt/lustre/share_data/jixinya/MEAD/
|
| 3 |
+
frame_shape: [256, 256, 3]
|
| 4 |
+
id_sampling: False
|
| 5 |
+
pairs_list: Random_choice
|
| 6 |
+
augmentation_params:
|
| 7 |
+
crop_mouth_param:
|
| 8 |
+
center_x: 135
|
| 9 |
+
center_y: 190
|
| 10 |
+
mask_width: 100
|
| 11 |
+
mask_height: 60
|
| 12 |
+
rotation_param:
|
| 13 |
+
degrees: 30
|
| 14 |
+
perspective_param:
|
| 15 |
+
pers_num: 30
|
| 16 |
+
enlarge_num: 40
|
| 17 |
+
flip_param:
|
| 18 |
+
horizontal_flip: True
|
| 19 |
+
time_flip: False
|
| 20 |
+
jitter_param:
|
| 21 |
+
brightness: 0
|
| 22 |
+
contrast: 0
|
| 23 |
+
saturation: 0
|
| 24 |
+
hue: 0
|
| 25 |
+
|
| 26 |
+
model_params:
|
| 27 |
+
common_params:
|
| 28 |
+
num_kp: 10
|
| 29 |
+
num_channels: 3
|
| 30 |
+
estimate_jacobian: True
|
| 31 |
+
audio_params:
|
| 32 |
+
num_kp: 10
|
| 33 |
+
num_channels : 3
|
| 34 |
+
num_channels_a : 3
|
| 35 |
+
estimate_jacobian: True
|
| 36 |
+
kp_detector_params:
|
| 37 |
+
temperature: 0.1
|
| 38 |
+
block_expansion: 32
|
| 39 |
+
max_features: 1024
|
| 40 |
+
scale_factor: 0.25
|
| 41 |
+
num_blocks: 5
|
| 42 |
+
generator_params:
|
| 43 |
+
block_expansion: 64
|
| 44 |
+
max_features: 512
|
| 45 |
+
num_down_blocks: 2
|
| 46 |
+
num_bottleneck_blocks: 6
|
| 47 |
+
estimate_occlusion_map: True
|
| 48 |
+
dense_motion_params:
|
| 49 |
+
block_expansion: 64
|
| 50 |
+
max_features: 1024
|
| 51 |
+
num_blocks: 5
|
| 52 |
+
scale_factor: 0.25
|
| 53 |
+
discriminator_params:
|
| 54 |
+
scales: [1]
|
| 55 |
+
block_expansion: 32
|
| 56 |
+
max_features: 512
|
| 57 |
+
num_blocks: 4
|
| 58 |
+
sn: True
|
| 59 |
+
|
| 60 |
+
train_params:
|
| 61 |
+
type: linear_4
|
| 62 |
+
smooth: False
|
| 63 |
+
jaco_net: cnn
|
| 64 |
+
ldmark: fake
|
| 65 |
+
generator: not
|
| 66 |
+
train_generator: False
|
| 67 |
+
num_epochs: 300
|
| 68 |
+
num_repeats: 1
|
| 69 |
+
epoch_milestones: [60, 90]
|
| 70 |
+
lr_generator: 2.0e-4
|
| 71 |
+
lr_discriminator: 2.0e-4
|
| 72 |
+
lr_kp_detector: 2.0e-4
|
| 73 |
+
lr_audio_feature: 2.0e-4
|
| 74 |
+
batch_size: 16
|
| 75 |
+
scales: [1, 0.5, 0.25, 0.125]
|
| 76 |
+
checkpoint_freq: 1
|
| 77 |
+
transform_params:
|
| 78 |
+
sigma_affine: 0.05
|
| 79 |
+
sigma_tps: 0.005
|
| 80 |
+
points_tps: 5
|
| 81 |
+
loss_weights:
|
| 82 |
+
generator_gan: 0
|
| 83 |
+
discriminator_gan: 1
|
| 84 |
+
feature_matching: [10, 10, 10, 10]
|
| 85 |
+
perceptual: [10, 10, 10, 10, 10]
|
| 86 |
+
equivariance_value: 0
|
| 87 |
+
equivariance_jacobian: 0
|
| 88 |
+
emo: 10
|
| 89 |
+
|
| 90 |
+
reconstruction_params:
|
| 91 |
+
num_videos: 1000
|
| 92 |
+
format: '.mp4'
|
| 93 |
+
|
| 94 |
+
animate_params:
|
| 95 |
+
num_pairs: 50
|
| 96 |
+
format: '.mp4'
|
| 97 |
+
normalization_params:
|
| 98 |
+
adapt_movement_scale: False
|
| 99 |
+
use_relative_movement: True
|
| 100 |
+
use_relative_jacobian: True
|
| 101 |
+
|
| 102 |
+
visualizer_params:
|
| 103 |
+
kp_size: 5
|
| 104 |
+
draw_border: True
|
| 105 |
+
colormap: 'gist_rainbow'
|
FONT/config/train_part1.yaml
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset_params:
|
| 2 |
+
name: Lrw
|
| 3 |
+
root_dir: dataset/LRW/
|
| 4 |
+
frame_shape: [256, 256, 3]
|
| 5 |
+
id_sampling: False
|
| 6 |
+
augmentation_params:
|
| 7 |
+
flip_param:
|
| 8 |
+
horizontal_flip: False
|
| 9 |
+
time_flip: False
|
| 10 |
+
jitter_param:
|
| 11 |
+
brightness: 0.1
|
| 12 |
+
contrast: 0.1
|
| 13 |
+
saturation: 0.1
|
| 14 |
+
hue: 0.1
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
model_params:
|
| 18 |
+
common_params:
|
| 19 |
+
num_kp: 10
|
| 20 |
+
num_channels: 3
|
| 21 |
+
estimate_jacobian: True
|
| 22 |
+
audio_params:
|
| 23 |
+
num_kp: 10
|
| 24 |
+
num_channels : 3
|
| 25 |
+
num_channels_a : 3
|
| 26 |
+
estimate_jacobian: True
|
| 27 |
+
kp_detector_params:
|
| 28 |
+
temperature: 0.1
|
| 29 |
+
block_expansion: 32
|
| 30 |
+
max_features: 1024
|
| 31 |
+
scale_factor: 0.25
|
| 32 |
+
num_blocks: 5
|
| 33 |
+
generator_params:
|
| 34 |
+
block_expansion: 64
|
| 35 |
+
max_features: 512
|
| 36 |
+
num_down_blocks: 2
|
| 37 |
+
num_bottleneck_blocks: 6
|
| 38 |
+
estimate_occlusion_map: True
|
| 39 |
+
dense_motion_params:
|
| 40 |
+
block_expansion: 64
|
| 41 |
+
max_features: 1024
|
| 42 |
+
num_blocks: 5
|
| 43 |
+
scale_factor: 0.25
|
| 44 |
+
discriminator_params:
|
| 45 |
+
scales: [1]
|
| 46 |
+
block_expansion: 32
|
| 47 |
+
max_features: 512
|
| 48 |
+
num_blocks: 4
|
| 49 |
+
sn: True
|
| 50 |
+
|
| 51 |
+
train_params:
|
| 52 |
+
jaco_net: cnn
|
| 53 |
+
ldmark: fake
|
| 54 |
+
generator: not
|
| 55 |
+
num_epochs: 600
|
| 56 |
+
num_repeats: 1
|
| 57 |
+
epoch_milestones: [60, 90]
|
| 58 |
+
lr_generator: 2.0e-4
|
| 59 |
+
lr_discriminator: 2.0e-4
|
| 60 |
+
lr_kp_detector: 2.0e-4
|
| 61 |
+
lr_audio_feature: 2.0e-4
|
| 62 |
+
batch_size: 16
|
| 63 |
+
scales: [1, 0.5, 0.25, 0.125]
|
| 64 |
+
checkpoint_freq: 1
|
| 65 |
+
transform_params:
|
| 66 |
+
sigma_affine: 0.05
|
| 67 |
+
sigma_tps: 0.005
|
| 68 |
+
points_tps: 5
|
| 69 |
+
loss_weights:
|
| 70 |
+
generator_gan: 0
|
| 71 |
+
discriminator_gan: 0
|
| 72 |
+
feature_matching: [10, 10, 10, 10]
|
| 73 |
+
perceptual: [10, 10, 10, 10, 10]
|
| 74 |
+
equivariance_value: 0
|
| 75 |
+
equivariance_jacobian: 0
|
| 76 |
+
audio: 10
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
visualizer_params:
|
| 81 |
+
kp_size: 5
|
| 82 |
+
draw_border: True
|
| 83 |
+
colormap: 'gist_rainbow'
|
FONT/config/train_part1_fine_tune.yaml
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset_params:
|
| 2 |
+
name: Lrw
|
| 3 |
+
root_dir: dataset/LRW/
|
| 4 |
+
frame_shape: [256, 256, 3]
|
| 5 |
+
id_sampling: False
|
| 6 |
+
augmentation_params:
|
| 7 |
+
flip_param:
|
| 8 |
+
horizontal_flip: False
|
| 9 |
+
time_flip: False
|
| 10 |
+
jitter_param:
|
| 11 |
+
brightness: 0.1
|
| 12 |
+
contrast: 0.1
|
| 13 |
+
saturation: 0.1
|
| 14 |
+
hue: 0.1
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
model_params:
|
| 18 |
+
common_params:
|
| 19 |
+
num_kp: 10
|
| 20 |
+
num_channels: 3
|
| 21 |
+
estimate_jacobian: True
|
| 22 |
+
audio_params:
|
| 23 |
+
num_kp: 10
|
| 24 |
+
num_channels : 3
|
| 25 |
+
num_channels_a : 3
|
| 26 |
+
estimate_jacobian: True
|
| 27 |
+
kp_detector_params:
|
| 28 |
+
temperature: 0.1
|
| 29 |
+
block_expansion: 32
|
| 30 |
+
max_features: 1024
|
| 31 |
+
scale_factor: 0.25
|
| 32 |
+
num_blocks: 5
|
| 33 |
+
generator_params:
|
| 34 |
+
block_expansion: 64
|
| 35 |
+
max_features: 512
|
| 36 |
+
num_down_blocks: 2
|
| 37 |
+
num_bottleneck_blocks: 6
|
| 38 |
+
estimate_occlusion_map: True
|
| 39 |
+
dense_motion_params:
|
| 40 |
+
block_expansion: 64
|
| 41 |
+
max_features: 1024
|
| 42 |
+
num_blocks: 5
|
| 43 |
+
scale_factor: 0.25
|
| 44 |
+
discriminator_params:
|
| 45 |
+
scales: [1]
|
| 46 |
+
block_expansion: 32
|
| 47 |
+
max_features: 512
|
| 48 |
+
num_blocks: 4
|
| 49 |
+
sn: True
|
| 50 |
+
|
| 51 |
+
train_params:
|
| 52 |
+
jaco_net: cnn
|
| 53 |
+
ldmark: fake
|
| 54 |
+
generator: audio
|
| 55 |
+
num_epochs: 600
|
| 56 |
+
num_repeats: 1
|
| 57 |
+
epoch_milestones: [60, 90]
|
| 58 |
+
lr_generator: 2.0e-4
|
| 59 |
+
lr_discriminator: 2.0e-4
|
| 60 |
+
lr_kp_detector: 2.0e-4
|
| 61 |
+
lr_audio_feature: 2.0e-4
|
| 62 |
+
batch_size: 6
|
| 63 |
+
scales: [1, 0.5, 0.25, 0.125]
|
| 64 |
+
checkpoint_freq: 1
|
| 65 |
+
transform_params:
|
| 66 |
+
sigma_affine: 0.05
|
| 67 |
+
sigma_tps: 0.005
|
| 68 |
+
points_tps: 5
|
| 69 |
+
loss_weights:
|
| 70 |
+
generator_gan: 0
|
| 71 |
+
discriminator_gan: 0
|
| 72 |
+
feature_matching: [10, 10, 10, 10]
|
| 73 |
+
perceptual: [0.1, 0.1, 0.1, 0.1, 0.1]
|
| 74 |
+
equivariance_value: 0
|
| 75 |
+
equivariance_jacobian: 0
|
| 76 |
+
audio: 10
|
| 77 |
+
|
| 78 |
+
visualizer_params:
|
| 79 |
+
kp_size: 5
|
| 80 |
+
draw_border: True
|
| 81 |
+
colormap: 'gist_rainbow'
|
FONT/config/train_part1_fine_tune_hdtf.yaml
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset_params:
|
| 2 |
+
name: hdtf
|
| 3 |
+
root_dir: dataset/LRW/
|
| 4 |
+
frame_shape: [256, 256, 3]
|
| 5 |
+
id_sampling: False
|
| 6 |
+
augmentation_params:
|
| 7 |
+
flip_param:
|
| 8 |
+
horizontal_flip: False
|
| 9 |
+
time_flip: False
|
| 10 |
+
jitter_param:
|
| 11 |
+
brightness: 0.1
|
| 12 |
+
contrast: 0.1
|
| 13 |
+
saturation: 0.1
|
| 14 |
+
hue: 0.1
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
model_params:
|
| 18 |
+
common_params:
|
| 19 |
+
num_kp: 10
|
| 20 |
+
num_channels: 3
|
| 21 |
+
estimate_jacobian: True
|
| 22 |
+
audio_params:
|
| 23 |
+
num_kp: 10
|
| 24 |
+
num_channels : 3
|
| 25 |
+
num_channels_a : 3
|
| 26 |
+
estimate_jacobian: True
|
| 27 |
+
kp_detector_params:
|
| 28 |
+
temperature: 0.1
|
| 29 |
+
block_expansion: 32
|
| 30 |
+
max_features: 1024
|
| 31 |
+
scale_factor: 0.25
|
| 32 |
+
num_blocks: 5
|
| 33 |
+
generator_params:
|
| 34 |
+
block_expansion: 64
|
| 35 |
+
max_features: 512
|
| 36 |
+
num_down_blocks: 2
|
| 37 |
+
num_bottleneck_blocks: 6
|
| 38 |
+
estimate_occlusion_map: True
|
| 39 |
+
dense_motion_params:
|
| 40 |
+
block_expansion: 64
|
| 41 |
+
max_features: 1024
|
| 42 |
+
num_blocks: 5
|
| 43 |
+
scale_factor: 0.25
|
| 44 |
+
discriminator_params:
|
| 45 |
+
scales: [1]
|
| 46 |
+
block_expansion: 32
|
| 47 |
+
max_features: 512
|
| 48 |
+
num_blocks: 4
|
| 49 |
+
sn: True
|
| 50 |
+
|
| 51 |
+
train_params:
|
| 52 |
+
jaco_net: cnn
|
| 53 |
+
ldmark: fake
|
| 54 |
+
generator: audio
|
| 55 |
+
num_epochs: 7000
|
| 56 |
+
num_repeats: 1
|
| 57 |
+
epoch_milestones: [60, 90]
|
| 58 |
+
lr_generator: 2.0e-4
|
| 59 |
+
lr_discriminator: 2.0e-4
|
| 60 |
+
lr_kp_detector: 2.0e-4
|
| 61 |
+
lr_audio_feature: 2.0e-4
|
| 62 |
+
batch_size: 6
|
| 63 |
+
scales: [1, 0.5, 0.25, 0.125]
|
| 64 |
+
checkpoint_freq: 1
|
| 65 |
+
transform_params:
|
| 66 |
+
sigma_affine: 0.05
|
| 67 |
+
sigma_tps: 0.005
|
| 68 |
+
points_tps: 5
|
| 69 |
+
loss_weights:
|
| 70 |
+
generator_gan: 0
|
| 71 |
+
discriminator_gan: 0
|
| 72 |
+
feature_matching: [10, 10, 10, 10]
|
| 73 |
+
perceptual: [0.1, 0.1, 0.1, 0.1, 0.1]
|
| 74 |
+
equivariance_value: 0
|
| 75 |
+
equivariance_jacobian: 0
|
| 76 |
+
audio: 10
|
| 77 |
+
|
| 78 |
+
visualizer_params:
|
| 79 |
+
kp_size: 5
|
| 80 |
+
draw_border: True
|
| 81 |
+
colormap: 'gist_rainbow'
|
FONT/config/train_part1_hdtf.yaml
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset_params:
|
| 2 |
+
name: hdtf
|
| 3 |
+
root_dir: dataset/LRW/
|
| 4 |
+
frame_shape: [256, 256, 3]
|
| 5 |
+
id_sampling: False
|
| 6 |
+
augmentation_params:
|
| 7 |
+
flip_param:
|
| 8 |
+
horizontal_flip: False
|
| 9 |
+
time_flip: False
|
| 10 |
+
jitter_param:
|
| 11 |
+
brightness: 0.1
|
| 12 |
+
contrast: 0.1
|
| 13 |
+
saturation: 0.1
|
| 14 |
+
hue: 0.1
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
model_params:
|
| 18 |
+
common_params:
|
| 19 |
+
num_kp: 10
|
| 20 |
+
num_channels: 3
|
| 21 |
+
estimate_jacobian: True
|
| 22 |
+
audio_params:
|
| 23 |
+
num_kp: 10
|
| 24 |
+
num_channels : 3
|
| 25 |
+
num_channels_a : 3
|
| 26 |
+
estimate_jacobian: True
|
| 27 |
+
kp_detector_params:
|
| 28 |
+
temperature: 0.1
|
| 29 |
+
block_expansion: 32
|
| 30 |
+
max_features: 1024
|
| 31 |
+
scale_factor: 0.25
|
| 32 |
+
num_blocks: 5
|
| 33 |
+
generator_params:
|
| 34 |
+
block_expansion: 64
|
| 35 |
+
max_features: 512
|
| 36 |
+
num_down_blocks: 2
|
| 37 |
+
num_bottleneck_blocks: 6
|
| 38 |
+
estimate_occlusion_map: True
|
| 39 |
+
dense_motion_params:
|
| 40 |
+
block_expansion: 64
|
| 41 |
+
max_features: 1024
|
| 42 |
+
num_blocks: 5
|
| 43 |
+
scale_factor: 0.25
|
| 44 |
+
discriminator_params:
|
| 45 |
+
scales: [1]
|
| 46 |
+
block_expansion: 32
|
| 47 |
+
max_features: 512
|
| 48 |
+
num_blocks: 4
|
| 49 |
+
sn: True
|
| 50 |
+
|
| 51 |
+
train_params:
|
| 52 |
+
jaco_net: cnn
|
| 53 |
+
ldmark: fake
|
| 54 |
+
generator: not
|
| 55 |
+
num_epochs: 100000
|
| 56 |
+
num_repeats: 1
|
| 57 |
+
epoch_milestones: [60, 90]
|
| 58 |
+
lr_generator: 2.0e-4
|
| 59 |
+
lr_discriminator: 2.0e-4
|
| 60 |
+
lr_kp_detector: 2.0e-4
|
| 61 |
+
lr_audio_feature: 2.0e-4
|
| 62 |
+
batch_size: 16
|
| 63 |
+
scales: [1, 0.5, 0.25, 0.125]
|
| 64 |
+
checkpoint_freq: 1
|
| 65 |
+
transform_params:
|
| 66 |
+
sigma_affine: 0.05
|
| 67 |
+
sigma_tps: 0.005
|
| 68 |
+
points_tps: 5
|
| 69 |
+
loss_weights:
|
| 70 |
+
generator_gan: 0
|
| 71 |
+
discriminator_gan: 0
|
| 72 |
+
feature_matching: [10, 10, 10, 10]
|
| 73 |
+
perceptual: [10, 10, 10, 10, 10]
|
| 74 |
+
equivariance_value: 0
|
| 75 |
+
equivariance_jacobian: 0
|
| 76 |
+
audio: 10
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
visualizer_params:
|
| 81 |
+
kp_size: 5
|
| 82 |
+
draw_border: True
|
| 83 |
+
colormap: 'gist_rainbow'
|
FONT/config/train_part2.yaml
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
dataset_params:
|
| 2 |
+
name: MEAD
|
| 3 |
+
root_dir: dataset/MEAD/
|
| 4 |
+
frame_shape: [256, 256, 3]
|
| 5 |
+
id_sampling: False
|
| 6 |
+
augmentation_params:
|
| 7 |
+
crop_mouth_param:
|
| 8 |
+
center_x: 135
|
| 9 |
+
center_y: 190
|
| 10 |
+
mask_width: 100
|
| 11 |
+
mask_height: 60
|
| 12 |
+
rotation_param:
|
| 13 |
+
degrees: 30
|
| 14 |
+
perspective_param:
|
| 15 |
+
pers_num: 30
|
| 16 |
+
enlarge_num: 40
|
| 17 |
+
flip_param:
|
| 18 |
+
horizontal_flip: True
|
| 19 |
+
time_flip: False
|
| 20 |
+
jitter_param:
|
| 21 |
+
brightness: 0
|
| 22 |
+
contrast: 0
|
| 23 |
+
saturation: 0
|
| 24 |
+
hue: 0
|
| 25 |
+
|
| 26 |
+
model_params:
|
| 27 |
+
common_params:
|
| 28 |
+
num_kp: 10
|
| 29 |
+
num_channels: 3
|
| 30 |
+
estimate_jacobian: True
|
| 31 |
+
audio_params:
|
| 32 |
+
num_kp: 10
|
| 33 |
+
num_channels : 3
|
| 34 |
+
num_channels_a : 3
|
| 35 |
+
estimate_jacobian: True
|
| 36 |
+
kp_detector_params:
|
| 37 |
+
temperature: 0.1
|
| 38 |
+
block_expansion: 32
|
| 39 |
+
max_features: 1024
|
| 40 |
+
scale_factor: 0.25
|
| 41 |
+
num_blocks: 5
|
| 42 |
+
generator_params:
|
| 43 |
+
block_expansion: 64
|
| 44 |
+
max_features: 512
|
| 45 |
+
num_down_blocks: 2
|
| 46 |
+
num_bottleneck_blocks: 6
|
| 47 |
+
estimate_occlusion_map: True
|
| 48 |
+
dense_motion_params:
|
| 49 |
+
block_expansion: 64
|
| 50 |
+
max_features: 1024
|
| 51 |
+
num_blocks: 5
|
| 52 |
+
scale_factor: 0.25
|
| 53 |
+
discriminator_params:
|
| 54 |
+
scales: [1]
|
| 55 |
+
block_expansion: 32
|
| 56 |
+
max_features: 512
|
| 57 |
+
num_blocks: 4
|
| 58 |
+
sn: True
|
| 59 |
+
|
| 60 |
+
train_params:
|
| 61 |
+
type: linear_4
|
| 62 |
+
smooth: False
|
| 63 |
+
jaco_net: cnn
|
| 64 |
+
ldmark: fake
|
| 65 |
+
generator: not
|
| 66 |
+
num_epochs: 300
|
| 67 |
+
num_repeats: 1
|
| 68 |
+
epoch_milestones: [60, 90]
|
| 69 |
+
lr_generator: 2.0e-4
|
| 70 |
+
lr_discriminator: 2.0e-4
|
| 71 |
+
lr_kp_detector: 2.0e-4
|
| 72 |
+
lr_audio_feature: 2.0e-4
|
| 73 |
+
batch_size: 16
|
| 74 |
+
scales: [1, 0.5, 0.25, 0.125]
|
| 75 |
+
checkpoint_freq: 1
|
| 76 |
+
transform_params:
|
| 77 |
+
sigma_affine: 0.05
|
| 78 |
+
sigma_tps: 0.005
|
| 79 |
+
points_tps: 5
|
| 80 |
+
loss_weights:
|
| 81 |
+
generator_gan: 0
|
| 82 |
+
discriminator_gan: 0
|
| 83 |
+
feature_matching: [10, 10, 10, 10]
|
| 84 |
+
perceptual: [10, 10, 10, 10, 10]
|
| 85 |
+
equivariance_value: 0
|
| 86 |
+
equivariance_jacobian: 0
|
| 87 |
+
emo: 10
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
visualizer_params:
|
| 91 |
+
kp_size: 5
|
| 92 |
+
draw_border: True
|
| 93 |
+
colormap: 'gist_rainbow'
|
FONT/demo.py
ADDED
|
@@ -0,0 +1,632 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Created on Wed Oct 6 20:57:27 2021
|
| 5 |
+
|
| 6 |
+
@author: thea
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import matplotlib
|
| 10 |
+
matplotlib.use('Agg')
|
| 11 |
+
import os,sys
|
| 12 |
+
import yaml
|
| 13 |
+
from argparse import ArgumentParser
|
| 14 |
+
from tqdm import tqdm
|
| 15 |
+
from skimage import io, img_as_float32
|
| 16 |
+
import imageio
|
| 17 |
+
import numpy as np
|
| 18 |
+
from skimage.transform import resize
|
| 19 |
+
from skimage import img_as_ubyte
|
| 20 |
+
import torch
|
| 21 |
+
from filter1 import OneEuroFilter
|
| 22 |
+
import torch.utils
|
| 23 |
+
|
| 24 |
+
from torch.autograd import Variable
|
| 25 |
+
from modules.generator import OcclusionAwareGenerator
|
| 26 |
+
from modules.keypoint_detector import KPDetector, KPDetector_a
|
| 27 |
+
from modules.util import AT_net, Emotion_k, Emotion_map, AT_net2
|
| 28 |
+
from augmentation import AllAugmentationTransform
|
| 29 |
+
|
| 30 |
+
from scipy.spatial import ConvexHull
|
| 31 |
+
import audio
|
| 32 |
+
import random
|
| 33 |
+
import python_speech_features
|
| 34 |
+
from pathlib import Path
|
| 35 |
+
import dlib
|
| 36 |
+
import cv2
|
| 37 |
+
from skimage.draw import circle
|
| 38 |
+
import matplotlib.pyplot as plt
|
| 39 |
+
import librosa
|
| 40 |
+
from skimage import transform as tf
|
| 41 |
+
import torch.nn.functional as F
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
detector = dlib.get_frontal_face_detector()
|
| 46 |
+
predictor = dlib.shape_predictor('./ckpt/shape_predictor_68_face_landmarks.dat')
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def load_checkpoints(opt, checkpoint_path, audio_checkpoint_path, emo_checkpoint_path, kp_checkpoint_path, cpu=False):
|
| 52 |
+
|
| 53 |
+
with open(opt.config) as f:
|
| 54 |
+
config = yaml.load(f, Loader=yaml.FullLoader)
|
| 55 |
+
|
| 56 |
+
generator = OcclusionAwareGenerator(**config['model_params']['generator_params'],
|
| 57 |
+
**config['model_params']['common_params'])
|
| 58 |
+
if not cpu:
|
| 59 |
+
generator.cuda()
|
| 60 |
+
|
| 61 |
+
kp_detector = KPDetector(**config['model_params']['kp_detector_params'],
|
| 62 |
+
**config['model_params']['common_params'])
|
| 63 |
+
if not cpu:
|
| 64 |
+
kp_detector.cuda()
|
| 65 |
+
|
| 66 |
+
kp_detector_a = KPDetector_a(**config['model_params']['kp_detector_params'],
|
| 67 |
+
**config['model_params']['audio_params'])
|
| 68 |
+
|
| 69 |
+
audio_feature = AT_net2()
|
| 70 |
+
# audio_feature = AT_net()
|
| 71 |
+
# audio_feature = AT_net_ori()
|
| 72 |
+
if opt.type.startswith('linear'):
|
| 73 |
+
emo_detector = Emotion_k(block_expansion=32, num_channels=3, max_features=1024,
|
| 74 |
+
num_blocks=5, scale_factor=0.25, num_classes=8)
|
| 75 |
+
elif opt.type.startswith('map'):
|
| 76 |
+
emo_detector = Emotion_map(block_expansion=32, num_channels=3, max_features=1024,
|
| 77 |
+
num_blocks=5, scale_factor=0.25, num_classes=8)
|
| 78 |
+
if not cpu:
|
| 79 |
+
kp_detector_a.cuda()
|
| 80 |
+
audio_feature.cuda()
|
| 81 |
+
emo_detector.cuda()
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
if cpu:
|
| 87 |
+
checkpoint = torch.load(checkpoint_path, map_location=torch.device('cpu'))
|
| 88 |
+
audio_checkpoint = torch.load(audio_checkpoint_path, map_location=torch.device('cpu'))
|
| 89 |
+
emo_checkpoint = torch.load(emo_checkpoint_path, map_location=torch.device('cpu'))
|
| 90 |
+
kp_checkpoint = torch.load(kp_checkpoint_path, map_location=torch.device('cpu'))
|
| 91 |
+
else:
|
| 92 |
+
checkpoint = torch.load(checkpoint_path)
|
| 93 |
+
audio_checkpoint = torch.load(audio_checkpoint_path)
|
| 94 |
+
emo_checkpoint = torch.load(emo_checkpoint_path)
|
| 95 |
+
kp_checkpoint = torch.load(kp_checkpoint_path)
|
| 96 |
+
|
| 97 |
+
generator.load_state_dict(checkpoint['generator'])
|
| 98 |
+
kp_detector.load_state_dict(kp_checkpoint['kp_detector'])
|
| 99 |
+
audio_feature.load_state_dict(audio_checkpoint['audio_feature'], strict=False)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
kp_detector_a.load_state_dict(audio_checkpoint['kp_detector_a'])
|
| 105 |
+
emo_detector.load_state_dict(emo_checkpoint['emo_detector'])
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
if not cpu:
|
| 109 |
+
generator = generator.cuda()
|
| 110 |
+
kp_detector = kp_detector.cuda()
|
| 111 |
+
audio_feature = audio_feature.cuda()
|
| 112 |
+
kp_detector_a = kp_detector_a.cuda()
|
| 113 |
+
emo_detector = emo_detector.cuda()
|
| 114 |
+
|
| 115 |
+
generator.eval()
|
| 116 |
+
kp_detector.eval()
|
| 117 |
+
audio_feature.eval()
|
| 118 |
+
kp_detector_a.eval()
|
| 119 |
+
emo_detector.eval()
|
| 120 |
+
return generator, kp_detector, kp_detector_a, audio_feature, emo_detector
|
| 121 |
+
|
| 122 |
+
def normalize_kp(kp_source, kp_driving, kp_driving_initial, adapt_movement_scale=False,
|
| 123 |
+
use_relative_movement=False, use_relative_jacobian=False):
|
| 124 |
+
if adapt_movement_scale:
|
| 125 |
+
source_area = ConvexHull(kp_source['value'][0].data.cpu().numpy()).volume
|
| 126 |
+
driving_area = ConvexHull(kp_driving_initial['value'][0].data.cpu().numpy()).volume
|
| 127 |
+
adapt_movement_scale = np.sqrt(source_area) / np.sqrt(driving_area)
|
| 128 |
+
else:
|
| 129 |
+
adapt_movement_scale = 1
|
| 130 |
+
|
| 131 |
+
kp_new = {k: v for k, v in kp_driving.items()}
|
| 132 |
+
|
| 133 |
+
if use_relative_movement:
|
| 134 |
+
kp_value_diff = (kp_driving['value'] - kp_driving_initial['value'])
|
| 135 |
+
kp_value_diff *= adapt_movement_scale
|
| 136 |
+
kp_new['value'] = kp_value_diff + kp_source['value']
|
| 137 |
+
|
| 138 |
+
if use_relative_jacobian:
|
| 139 |
+
jacobian_diff = torch.matmul(kp_driving['jacobian'], torch.inverse(kp_driving_initial['jacobian']))
|
| 140 |
+
kp_new['jacobian'] = torch.matmul(jacobian_diff, kp_source['jacobian'])
|
| 141 |
+
|
| 142 |
+
return kp_new
|
| 143 |
+
|
| 144 |
+
def shape_to_np(shape, dtype="int"):
|
| 145 |
+
# initialize the list of (x, y)-coordinates
|
| 146 |
+
coords = np.zeros((shape.num_parts, 2), dtype=dtype)
|
| 147 |
+
|
| 148 |
+
# loop over all facial landmarks and convert them
|
| 149 |
+
# to a 2-tuple of (x, y)-coordinates
|
| 150 |
+
for i in range(0, shape.num_parts):
|
| 151 |
+
coords[i] = (shape.part(i).x, shape.part(i).y)
|
| 152 |
+
|
| 153 |
+
# return the list of (x, y)-coordinates
|
| 154 |
+
return coords
|
| 155 |
+
|
| 156 |
+
def get_aligned_image(driving_video, opt):
|
| 157 |
+
aligned_array = []
|
| 158 |
+
|
| 159 |
+
video_array = np.array(driving_video)
|
| 160 |
+
source_image=video_array[0]
|
| 161 |
+
# aligned_array.append(source_image)
|
| 162 |
+
source_image = np.array(source_image * 255, dtype=np.uint8)
|
| 163 |
+
gray = cv2.cvtColor(source_image, cv2.COLOR_BGR2GRAY)
|
| 164 |
+
rects = detector(gray, 1) #detect human face
|
| 165 |
+
for (i, rect) in enumerate(rects):
|
| 166 |
+
template = predictor(gray, rect) #detect 68 points
|
| 167 |
+
template = shape_to_np(template)
|
| 168 |
+
|
| 169 |
+
if opt.emotion == 'surprised' or opt.emotion == 'fear':
|
| 170 |
+
template = template-[0,10]
|
| 171 |
+
for i in range(len(video_array)):
|
| 172 |
+
image=np.array(video_array[i] * 255, dtype=np.uint8)
|
| 173 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| 174 |
+
rects = detector(gray, 1) #detect human face
|
| 175 |
+
for (j, rect) in enumerate(rects):
|
| 176 |
+
shape = predictor(gray, rect) #detect 68 points
|
| 177 |
+
shape = shape_to_np(shape)
|
| 178 |
+
|
| 179 |
+
pts2 = np.float32(template[:35,:])
|
| 180 |
+
pts1 = np.float32(shape[:35,:]) #eye and nose
|
| 181 |
+
|
| 182 |
+
# pts2 = np.float32(np.concatenate((template[:16,:],template[27:36,:]),axis = 0))
|
| 183 |
+
# pts1 = np.float32(np.concatenate((shape[:16,:],shape[27:36,:]),axis = 0)) #eye and nose
|
| 184 |
+
# pts1 = np.float32(landmark[17:35,:])
|
| 185 |
+
tform = tf.SimilarityTransform()
|
| 186 |
+
tform.estimate( pts2, pts1) #Set the transformation matrix with the explicit parameters.
|
| 187 |
+
dst = tf.warp(image, tform, output_shape=(256, 256))
|
| 188 |
+
|
| 189 |
+
dst = np.array(dst, dtype=np.float32)
|
| 190 |
+
aligned_array.append(dst)
|
| 191 |
+
|
| 192 |
+
return aligned_array
|
| 193 |
+
|
| 194 |
+
def get_transformed_image(driving_video, opt):
|
| 195 |
+
video_array = np.array(driving_video)
|
| 196 |
+
with open(opt.config) as f:
|
| 197 |
+
config = yaml.load(f, Loader=yaml.FullLoader)
|
| 198 |
+
transformations = AllAugmentationTransform(**config['dataset_params']['augmentation_params'])
|
| 199 |
+
transformed_array = transformations(video_array)
|
| 200 |
+
return transformed_array
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def make_animation_smooth(source_image, driving_video, transformed_video, deco_out, kp_loss, generator, kp_detector, kp_detector_a, emo_detector, opt, relative=True, adapt_movement_scale=True, cpu=False):
|
| 205 |
+
with torch.no_grad():
|
| 206 |
+
predictions = []
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
source = torch.tensor(source_image[np.newaxis].astype(np.float32)).permute(0, 3, 1, 2)
|
| 210 |
+
if not cpu:
|
| 211 |
+
source = source.cuda()
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
driving = torch.tensor(np.array(driving_video)[np.newaxis].astype(np.float32)).permute(0, 4, 1, 2, 3)
|
| 215 |
+
transformed_driving = torch.tensor(np.array(transformed_video)[np.newaxis].astype(np.float32)).permute(0, 4, 1, 2, 3)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
kp_source = kp_detector(source)
|
| 219 |
+
|
| 220 |
+
save_dpi = (100, 100)
|
| 221 |
+
# save kp source image
|
| 222 |
+
# source = kp_source['value'].data.cpu().numpy()
|
| 223 |
+
# kp_array = source[0]
|
| 224 |
+
# image = np.zeros((256,256,3))
|
| 225 |
+
# spatial_size = np.array(image.shape[:2][::-1])[np.newaxis]
|
| 226 |
+
# kp_array = spatial_size * (kp_array + 1) / 2
|
| 227 |
+
# num_kp = kp_array.shape[0]
|
| 228 |
+
colormap = plt.get_cmap('gist_rainbow')
|
| 229 |
+
# for kp_ind, kp in enumerate(kp_array):
|
| 230 |
+
# rr, cc = circle(kp[1], kp[0], 5, shape=image.shape[:2])
|
| 231 |
+
# image[rr, cc] = np.array(colormap(kp_ind / num_kp))[:3]
|
| 232 |
+
# imageio.imsave('./result/kp_source.png', image)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
kp_driving_initial = kp_detector_a(deco_out[:,0])
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
emo_driving_all = []
|
| 240 |
+
features = []
|
| 241 |
+
kp_driving_all = []
|
| 242 |
+
for frame_idx in tqdm(range(len(deco_out[0]))):
|
| 243 |
+
|
| 244 |
+
driving_frame = driving[:, :, frame_idx]
|
| 245 |
+
transformed_frame = transformed_driving[:, :, frame_idx]
|
| 246 |
+
if not cpu:
|
| 247 |
+
driving_frame = driving_frame.cuda()
|
| 248 |
+
transformed_frame = transformed_frame.cuda()
|
| 249 |
+
kp_driving = kp_detector_a(deco_out[:,frame_idx])
|
| 250 |
+
kp_driving_all.append(kp_driving)
|
| 251 |
+
if opt.add_emo:
|
| 252 |
+
value = kp_driving['value']
|
| 253 |
+
jacobian = kp_driving['jacobian']
|
| 254 |
+
if opt.type == 'linear_3':
|
| 255 |
+
emo_driving,_ = emo_detector(transformed_frame,value,jacobian)
|
| 256 |
+
features.append(emo_detector.feature(transformed_frame).data.cpu().numpy())
|
| 257 |
+
|
| 258 |
+
emo_driving_all.append(emo_driving)
|
| 259 |
+
features = np.array(features)
|
| 260 |
+
if opt.add_emo:
|
| 261 |
+
one_euro_filter_v = OneEuroFilter(mincutoff=1, beta=0.2, dcutoff=1.0, freq=100)#1 0.4
|
| 262 |
+
one_euro_filter_j = OneEuroFilter(mincutoff=1, beta=0.2, dcutoff=1.0, freq=100)#1 0.4
|
| 263 |
+
|
| 264 |
+
for j in range(len(emo_driving_all)):
|
| 265 |
+
emo_driving_all[j]['value']=one_euro_filter_v.process(emo_driving_all[j]['value'].cpu()*100)/100
|
| 266 |
+
emo_driving_all[j]['value'] = emo_driving_all[j]['value'].cuda()
|
| 267 |
+
emo_driving_all[j]['jacobian']=one_euro_filter_j.process(emo_driving_all[j]['jacobian'].cpu()*100)/100
|
| 268 |
+
emo_driving_all[j]['jacobian'] = emo_driving_all[j]['jacobian'].cuda()
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
one_euro_filter_v = OneEuroFilter(mincutoff=0.05, beta=8, dcutoff=1.0, freq=100)
|
| 272 |
+
one_euro_filter_j = OneEuroFilter(mincutoff=0.05, beta=8, dcutoff=1.0, freq=100)
|
| 273 |
+
|
| 274 |
+
for j in range(len(kp_driving_all)):
|
| 275 |
+
kp_driving_all[j]['value']=one_euro_filter_v.process(kp_driving_all[j]['value'].cpu()*10)/10
|
| 276 |
+
kp_driving_all[j]['value'] = kp_driving_all[j]['value'].cuda()
|
| 277 |
+
kp_driving_all[j]['jacobian']=one_euro_filter_j.process(kp_driving_all[j]['jacobian'].cpu()*10)/10
|
| 278 |
+
kp_driving_all[j]['jacobian'] = kp_driving_all[j]['jacobian'].cuda()
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
for frame_idx in tqdm(range(len(deco_out[0]))):
|
| 282 |
+
|
| 283 |
+
if opt.check_add:
|
| 284 |
+
kp_driving = kp_detector_a(deco_out[:,0])
|
| 285 |
+
else:
|
| 286 |
+
kp_driving = kp_driving_all[frame_idx]
|
| 287 |
+
|
| 288 |
+
# kp_driving_real = kp_detector(driving_frame)
|
| 289 |
+
|
| 290 |
+
# kp_driving['value'] = (1-opt.weight)*kp_driving['value'] + opt.weight*kp_driving_real['value']
|
| 291 |
+
# kp_driving['jacobian'] = (1-opt.weight)*kp_driving['jacobian'] + opt.weight*kp_driving_real['jacobian']
|
| 292 |
+
|
| 293 |
+
if opt.add_emo:
|
| 294 |
+
emo_driving = emo_driving_all[frame_idx]
|
| 295 |
+
if opt.type == 'linear_3':
|
| 296 |
+
kp_driving['value'][:,1] = kp_driving['value'][:,1] + emo_driving['value'][:,0]*0.2
|
| 297 |
+
kp_driving['jacobian'][:,1] = kp_driving['jacobian'][:,1] + emo_driving['jacobian'][:,0]*0.2
|
| 298 |
+
kp_driving['value'][:,4] = kp_driving['value'][:,4] + emo_driving['value'][:,1]
|
| 299 |
+
kp_driving['jacobian'][:,4] = kp_driving['jacobian'][:,4] + emo_driving['jacobian'][:,1]
|
| 300 |
+
kp_driving['value'][:,6] = kp_driving['value'][:,6] + emo_driving['value'][:,2]
|
| 301 |
+
kp_driving['jacobian'][:,6] = kp_driving['jacobian'][:,6] + emo_driving['jacobian'][:,2]
|
| 302 |
+
# kp_driving['value'][:,8] = kp_driving['value'][:,8] + emo_driving['value'][:,3]
|
| 303 |
+
# kp_driving['jacobian'][:,8] = kp_driving['jacobian'][:,8] + emo_driving['jacobian'][:,3]
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
kp_norm = normalize_kp(kp_source=kp_source, kp_driving=kp_driving,
|
| 307 |
+
kp_driving_initial=kp_driving_initial, use_relative_movement=relative,
|
| 308 |
+
use_relative_jacobian=relative, adapt_movement_scale=adapt_movement_scale)
|
| 309 |
+
out = generator(source, kp_source=kp_source, kp_driving=kp_norm)
|
| 310 |
+
|
| 311 |
+
# occlusion_map = out['occlusion_map'].data.cpu().repeat(1, 3, 1, 1)
|
| 312 |
+
# occlusion_map = F.interpolate(occlusion_map, size=(256,256)).numpy()
|
| 313 |
+
# occlusion_map = np.transpose(occlusion_map, [0, 2, 3, 1])
|
| 314 |
+
# print(occlusion_map.shape)
|
| 315 |
+
# imageio.imsave('./result/occlusion.png', occlusion_map[0], dpi=save_dpi)
|
| 316 |
+
#
|
| 317 |
+
# prediction = out['prediction'].data.cpu().numpy()
|
| 318 |
+
# prediction = np.transpose(prediction, [0, 2, 3, 1])
|
| 319 |
+
# print(prediction.shape)
|
| 320 |
+
# imageio.imsave('./result/prediction.png', prediction[0], dpi=save_dpi)
|
| 321 |
+
#
|
| 322 |
+
# full_mask = []
|
| 323 |
+
# for i in range(out['sparse_deformed'].shape[1]):
|
| 324 |
+
# mask = out['mask'][:, i:(i + 1)].data.cpu().repeat(1, 3, 1, 1)
|
| 325 |
+
# mask = F.interpolate(mask, size=(256,256))
|
| 326 |
+
# mask = np.transpose(mask.numpy(), (0, 2, 3, 1))
|
| 327 |
+
# if i != 0:
|
| 328 |
+
# color = np.array(colormap((i - 1) / (out['sparse_deformed'].shape[1] - 1)))[:3]
|
| 329 |
+
# else:
|
| 330 |
+
# color = np.array((0, 0, 0))
|
| 331 |
+
#
|
| 332 |
+
# color = color.reshape((1, 1, 1, 3))
|
| 333 |
+
#
|
| 334 |
+
# full_mask.append(mask * color)
|
| 335 |
+
# motion_flow = sum(full_mask)
|
| 336 |
+
# print(motion_flow.shape)
|
| 337 |
+
# imageio.imsave('./result/motion_flow.png', motion_flow[0], dpi=save_dpi)
|
| 338 |
+
# quit()
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
predictions.append(np.transpose(out['prediction'].data.cpu().numpy(), [0, 2, 3, 1])[0])
|
| 343 |
+
return predictions, features
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
def test_auido(example_image, audio_feature, all_pose, opt):
|
| 348 |
+
with open(opt.config) as f:
|
| 349 |
+
para = yaml.load(f, Loader=yaml.FullLoader)
|
| 350 |
+
|
| 351 |
+
# encoder = audio_feature()
|
| 352 |
+
if not opt.cpu:
|
| 353 |
+
audio_feature = audio_feature.cuda()
|
| 354 |
+
|
| 355 |
+
audio_feature.eval()
|
| 356 |
+
# decoder.eval()
|
| 357 |
+
test_file = opt.in_file
|
| 358 |
+
pose = all_pose[:,:6]
|
| 359 |
+
if len(pose) == 1:
|
| 360 |
+
pose = np.repeat(pose,100,0)
|
| 361 |
+
|
| 362 |
+
elif opt.smooth_pose:
|
| 363 |
+
one_euro_filter = OneEuroFilter(mincutoff=0.004, beta=0.7, dcutoff=1.0, freq=100)
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
for j in range(len(pose)):
|
| 367 |
+
pose[j]=one_euro_filter.process(pose[j])
|
| 368 |
+
# pose[j]=pose[0]
|
| 369 |
+
|
| 370 |
+
example_image = np.array(example_image, dtype='float32').transpose((2, 0, 1))
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
# # get wav2lip audio feature
|
| 375 |
+
# wav = audio.load_wav(test_file, 16000)
|
| 376 |
+
# mel = audio.melspectrogram(wav)
|
| 377 |
+
# fps = 30
|
| 378 |
+
# mel_step_size = 16
|
| 379 |
+
# mel_chunks = []
|
| 380 |
+
# mel_idx_multiplier = 80. / fps
|
| 381 |
+
# i = 0
|
| 382 |
+
# while 1:
|
| 383 |
+
# start_idx = int(i * mel_idx_multiplier)
|
| 384 |
+
# if start_idx + mel_step_size > len(mel[0]):
|
| 385 |
+
# mel_chunks.append(mel[:, len(mel[0]) - mel_step_size:])
|
| 386 |
+
# break
|
| 387 |
+
# mel_chunks.append(mel[:, start_idx: start_idx + mel_step_size])
|
| 388 |
+
# i += 1
|
| 389 |
+
# mel_chunks = np.stack(mel_chunks, axis=0)
|
| 390 |
+
# input_mfcc = torch.FloatTensor(mel_chunks).cuda()
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
# get atvg audio feature
|
| 394 |
+
speech, sr = librosa.load(test_file, sr=16000)
|
| 395 |
+
# mfcc = python_speech_features.mfcc(speech ,16000,winstep=0.01)
|
| 396 |
+
speech = np.insert(speech, 0, np.zeros(1920))
|
| 397 |
+
speech = np.append(speech, np.zeros(1920))
|
| 398 |
+
mfcc = python_speech_features.mfcc(speech,16000,winstep=0.01)
|
| 399 |
+
ind = 3
|
| 400 |
+
fake_lmark = []
|
| 401 |
+
input_mfcc = []
|
| 402 |
+
while ind <= int(mfcc.shape[0] / 4) - 4:
|
| 403 |
+
t_mfcc = mfcc[(ind - 3) * 4: (ind + 4) * 4, 1:]
|
| 404 |
+
t_mfcc = torch.FloatTensor(t_mfcc).cuda()
|
| 405 |
+
input_mfcc.append(t_mfcc)
|
| 406 |
+
ind += 1
|
| 407 |
+
input_mfcc = torch.stack(input_mfcc, dim=0) # N,28,12
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
print ('=======================================')
|
| 411 |
+
print ('Start to generate images')
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
with torch.no_grad():
|
| 415 |
+
if (len(pose)<len(input_mfcc)):
|
| 416 |
+
gap = len(input_mfcc)-len(pose)
|
| 417 |
+
n = int((gap/len(pose)/2)) +2
|
| 418 |
+
pose = np.concatenate((pose,pose[::-1,:]),axis = 0)
|
| 419 |
+
pose = np.tile(pose, (n,1))
|
| 420 |
+
if(len(pose)>len(input_mfcc)):
|
| 421 |
+
pose = pose[:len(input_mfcc),:]
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
if not opt.cpu:
|
| 425 |
+
example_image = Variable(torch.FloatTensor(example_image.astype(float)) ).cuda()
|
| 426 |
+
example_image = torch.unsqueeze(example_image,0)
|
| 427 |
+
pose = Variable(torch.FloatTensor(pose.astype(float)) ).cuda()
|
| 428 |
+
|
| 429 |
+
pose = pose.unsqueeze(0)
|
| 430 |
+
|
| 431 |
+
input_mfcc = input_mfcc.unsqueeze(0)
|
| 432 |
+
|
| 433 |
+
deco_out = audio_feature(example_image,input_mfcc,pose,para['train_params']['jaco_net'],1.6)
|
| 434 |
+
|
| 435 |
+
# ATNET
|
| 436 |
+
# deco_out = audio_feature(example_image, input_mfcc, pose, para['train_params']['jaco_net'])
|
| 437 |
+
|
| 438 |
+
return deco_out
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
def save(path, frames, format):
|
| 442 |
+
|
| 443 |
+
if format == '.png':
|
| 444 |
+
if not os.path.exists(path):
|
| 445 |
+
|
| 446 |
+
os.makedirs(path)
|
| 447 |
+
for j, frame in enumerate(frames):
|
| 448 |
+
imageio.imsave(path+'/'+str(j)+'.png',frame)
|
| 449 |
+
# imageio.imsave(os.path.join(path, str(j) + '.png'), frames[j])
|
| 450 |
+
else:
|
| 451 |
+
print ("Unknown format %s" % format)
|
| 452 |
+
exit()
|
| 453 |
+
|
| 454 |
+
class VideoWriter(object):
|
| 455 |
+
def __init__(self, path, width, height, fps):
|
| 456 |
+
fourcc = cv2.VideoWriter_fourcc(*'XVID')
|
| 457 |
+
self.path = path
|
| 458 |
+
self.out = cv2.VideoWriter(self.path, fourcc, fps, (width, height))
|
| 459 |
+
|
| 460 |
+
def write_frame(self, frame):
|
| 461 |
+
self.out.write(frame)
|
| 462 |
+
|
| 463 |
+
def end(self):
|
| 464 |
+
self.out.release()
|
| 465 |
+
|
| 466 |
+
def concatenate(number, imgs, save_path):
|
| 467 |
+
width, height = imgs.shape[-3:-1]
|
| 468 |
+
imgs = imgs.reshape(number,-1,width,height,3)
|
| 469 |
+
if number == 2:
|
| 470 |
+
left = imgs[0]
|
| 471 |
+
right = imgs[1]
|
| 472 |
+
|
| 473 |
+
im_all = []
|
| 474 |
+
for i in range(len(left)):
|
| 475 |
+
im = np.concatenate((left[i],right[i]),axis = 1)
|
| 476 |
+
im_all.append(im)
|
| 477 |
+
if number == 3:
|
| 478 |
+
left = imgs[0]
|
| 479 |
+
middle = imgs[1]
|
| 480 |
+
right = imgs[2]
|
| 481 |
+
|
| 482 |
+
im_all = []
|
| 483 |
+
for i in range(len(left)):
|
| 484 |
+
im = np.concatenate((left[i],middle[i],right[i]),axis = 1)
|
| 485 |
+
im_all.append(im)
|
| 486 |
+
if number == 4:
|
| 487 |
+
left = imgs[0]
|
| 488 |
+
left2 = imgs[1]
|
| 489 |
+
right = imgs[2]
|
| 490 |
+
right2 = imgs[3]
|
| 491 |
+
|
| 492 |
+
im_all = []
|
| 493 |
+
for i in range(len(left)):
|
| 494 |
+
im = np.concatenate((left[i],left2[i],right[i],right2[i]),axis = 1)
|
| 495 |
+
im_all.append(im)
|
| 496 |
+
if number == 5:
|
| 497 |
+
left = imgs[0]
|
| 498 |
+
left2 = imgs[1]
|
| 499 |
+
middle = imgs[2]
|
| 500 |
+
right = imgs[3]
|
| 501 |
+
right2 = imgs[4]
|
| 502 |
+
|
| 503 |
+
im_all = []
|
| 504 |
+
for i in range(len(left)):
|
| 505 |
+
im = np.concatenate((left[i],left2[i],middle[i],right[i],right2[i]),axis = 1)
|
| 506 |
+
im_all.append(im)
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
imageio.mimsave(save_path, [img_as_ubyte(frame) for frame in im_all], fps=25)
|
| 510 |
+
|
| 511 |
+
def add_audio(video_name=None, audio_dir = None):
|
| 512 |
+
|
| 513 |
+
command = 'ffmpeg -i ' + video_name + ' -i ' + audio_dir + ' -vcodec copy -acodec copy -y ' + video_name.replace('.mp4','.mov')
|
| 514 |
+
print (command)
|
| 515 |
+
os.system(command)
|
| 516 |
+
|
| 517 |
+
def smooth_pose(pose_file, pose_long):
|
| 518 |
+
start = np.load(pose_file)
|
| 519 |
+
video_pose = np.load(pose_long)
|
| 520 |
+
if video_pose.shape[-1] == 6:
|
| 521 |
+
start = start[:, :6]
|
| 522 |
+
|
| 523 |
+
|
| 524 |
+
delta = video_pose - video_pose[0,:]
|
| 525 |
+
# print(len(delta))
|
| 526 |
+
|
| 527 |
+
pose = np.repeat(start,len(delta),axis = 0)
|
| 528 |
+
all_pose = pose + delta
|
| 529 |
+
|
| 530 |
+
return all_pose
|
| 531 |
+
|
| 532 |
+
def test(opt, name):
|
| 533 |
+
pose_dim = np.load(opt.pose_file).shape[-1]
|
| 534 |
+
all_pose = np.load(opt.pose_file).reshape(-1,pose_dim)
|
| 535 |
+
if opt.pose_long:
|
| 536 |
+
all_pose = smooth_pose(opt.pose_file,opt.pose_given)
|
| 537 |
+
|
| 538 |
+
|
| 539 |
+
source_image = img_as_float32(io.imread(opt.source_image))
|
| 540 |
+
source_image = resize(source_image, (256, 256))[..., :3]
|
| 541 |
+
|
| 542 |
+
reader = imageio.get_reader(opt.driving_video)
|
| 543 |
+
fps = reader.get_meta_data()['fps']
|
| 544 |
+
driving_video = []
|
| 545 |
+
try:
|
| 546 |
+
for im in reader:
|
| 547 |
+
driving_video.append(im)
|
| 548 |
+
except RuntimeError:
|
| 549 |
+
pass
|
| 550 |
+
reader.close()
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
driving_video = [resize(frame, (256, 256))[..., :3] for frame in driving_video]
|
| 554 |
+
driving_video = get_aligned_image(driving_video, opt)
|
| 555 |
+
transformed_video = get_transformed_image(driving_video, opt)
|
| 556 |
+
transformed_video = np.array(transformed_video)
|
| 557 |
+
|
| 558 |
+
generator, kp_detector,kp_detector_a, audio_feature, emo_detector = \
|
| 559 |
+
load_checkpoints(opt=opt, checkpoint_path=opt.checkpoint, audio_checkpoint_path=opt.audio_checkpoint, emo_checkpoint_path = opt.emo_checkpoint, kp_checkpoint_path=opt.kp_checkpoint, cpu=opt.cpu)
|
| 560 |
+
|
| 561 |
+
deco_out = test_auido(source_image, audio_feature, all_pose, opt) #1,N,32+3,64,64
|
| 562 |
+
|
| 563 |
+
|
| 564 |
+
if len(driving_video) < len(deco_out[0]):
|
| 565 |
+
driving_video = np.resize(driving_video,(len(deco_out[0]),256,256,3))
|
| 566 |
+
transformed_video = np.resize(transformed_video,(len(deco_out[0]),256,256,3))
|
| 567 |
+
|
| 568 |
+
else:
|
| 569 |
+
driving_video = driving_video[:len(deco_out[0])]
|
| 570 |
+
opt.add_emo = False
|
| 571 |
+
predictions, _ = make_animation_smooth(source_image, driving_video, transformed_video, deco_out, opt.kp_loss, generator, kp_detector, kp_detector_a, emo_detector, opt, relative=opt.relative, adapt_movement_scale=opt.adapt_scale, cpu=opt.cpu)
|
| 572 |
+
|
| 573 |
+
imageio.mimsave(os.path.join(opt.result_path,'neutral.mp4'), [img_as_ubyte(frame) for frame in predictions], fps=fps)
|
| 574 |
+
predictions = np.array(predictions)
|
| 575 |
+
|
| 576 |
+
opt.add_emo = True
|
| 577 |
+
predictions1,_ = make_animation_smooth(source_image, driving_video, transformed_video, deco_out, opt.kp_loss, generator, kp_detector, kp_detector_a, emo_detector, opt, relative=opt.relative, adapt_movement_scale=opt.adapt_scale, cpu=opt.cpu)
|
| 578 |
+
|
| 579 |
+
imageio.mimsave(os.path.join(opt.result_path,'emotion.mp4'), [img_as_ubyte(frame) for frame in predictions1], fps=fps)
|
| 580 |
+
add_audio(os.path.join(opt.result_path,'emotion.mp4'),opt.in_file)
|
| 581 |
+
predictions1 = np.array(predictions1)
|
| 582 |
+
all_imgs = np.concatenate((driving_video,predictions,predictions1),axis = 0)
|
| 583 |
+
save_path = os.path.join(opt.result_path, 'all.mp4')
|
| 584 |
+
concatenate(3, all_imgs, save_path)
|
| 585 |
+
add_audio(save_path,opt.in_file)
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
|
| 589 |
+
if __name__ == "__main__":
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
|
| 593 |
+
parser = ArgumentParser()
|
| 594 |
+
parser.add_argument("--config", default ='config/MEAD_emo_video_aug_delta_4_crop_random_crop.yaml', help="path to config")#required=True default ='config/vox-256.yaml'
|
| 595 |
+
|
| 596 |
+
parser.add_argument("--audio_checkpoint", default='ckpt/1-6000.pth.tar', help="path to checkpoint to restore")
|
| 597 |
+
parser.add_argument("--checkpoint", default='ckpt/124_52000.pth.tar', help="path to checkpoint to restore")
|
| 598 |
+
parser.add_argument("--kp_checkpoint", default='ckpt/124_52000.pth.tar', help="path to checkpoint to restore")
|
| 599 |
+
# parser.add_argument("--emo_checkpoint", default='ablation/ablation/ten/10-6000.pth.tar', help="path to checkpoint to restore")
|
| 600 |
+
parser.add_argument("--emo_checkpoint", default='ckpt/5-3000.pth.tar', help="path to checkpoint to restore")
|
| 601 |
+
|
| 602 |
+
parser.add_argument("--source_image", default='test/image/21.png', help="path to source image")
|
| 603 |
+
|
| 604 |
+
parser.add_argument("--driving_video", default='test/video/disgusted.mp4', help="path to driving video")#data/M030/video/M030_angry_
|
| 605 |
+
parser.add_argument('--in_file', type=str, default='test/audio/sample1.mov')
|
| 606 |
+
parser.add_argument('--pose_file', type=str, default='test/pose/60.npy')
|
| 607 |
+
parser.add_argument('--pose_given', type=str, default='test/pose_long/50IAfJCypFI_Alex_Kingston_50IAfJCypFI_0001.npy')
|
| 608 |
+
|
| 609 |
+
parser.add_argument("--result_path", default='result/', help="path to output")#'/media/thea/新加卷/fomm/Exp/'+emotion+'.mp4'
|
| 610 |
+
|
| 611 |
+
parser.add_argument("--relative", dest="relative", action="store_true", help="use relative or absolute keypoint coordinates")
|
| 612 |
+
parser.add_argument("--adapt_scale", dest="adapt_scale", action="store_true", help="adapt movement scale based on convex hull of keypoints")
|
| 613 |
+
|
| 614 |
+
parser.add_argument("--cpu", dest="cpu", action="store_true", help="cpu mode.")
|
| 615 |
+
parser.add_argument("--kp_loss", default=0, help="keypoint loss.")
|
| 616 |
+
|
| 617 |
+
parser.add_argument("--smooth_pose", default=True, help="cpu mode.")
|
| 618 |
+
parser.add_argument("--pose_long", default=False, help="use given long poses.")
|
| 619 |
+
parser.add_argument("--weight", default=0, help="cpu mode.")
|
| 620 |
+
parser.add_argument("--add_emo", default=False, help="add emotion.")
|
| 621 |
+
parser.add_argument("--check_add", default=False, help="check emotion displacement.")
|
| 622 |
+
parser.add_argument("--type", default='linear_3', help="add emotion type.")
|
| 623 |
+
parser.add_argument("--emotion", default='disgusted', help="emotion category, 'angry', 'contempt','disgusted','fear','happy','neutral','sad','surprised'.")
|
| 624 |
+
parser.set_defaults(relative=False)
|
| 625 |
+
parser.set_defaults(adapt_scale=False)
|
| 626 |
+
|
| 627 |
+
opt = parser.parse_args()
|
| 628 |
+
# opt.cpu = True
|
| 629 |
+
|
| 630 |
+
test(opt,'test')
|
| 631 |
+
|
| 632 |
+
|
FONT/filter1.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
#import pickle
|
| 3 |
+
import time
|
| 4 |
+
import numpy as np
|
| 5 |
+
import copy
|
| 6 |
+
|
| 7 |
+
from matplotlib import pyplot as plt
|
| 8 |
+
from tqdm import tqdm
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class LowPassFilter:
|
| 14 |
+
def __init__(self):
|
| 15 |
+
self.prev_raw_value = None
|
| 16 |
+
self.prev_filtered_value = None
|
| 17 |
+
|
| 18 |
+
def process(self, value, alpha):
|
| 19 |
+
if self.prev_raw_value is None:
|
| 20 |
+
s = value
|
| 21 |
+
else:
|
| 22 |
+
s = alpha * value + (1.0 - alpha) * self.prev_filtered_value
|
| 23 |
+
self.prev_raw_value = value
|
| 24 |
+
self.prev_filtered_value = s
|
| 25 |
+
return s
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class OneEuroFilter:
|
| 29 |
+
def __init__(self, mincutoff=1.0, beta=0.0, dcutoff=1.0, freq=30):
|
| 30 |
+
self.freq = freq
|
| 31 |
+
self.mincutoff = mincutoff
|
| 32 |
+
self.beta = beta
|
| 33 |
+
self.dcutoff = dcutoff
|
| 34 |
+
self.x_filter = LowPassFilter()
|
| 35 |
+
self.dx_filter = LowPassFilter()
|
| 36 |
+
|
| 37 |
+
def compute_alpha(self, cutoff):
|
| 38 |
+
te = 1.0 / self.freq
|
| 39 |
+
tau = 1.0 / (2 * np.pi * cutoff)
|
| 40 |
+
return 1.0 / (1.0 + tau / te)
|
| 41 |
+
|
| 42 |
+
def process(self, x):
|
| 43 |
+
prev_x = self.x_filter.prev_raw_value
|
| 44 |
+
dx = 0.0 if prev_x is None else (x - prev_x) * self.freq
|
| 45 |
+
edx = self.dx_filter.process(dx, self.compute_alpha(self.dcutoff))
|
| 46 |
+
cutoff = self.mincutoff + self.beta * np.abs(edx)
|
| 47 |
+
return self.x_filter.process(x, self.compute_alpha(cutoff))
|
| 48 |
+
|
FONT/frames_dataset.py
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from skimage import io, img_as_float32, transform
|
| 3 |
+
from skimage.color import gray2rgb
|
| 4 |
+
from sklearn.model_selection import train_test_split
|
| 5 |
+
from imageio import mimread
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
from torch.utils.data import Dataset
|
| 9 |
+
import pandas as pd
|
| 10 |
+
from augmentation import AllAugmentationTransform
|
| 11 |
+
import glob
|
| 12 |
+
import pickle
|
| 13 |
+
import random
|
| 14 |
+
from filter1 import OneEuroFilter
|
| 15 |
+
def read_video(name, frame_shape):
|
| 16 |
+
"""
|
| 17 |
+
Read video which can be:
|
| 18 |
+
- an image of concatenated frames
|
| 19 |
+
- '.mp4' and'.gif'
|
| 20 |
+
- folder with videos
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
if os.path.isdir(name):
|
| 24 |
+
frames = sorted(os.listdir(name))
|
| 25 |
+
num_frames = len(frames)
|
| 26 |
+
video_array = np.array(
|
| 27 |
+
[img_as_float32(io.imread(os.path.join(name, frames[idx]))) for idx in range(num_frames)])
|
| 28 |
+
elif name.lower().endswith('.png') or name.lower().endswith('.jpg'):
|
| 29 |
+
image = io.imread(name)
|
| 30 |
+
|
| 31 |
+
if len(image.shape) == 2 or image.shape[2] == 1:
|
| 32 |
+
image = gray2rgb(image)
|
| 33 |
+
|
| 34 |
+
if image.shape[2] == 4:
|
| 35 |
+
image = image[..., :3]
|
| 36 |
+
|
| 37 |
+
image = img_as_float32(image)
|
| 38 |
+
|
| 39 |
+
video_array = np.moveaxis(image, 1, 0)
|
| 40 |
+
|
| 41 |
+
video_array = video_array.reshape((-1,) + frame_shape)
|
| 42 |
+
video_array = np.moveaxis(video_array, 1, 2)
|
| 43 |
+
elif name.lower().endswith('.gif') or name.lower().endswith('.mp4') or name.lower().endswith('.mov'):
|
| 44 |
+
video = np.array(mimread(name))
|
| 45 |
+
if len(video.shape) == 3:
|
| 46 |
+
video = np.array([gray2rgb(frame) for frame in video])
|
| 47 |
+
if video.shape[-1] == 4:
|
| 48 |
+
video = video[..., :3]
|
| 49 |
+
video_array = img_as_float32(video)
|
| 50 |
+
else:
|
| 51 |
+
raise Exception("Unknown file extensions %s" % name)
|
| 52 |
+
|
| 53 |
+
return video_array
|
| 54 |
+
|
| 55 |
+
def get_list(ipath,base_name):
|
| 56 |
+
#ipath = '/mnt/lustre/share/jixinya/LRW/pose/train_fo/'
|
| 57 |
+
ipath = os.path.join(ipath,base_name)
|
| 58 |
+
name_list = os.listdir(ipath)
|
| 59 |
+
image_path = os.path.join('/mnt/lustre/share/jixinya/LRW/Image/',base_name)
|
| 60 |
+
all = []
|
| 61 |
+
for k in range(len(name_list)):
|
| 62 |
+
name = name_list[k]
|
| 63 |
+
path_ = os.path.join(ipath,name)
|
| 64 |
+
Dir = os.listdir(path_)
|
| 65 |
+
for i in range(len(Dir)):
|
| 66 |
+
word = Dir[i]
|
| 67 |
+
path = os.path.join(path_, word)
|
| 68 |
+
if os.path.exists(os.path.join(image_path,name,word.split('.')[0])):
|
| 69 |
+
all.append(name+'/'+word.split('.')[0])
|
| 70 |
+
#print(k,name,i,word)
|
| 71 |
+
print('get list '+os.path.basename(ipath))
|
| 72 |
+
return all
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class AudioDataset(Dataset):
|
| 76 |
+
"""
|
| 77 |
+
Dataset of videos, each video can be represented as:
|
| 78 |
+
- an image of concatenated frames
|
| 79 |
+
- '.mp4' or '.gif'
|
| 80 |
+
- folder with all frames
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
def __init__(self, name, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 84 |
+
random_seed=0, augmentation_params=None):
|
| 85 |
+
self.root_dir = root_dir
|
| 86 |
+
self.audio_dir = os.path.join(root_dir,'MFCC')
|
| 87 |
+
self.image_dir = os.path.join(root_dir,'Image')
|
| 88 |
+
self.pose_dir = os.path.join(root_dir,'pose')
|
| 89 |
+
# assert len(os.listdir(self.audio_dir)) == len(os.listdir(self.image_dir)), 'audio and image length not equal'
|
| 90 |
+
|
| 91 |
+
# self.videos=np.load('../LRW/list/train_fo.npy')
|
| 92 |
+
# self.videos = os.listdir(self.landmark_dir)
|
| 93 |
+
self.frame_shape = tuple(frame_shape)
|
| 94 |
+
|
| 95 |
+
self.id_sampling = id_sampling
|
| 96 |
+
|
| 97 |
+
if os.path.exists(os.path.join(self.pose_dir, 'train_fo')):
|
| 98 |
+
assert os.path.exists(os.path.join(self.pose_dir, 'test_fo'))
|
| 99 |
+
print("Use predefined train-test split.")
|
| 100 |
+
if id_sampling:
|
| 101 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 102 |
+
os.listdir(os.path.join(self.image_dir, 'train'))}
|
| 103 |
+
train_videos = list(train_videos)
|
| 104 |
+
else:
|
| 105 |
+
train_videos = np.load('../LRW/list/train_fo.npy')# get_list(self.pose_dir, 'train_fo')
|
| 106 |
+
# df=open('../LRW/list/test_fo.txt','rb')
|
| 107 |
+
test_videos=np.load('../LRW/list/test_fo.npy')
|
| 108 |
+
# df.close()
|
| 109 |
+
# test_videos = np.load('../LRW/list/train_fo.npy')
|
| 110 |
+
#get_list(self.pose_dir, 'test_fo')
|
| 111 |
+
# self.root_dir = os.path.join(self.root_dir, 'train' if is_train else 'test')
|
| 112 |
+
|
| 113 |
+
self.image_dir = os.path.join(self.image_dir, 'train_fo' if is_train else 'test_fo')
|
| 114 |
+
self.audio_dir = os.path.join(self.audio_dir, 'train' if is_train else 'test')
|
| 115 |
+
self.pose_dir = os.path.join(self.pose_dir, 'train_fo' if is_train else 'test_fo')
|
| 116 |
+
else:
|
| 117 |
+
print("Use random train-test split.")
|
| 118 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 119 |
+
|
| 120 |
+
if is_train:
|
| 121 |
+
self.videos = train_videos
|
| 122 |
+
else:
|
| 123 |
+
self.videos = test_videos
|
| 124 |
+
|
| 125 |
+
self.is_train = is_train
|
| 126 |
+
|
| 127 |
+
if self.is_train:
|
| 128 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 129 |
+
else:
|
| 130 |
+
self.transform = None
|
| 131 |
+
|
| 132 |
+
def __len__(self):
|
| 133 |
+
return len(self.videos)
|
| 134 |
+
|
| 135 |
+
def __getitem__(self, idx):
|
| 136 |
+
if self.is_train and self.id_sampling:
|
| 137 |
+
name = self.videos[idx].split('.')[0]
|
| 138 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 139 |
+
else:
|
| 140 |
+
name = self.videos[idx].split('.')[0]
|
| 141 |
+
|
| 142 |
+
audio_path = os.path.join(self.audio_dir, name)
|
| 143 |
+
pose_path = os.path.join(self.pose_dir,name)
|
| 144 |
+
path = os.path.join(self.image_dir, name)
|
| 145 |
+
|
| 146 |
+
video_name = os.path.basename(path)
|
| 147 |
+
|
| 148 |
+
if os.path.isdir(path):
|
| 149 |
+
# if self.is_train and os.path.isdir(path):
|
| 150 |
+
|
| 151 |
+
# mfcc loading
|
| 152 |
+
r = random.choice([x for x in range(3, 8)])
|
| 153 |
+
|
| 154 |
+
example_image = img_as_float32(io.imread(os.path.join(path, str(r)+'.png')))
|
| 155 |
+
|
| 156 |
+
mfccs = []
|
| 157 |
+
for ind in range(1, 17):
|
| 158 |
+
# t_mfcc = mfcc[(r + ind - 3) * 4: (r + ind + 4) * 4, 1:]
|
| 159 |
+
t_mfcc = np.load(os.path.join(audio_path,str(r + ind)+'.npy'),allow_pickle=True)[:, 1:]
|
| 160 |
+
mfccs.append(t_mfcc)
|
| 161 |
+
mfccs = np.array(mfccs)
|
| 162 |
+
|
| 163 |
+
poses = []
|
| 164 |
+
video_array = []
|
| 165 |
+
for ind in range(1, 17):
|
| 166 |
+
|
| 167 |
+
t_pose = np.load(os.path.join(self.pose_dir,name+'.npy'))[r+ind,:-1]
|
| 168 |
+
|
| 169 |
+
poses.append(t_pose)
|
| 170 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 171 |
+
video_array.append(image)
|
| 172 |
+
poses = np.array(poses)
|
| 173 |
+
video_array = np.array(video_array)
|
| 174 |
+
|
| 175 |
+
else:
|
| 176 |
+
print('Wrong, data path not an existing file.')
|
| 177 |
+
|
| 178 |
+
if self.transform is not None:
|
| 179 |
+
video_array = self.transform(video_array)
|
| 180 |
+
|
| 181 |
+
out = {}
|
| 182 |
+
|
| 183 |
+
driving = np.array(video_array, dtype='float32')
|
| 184 |
+
spatial_size = np.array(driving.shape[1:3][::-1])[np.newaxis]
|
| 185 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 186 |
+
example_image = np.array(example_image, dtype='float32')
|
| 187 |
+
|
| 188 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 189 |
+
out['driving_pose'] = driving_pose
|
| 190 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 191 |
+
out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 192 |
+
# out['name'] = video_name
|
| 193 |
+
|
| 194 |
+
return out
|
| 195 |
+
|
| 196 |
+
class VoxDataset(Dataset):
|
| 197 |
+
"""
|
| 198 |
+
Dataset of videos, each video can be represented as:
|
| 199 |
+
- an image of concatenated frames
|
| 200 |
+
- '.mp4' or '.gif'
|
| 201 |
+
- folder with all frames
|
| 202 |
+
"""
|
| 203 |
+
|
| 204 |
+
def __init__(self, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 205 |
+
random_seed=0, pairs_list=None, augmentation_params=None):
|
| 206 |
+
self.root_dir = root_dir
|
| 207 |
+
self.audio_dir = os.path.join(root_dir,'MFCC')
|
| 208 |
+
self.image_dir = os.path.join(root_dir,'align_img')
|
| 209 |
+
|
| 210 |
+
self.pose_dir = os.path.join(root_dir,'align_pose')
|
| 211 |
+
# assert len(os.listdir(self.audio_dir)) == len(os.listdir(self.image_dir)), 'audio and image length not equal'
|
| 212 |
+
|
| 213 |
+
|
| 214 |
+
# df=open('../LRW/list/test_fo.txt','rb')
|
| 215 |
+
# self.videos=pickle.load(df)
|
| 216 |
+
# df.close()
|
| 217 |
+
self.videos=np.load('/mnt/lustre/share_data/jixinya/VoxCeleb1_Cut/right.npy')
|
| 218 |
+
# self.videos = os.listdir(self.landmark_dir)
|
| 219 |
+
self.frame_shape = tuple(frame_shape)
|
| 220 |
+
self.pairs_list = pairs_list
|
| 221 |
+
self.id_sampling = id_sampling
|
| 222 |
+
|
| 223 |
+
if os.path.exists(os.path.join(self.pose_dir, 'train_fo')):
|
| 224 |
+
assert os.path.exists(os.path.join(self.pose_dir, 'test_fo'))
|
| 225 |
+
print("Use predefined train-test split.")
|
| 226 |
+
if id_sampling:
|
| 227 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 228 |
+
os.listdir(os.path.join(self.image_dir, 'train'))}
|
| 229 |
+
train_videos = list(train_videos)
|
| 230 |
+
else:
|
| 231 |
+
train_videos = np.load('/mnt/lustre/share_data/jixinya/VoxCeleb1_Cut/right.npy')# get_list(self.pose_dir, 'train_fo')
|
| 232 |
+
|
| 233 |
+
self.image_dir = os.path.join(self.image_dir, 'train_fo' if is_train else 'test_fo')
|
| 234 |
+
self.audio_dir = os.path.join(self.audio_dir, 'train' if is_train else 'test')
|
| 235 |
+
self.pose_dir = os.path.join(self.pose_dir, 'train_fo' if is_train else 'test_fo')
|
| 236 |
+
else:
|
| 237 |
+
print("Use random train-test split.")
|
| 238 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 239 |
+
|
| 240 |
+
if is_train:
|
| 241 |
+
self.videos = train_videos
|
| 242 |
+
else:
|
| 243 |
+
self.videos = test_videos
|
| 244 |
+
|
| 245 |
+
self.is_train = is_train
|
| 246 |
+
|
| 247 |
+
if self.is_train:
|
| 248 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 249 |
+
else:
|
| 250 |
+
self.transform = None
|
| 251 |
+
|
| 252 |
+
def __len__(self):
|
| 253 |
+
return len(self.videos)
|
| 254 |
+
|
| 255 |
+
def __getitem__(self, idx):
|
| 256 |
+
if self.is_train and self.id_sampling:
|
| 257 |
+
name = self.videos[idx].split('.')[0]
|
| 258 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 259 |
+
else:
|
| 260 |
+
name = self.videos[idx].split('.')[0]
|
| 261 |
+
|
| 262 |
+
audio_path = os.path.join(self.audio_dir, name+'.npy')
|
| 263 |
+
pose_path = os.path.join(self.pose_dir,name+'.npy')
|
| 264 |
+
path = os.path.join(self.image_dir, name)
|
| 265 |
+
|
| 266 |
+
video_name = os.path.basename(path)
|
| 267 |
+
|
| 268 |
+
if os.path.isdir(path):
|
| 269 |
+
# if self.is_train and os.path.isdir(path):
|
| 270 |
+
frames = os.listdir(path)
|
| 271 |
+
num_frames = len(frames)
|
| 272 |
+
frame_idx = np.sort(np.random.choice(num_frames, replace=True, size=2))
|
| 273 |
+
video_array = [img_as_float32(io.imread(os.path.join(path, frames[idx]))) for idx in frame_idx]
|
| 274 |
+
mfcc = np.load(audio_path)
|
| 275 |
+
pose = np.load(pose_path)
|
| 276 |
+
|
| 277 |
+
# print(audio_path,pose_path,len(mfcc))
|
| 278 |
+
|
| 279 |
+
try:
|
| 280 |
+
len(mfcc) > 16
|
| 281 |
+
except:
|
| 282 |
+
print('wrongmfcc len:',audio_path)
|
| 283 |
+
if 16 < len(mfcc) < 24 :
|
| 284 |
+
r = 0
|
| 285 |
+
else:
|
| 286 |
+
|
| 287 |
+
r = random.choice([x for x in range(3, len(mfcc)-20)])
|
| 288 |
+
|
| 289 |
+
mfccs = []
|
| 290 |
+
poses = []
|
| 291 |
+
video_array = []
|
| 292 |
+
for ind in range(1, 17):
|
| 293 |
+
t_mfcc = mfcc[r+ind][:, 1:]
|
| 294 |
+
mfccs.append(t_mfcc)
|
| 295 |
+
t_pose = pose[r+ind,:-1]
|
| 296 |
+
poses.append(t_pose)
|
| 297 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 298 |
+
video_array.append(image)
|
| 299 |
+
mfccs = np.array(mfccs)
|
| 300 |
+
poses = np.array(poses)
|
| 301 |
+
video_array = np.array(video_array)
|
| 302 |
+
|
| 303 |
+
example_image = img_as_float32(io.imread(os.path.join(path, str(r)+'.png')))
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
else:
|
| 307 |
+
print('Wrong, data path not an existing file.')
|
| 308 |
+
|
| 309 |
+
if self.transform is not None:
|
| 310 |
+
video_array = self.transform(video_array)
|
| 311 |
+
|
| 312 |
+
out = {}
|
| 313 |
+
|
| 314 |
+
driving = np.array(video_array, dtype='float32')
|
| 315 |
+
|
| 316 |
+
spatial_size = np.array(driving.shape[1:3][::-1])[np.newaxis]
|
| 317 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 318 |
+
example_image = np.array(example_image, dtype='float32')
|
| 319 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 320 |
+
out['driving_pose'] = driving_pose
|
| 321 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 322 |
+
|
| 323 |
+
out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 324 |
+
# out['name'] = video_name
|
| 325 |
+
|
| 326 |
+
return out
|
| 327 |
+
|
| 328 |
+
class MeadDataset(Dataset):
|
| 329 |
+
"""
|
| 330 |
+
Dataset of videos, each video can be represented as:
|
| 331 |
+
- an image of concatenated frames
|
| 332 |
+
- '.mp4' or '.gif'
|
| 333 |
+
- folder with all frames
|
| 334 |
+
"""
|
| 335 |
+
|
| 336 |
+
def __init__(self, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 337 |
+
random_seed=0, augmentation_params=None):
|
| 338 |
+
self.root_dir = root_dir
|
| 339 |
+
|
| 340 |
+
self.audio_dir = os.path.join(root_dir,'MEAD_MFCC')
|
| 341 |
+
self.image_dir = os.path.join(root_dir,'MEAD_fomm_crop')
|
| 342 |
+
|
| 343 |
+
self.pose_dir = os.path.join(root_dir,'MEAD_fomm_pose_crop')
|
| 344 |
+
|
| 345 |
+
self.videos = np.load('/mnt/lustre/share_data/jixinya/MEAD/MEAD_fomm_audio_less_crop.npy')
|
| 346 |
+
self.dict = np.load('/mnt/lustre/share_data/jixinya/MEAD/MEAD_fomm_neu_dic_crop.npy',allow_pickle=True).item()
|
| 347 |
+
# self.videos = os.listdir(root_dir)
|
| 348 |
+
self.frame_shape = tuple(frame_shape)
|
| 349 |
+
|
| 350 |
+
self.id_sampling = id_sampling
|
| 351 |
+
if os.path.exists(os.path.join(root_dir, 'train')):
|
| 352 |
+
assert os.path.exists(os.path.join(root_dir, 'test'))
|
| 353 |
+
print("Use predefined train-test split.")
|
| 354 |
+
if id_sampling:
|
| 355 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 356 |
+
os.listdir(os.path.join(root_dir, 'train'))}
|
| 357 |
+
train_videos = list(train_videos)
|
| 358 |
+
else:
|
| 359 |
+
train_videos = os.listdir(os.path.join(root_dir, 'train'))
|
| 360 |
+
test_videos = os.listdir(os.path.join(root_dir, 'test'))
|
| 361 |
+
self.root_dir = os.path.join(self.root_dir, 'train' if is_train else 'test')
|
| 362 |
+
else:
|
| 363 |
+
print("Use random train-test split.")
|
| 364 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 365 |
+
|
| 366 |
+
if is_train:
|
| 367 |
+
self.videos = train_videos
|
| 368 |
+
else:
|
| 369 |
+
self.videos = test_videos
|
| 370 |
+
|
| 371 |
+
self.is_train = is_train
|
| 372 |
+
|
| 373 |
+
if self.is_train:
|
| 374 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 375 |
+
else:
|
| 376 |
+
self.transform = None
|
| 377 |
+
|
| 378 |
+
def __len__(self):
|
| 379 |
+
return len(self.videos)
|
| 380 |
+
|
| 381 |
+
def __getitem__(self, idx):
|
| 382 |
+
if self.is_train and self.id_sampling:
|
| 383 |
+
name = self.videos[idx]
|
| 384 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 385 |
+
else:
|
| 386 |
+
name = self.videos[idx]
|
| 387 |
+
path = os.path.join(self.image_dir, name)
|
| 388 |
+
|
| 389 |
+
video_name = os.path.basename(path)
|
| 390 |
+
id_name = path.split('/')[-2]
|
| 391 |
+
neu_list = self.dict[id_name]
|
| 392 |
+
neu_path = os.path.join(self.image_dir, np.random.choice(neu_list))
|
| 393 |
+
|
| 394 |
+
audio_path = os.path.join(self.audio_dir, name+'.npy')
|
| 395 |
+
pose_path = os.path.join(self.pose_dir,name+'.npy')
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
if self.is_train and os.path.isdir(path):
|
| 399 |
+
|
| 400 |
+
mfcc = np.load(audio_path)
|
| 401 |
+
pose_raw = np.load(pose_path)
|
| 402 |
+
one_euro_filter = OneEuroFilter(mincutoff=0.01, beta=0.7, dcutoff=1.0, freq=100)
|
| 403 |
+
pose = np.zeros((len(pose_raw),7))
|
| 404 |
+
|
| 405 |
+
for j in range(len(pose_raw)):
|
| 406 |
+
pose[j]=one_euro_filter.process(pose_raw[j])
|
| 407 |
+
# print(audio_path,pose_path,len(mfcc))
|
| 408 |
+
|
| 409 |
+
neu_frames = os.listdir(neu_path)
|
| 410 |
+
num_neu_frames = len(neu_frames)
|
| 411 |
+
frame_idx = np.random.choice(num_neu_frames)
|
| 412 |
+
example_image = img_as_float32(io.imread(os.path.join(neu_path, neu_frames[frame_idx])))
|
| 413 |
+
try:
|
| 414 |
+
len(mfcc) > 16
|
| 415 |
+
except:
|
| 416 |
+
print('wrongmfcc len:',audio_path)
|
| 417 |
+
if 16 < len(mfcc) < 24 :
|
| 418 |
+
r = 0
|
| 419 |
+
else:
|
| 420 |
+
|
| 421 |
+
r = random.choice([x for x in range(3, len(mfcc)-20)])
|
| 422 |
+
|
| 423 |
+
mfccs = []
|
| 424 |
+
poses = []
|
| 425 |
+
video_array = []
|
| 426 |
+
for ind in range(1, 17):
|
| 427 |
+
t_mfcc = mfcc[r+ind][:, 1:]
|
| 428 |
+
mfccs.append(t_mfcc)
|
| 429 |
+
t_pose = pose[r+ind,:-1]
|
| 430 |
+
poses.append(t_pose)
|
| 431 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 432 |
+
video_array.append(image)
|
| 433 |
+
mfccs = np.array(mfccs)
|
| 434 |
+
poses = np.array(poses)
|
| 435 |
+
video_array = np.array(video_array)
|
| 436 |
+
|
| 437 |
+
else:
|
| 438 |
+
print('Wrong, data path not an existing file.')
|
| 439 |
+
|
| 440 |
+
# if self.transform is not None:
|
| 441 |
+
# video_array = self.transform(video_array)
|
| 442 |
+
|
| 443 |
+
out = {}
|
| 444 |
+
if self.is_train:
|
| 445 |
+
|
| 446 |
+
driving = np.array(video_array, dtype='float32')
|
| 447 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 448 |
+
example_image = np.array(example_image, dtype='float32')
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 452 |
+
out['driving_pose'] = driving_pose
|
| 453 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 454 |
+
out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 455 |
+
|
| 456 |
+
# out['name'] = id_name+'/'+video_name
|
| 457 |
+
|
| 458 |
+
return out
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
class DatasetRepeater(Dataset):
|
| 462 |
+
"""
|
| 463 |
+
Pass several times over the same dataset for better i/o performance
|
| 464 |
+
"""
|
| 465 |
+
|
| 466 |
+
def __init__(self, dataset, num_repeats=100):
|
| 467 |
+
self.dataset = dataset
|
| 468 |
+
# self.dataset2 = dataset2
|
| 469 |
+
self.num_repeats = num_repeats
|
| 470 |
+
|
| 471 |
+
def __len__(self):
|
| 472 |
+
return self.num_repeats * self.dataset.__len__()
|
| 473 |
+
|
| 474 |
+
def __getitem__(self, idx):
|
| 475 |
+
# if idx % 5 == 0:
|
| 476 |
+
# return self.dataset2[idx % self.dataset2.__len__()]#% self.dataset.__len__()
|
| 477 |
+
# else:
|
| 478 |
+
# return self.dataset[idx % self.dataset.__len__()]
|
| 479 |
+
return self.dataset[idx % self.dataset.__len__()]
|
| 480 |
+
|
| 481 |
+
class TestsetRepeater(Dataset):
|
| 482 |
+
"""
|
| 483 |
+
Pass several times over the same dataset for better i/o performance
|
| 484 |
+
"""
|
| 485 |
+
|
| 486 |
+
def __init__(self, dataset, num_repeats=100):
|
| 487 |
+
self.dataset = dataset
|
| 488 |
+
|
| 489 |
+
self.num_repeats = num_repeats
|
| 490 |
+
|
| 491 |
+
def __len__(self):
|
| 492 |
+
return self.num_repeats * self.dataset.__len__()
|
| 493 |
+
|
| 494 |
+
def __getitem__(self, idx):
|
| 495 |
+
|
| 496 |
+
return self.dataset[idx % self.dataset.__len__()]#% self.dataset.__len__()
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
class PairedDataset(Dataset):
|
| 500 |
+
"""
|
| 501 |
+
Dataset of pairs for animation.
|
| 502 |
+
"""
|
| 503 |
+
|
| 504 |
+
def __init__(self, initial_dataset, number_of_pairs, seed=0):
|
| 505 |
+
self.initial_dataset = initial_dataset
|
| 506 |
+
pairs_list = self.initial_dataset.pairs_list
|
| 507 |
+
|
| 508 |
+
np.random.seed(seed)
|
| 509 |
+
|
| 510 |
+
if pairs_list is None:
|
| 511 |
+
max_idx = min(number_of_pairs, len(initial_dataset))
|
| 512 |
+
nx, ny = max_idx, max_idx
|
| 513 |
+
xy = np.mgrid[:nx, :ny].reshape(2, -1).T
|
| 514 |
+
number_of_pairs = min(xy.shape[0], number_of_pairs)
|
| 515 |
+
self.pairs = xy.take(np.random.choice(xy.shape[0], number_of_pairs, replace=False), axis=0)
|
| 516 |
+
else:
|
| 517 |
+
videos = self.initial_dataset.videos
|
| 518 |
+
name_to_index = {name: index for index, name in enumerate(videos)}
|
| 519 |
+
pairs = pd.read_csv(pairs_list)
|
| 520 |
+
pairs = pairs[np.logical_and(pairs['source'].isin(videos), pairs['driving'].isin(videos))]
|
| 521 |
+
|
| 522 |
+
number_of_pairs = min(pairs.shape[0], number_of_pairs)
|
| 523 |
+
self.pairs = []
|
| 524 |
+
self.start_frames = []
|
| 525 |
+
for ind in range(number_of_pairs):
|
| 526 |
+
self.pairs.append(
|
| 527 |
+
(name_to_index[pairs['driving'].iloc[ind]], name_to_index[pairs['source'].iloc[ind]]))
|
| 528 |
+
|
| 529 |
+
def __len__(self):
|
| 530 |
+
return len(self.pairs)
|
| 531 |
+
|
| 532 |
+
def __getitem__(self, idx):
|
| 533 |
+
pair = self.pairs[idx]
|
| 534 |
+
first = self.initial_dataset[pair[0]]
|
| 535 |
+
second = self.initial_dataset[pair[1]]
|
| 536 |
+
first = {'driving_' + key: value for key, value in first.items()}
|
| 537 |
+
second = {'source_' + key: value for key, value in second.items()}
|
| 538 |
+
|
| 539 |
+
return {**first, **second}
|
FONT/frames_dataset_liujin.py
ADDED
|
@@ -0,0 +1,727 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from skimage import io, img_as_float32, transform
|
| 3 |
+
from skimage.color import gray2rgb
|
| 4 |
+
from sklearn.model_selection import train_test_split
|
| 5 |
+
from imageio import mimread
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
from torch.utils.data import Dataset
|
| 9 |
+
import pandas as pd
|
| 10 |
+
from augmentation import AllAugmentationTransform
|
| 11 |
+
import glob
|
| 12 |
+
import pickle
|
| 13 |
+
import random
|
| 14 |
+
from filter1 import OneEuroFilter
|
| 15 |
+
import pickle
|
| 16 |
+
from tqdm import tqdm
|
| 17 |
+
from scipy.io import loadmat
|
| 18 |
+
import time
|
| 19 |
+
import random
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def read_video(name, frame_shape):
|
| 24 |
+
"""
|
| 25 |
+
Read video which can be:
|
| 26 |
+
- an image of concatenated frames
|
| 27 |
+
- '.mp4' and'.gif'
|
| 28 |
+
- folder with videos
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
if os.path.isdir(name):
|
| 32 |
+
frames = sorted(os.listdir(name))
|
| 33 |
+
num_frames = len(frames)
|
| 34 |
+
video_array = np.array(
|
| 35 |
+
[img_as_float32(io.imread(os.path.join(name, frames[idx]))) for idx in range(num_frames)])
|
| 36 |
+
elif name.lower().endswith('.png') or name.lower().endswith('.jpg'):
|
| 37 |
+
image = io.imread(name)
|
| 38 |
+
|
| 39 |
+
if len(image.shape) == 2 or image.shape[2] == 1:
|
| 40 |
+
image = gray2rgb(image)
|
| 41 |
+
|
| 42 |
+
if image.shape[2] == 4:
|
| 43 |
+
image = image[..., :3]
|
| 44 |
+
|
| 45 |
+
image = img_as_float32(image)
|
| 46 |
+
|
| 47 |
+
video_array = np.moveaxis(image, 1, 0)
|
| 48 |
+
|
| 49 |
+
video_array = video_array.reshape((-1,) + frame_shape)
|
| 50 |
+
video_array = np.moveaxis(video_array, 1, 2)
|
| 51 |
+
elif name.lower().endswith('.gif') or name.lower().endswith('.mp4') or name.lower().endswith('.mov'):
|
| 52 |
+
video = np.array(mimread(name))
|
| 53 |
+
if len(video.shape) == 3:
|
| 54 |
+
video = np.array([gray2rgb(frame) for frame in video])
|
| 55 |
+
if video.shape[-1] == 4:
|
| 56 |
+
video = video[..., :3]
|
| 57 |
+
video_array = img_as_float32(video)
|
| 58 |
+
else:
|
| 59 |
+
raise Exception("Unknown file extensions %s" % name)
|
| 60 |
+
|
| 61 |
+
return video_array
|
| 62 |
+
|
| 63 |
+
def get_list(ipath,base_name):
|
| 64 |
+
#ipath = '/mnt/lustre/share/jixinya/LRW/pose/train_fo/'
|
| 65 |
+
ipath = os.path.join(ipath,base_name)
|
| 66 |
+
name_list = os.listdir(ipath)
|
| 67 |
+
image_path = os.path.join('/mnt/lustre/share/jixinya/LRW/Image/',base_name)
|
| 68 |
+
all = []
|
| 69 |
+
for k in range(len(name_list)):
|
| 70 |
+
name = name_list[k]
|
| 71 |
+
path_ = os.path.join(ipath,name)
|
| 72 |
+
Dir = os.listdir(path_)
|
| 73 |
+
for i in range(len(Dir)):
|
| 74 |
+
word = Dir[i]
|
| 75 |
+
path = os.path.join(path_, word)
|
| 76 |
+
if os.path.exists(os.path.join(image_path,name,word.split('.')[0])):
|
| 77 |
+
all.append(name+'/'+word.split('.')[0])
|
| 78 |
+
#print(k,name,i,word)
|
| 79 |
+
print('get list '+os.path.basename(ipath))
|
| 80 |
+
return all
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class AudioDataset(Dataset):
|
| 84 |
+
"""
|
| 85 |
+
Dataset of videos, each video can be represented as:
|
| 86 |
+
- an image of concatenated frames
|
| 87 |
+
- '.mp4' or '.gif'
|
| 88 |
+
- folder with all frames
|
| 89 |
+
"""
|
| 90 |
+
|
| 91 |
+
def __init__(self, name, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 92 |
+
random_seed=0, augmentation_params=None):
|
| 93 |
+
|
| 94 |
+
self.root_dir = "/data/liujin/dataset/LRW/"
|
| 95 |
+
# self.audio_dir = os.path.join(root_dir,'lipread_atvg_audio_feature')
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
self.frame_shape = tuple(frame_shape)
|
| 99 |
+
|
| 100 |
+
self.is_train = is_train
|
| 101 |
+
self.split = 'train' if is_train else 'test'
|
| 102 |
+
|
| 103 |
+
self.used_video_frames_paths = self.get_used_frames_paths()
|
| 104 |
+
|
| 105 |
+
self.videos = self.get_video_items(self.used_video_frames_paths)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
print('length of LRW {} dataset is {}'.format(self.split, str(len(self.videos))) )
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
if self.is_train:
|
| 112 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 113 |
+
else:
|
| 114 |
+
self.transform = None
|
| 115 |
+
|
| 116 |
+
def get_video_items(self, all_video_frames_paths):
|
| 117 |
+
print('total video items length is {}'.format(len(all_video_frames_paths)))
|
| 118 |
+
video_items = []
|
| 119 |
+
cur_lengh = 0
|
| 120 |
+
init_start_time = time.time()
|
| 121 |
+
|
| 122 |
+
for video_frames_path in tqdm(all_video_frames_paths):
|
| 123 |
+
video_items.append(self.construct_Video_Info(video_frames_path))
|
| 124 |
+
if len(video_items) - cur_lengh > 5000:
|
| 125 |
+
init_end_time = time.time()
|
| 126 |
+
|
| 127 |
+
print(len(video_items))
|
| 128 |
+
print('Cost {} seconds'.format(str(init_end_time - init_start_time)))
|
| 129 |
+
cur_lengh = len(video_items)
|
| 130 |
+
init_start_time = init_end_time
|
| 131 |
+
return video_items
|
| 132 |
+
|
| 133 |
+
def get_used_frames_paths(self):
|
| 134 |
+
used_video_frames_paths = []
|
| 135 |
+
# used_words = ['ABOUT', 'ABSOLUTELY', 'ABUSE', 'ACCESS', 'ACCORDING',
|
| 136 |
+
# 'COUNCIL', 'COUNTRIES', 'COUNTRY', 'COUPLE', 'COURSE',
|
| 137 |
+
# 'HISTORY', 'HOMES', 'HOSPITAL', 'HOURS', 'HOUSE',
|
| 138 |
+
# 'OPPOSITION', 'ORDER', 'OTHER', 'OTHERS', 'OUTSIDE',
|
| 139 |
+
# 'SOMETHING', 'SOUTH', 'SOUTHERN', 'SPEAKING']
|
| 140 |
+
|
| 141 |
+
lrw_word_dir = "/data/liujin/dataset/LRW/lipread_frames/"
|
| 142 |
+
all_words = sorted(os.listdir(lrw_word_dir))
|
| 143 |
+
# used_words = all_words[0:500:5]
|
| 144 |
+
|
| 145 |
+
used_words = sorted(os.listdir("/data/liujin/dataset/LRW/lipread_3DDFA_pose/"))
|
| 146 |
+
|
| 147 |
+
for word in (used_words):
|
| 148 |
+
cur_videos_path = os.path.join('/data/liujin/dataset/LRW/lipread_frames/', word, self.split)
|
| 149 |
+
length = len(os.listdir(cur_videos_path))
|
| 150 |
+
for video_name in sorted(os.listdir(cur_videos_path))[:int(0.05 * length)]:
|
| 151 |
+
used_video_frames_paths.append(os.path.join(cur_videos_path, video_name))
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
return used_video_frames_paths[:]
|
| 155 |
+
|
| 156 |
+
def construct_Video_Info(self, cur_frames_path):
|
| 157 |
+
info = {}
|
| 158 |
+
|
| 159 |
+
cor_mfcc_path = cur_frames_path.replace('lipread_frames', 'lipread_atvg_audio_feature') + '.pkl'
|
| 160 |
+
with open(cor_mfcc_path, 'rb') as fp:
|
| 161 |
+
info['mfccs'] = pickle.load(fp) #N,28,12 ATVG / N,80,16,wav2lip
|
| 162 |
+
|
| 163 |
+
# cor_pose_path = cur_frames_path.replace('lipread_frames', 'lipread_3dmm')
|
| 164 |
+
cor_pose_path = cur_frames_path.replace('lipread_frames', 'lipread_3DDFA_pose')
|
| 165 |
+
cur_video_pose = []
|
| 166 |
+
for pose_name in sorted(os.listdir(cor_pose_path)):
|
| 167 |
+
frame_pose_path = os.path.join(cor_pose_path, pose_name)
|
| 168 |
+
cur_pose_data = None
|
| 169 |
+
if frame_pose_path.endswith('npy'):
|
| 170 |
+
cur_pose_data = np.load(frame_pose_path)
|
| 171 |
+
elif frame_pose_path.endswith('mat'):
|
| 172 |
+
file_mat = loadmat(frame_pose_path)
|
| 173 |
+
coeff_3dmm = file_mat['coeff']
|
| 174 |
+
angles = coeff_3dmm[:, 224:227] # euler angles for pose
|
| 175 |
+
translation = coeff_3dmm[:, 254:257] # translation
|
| 176 |
+
cur_pose_data = np.concatenate([angles, translation], 1)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
cur_video_pose.append(cur_pose_data)
|
| 180 |
+
cur_video_pose_array = np.concatenate(cur_video_pose, axis=0) #N,7
|
| 181 |
+
info['poses'] = cur_video_pose_array
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
frames = []
|
| 185 |
+
for frame_name in sorted(os.listdir(cur_frames_path)):
|
| 186 |
+
cur_frame_path = os.path.join(cur_frames_path, frame_name)
|
| 187 |
+
# frames.append(img_as_float32(io.imread(cur_frame_path)))
|
| 188 |
+
frames.append(cur_frame_path)
|
| 189 |
+
info['frames'] = frames
|
| 190 |
+
|
| 191 |
+
return info
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def __len__(self):
|
| 195 |
+
return len(self.videos)
|
| 196 |
+
|
| 197 |
+
def __getitem__(self, idx):
|
| 198 |
+
|
| 199 |
+
video_info = self.videos[idx]
|
| 200 |
+
# mfcc loading
|
| 201 |
+
r = random.choice([x for x in range(3, 8)])
|
| 202 |
+
|
| 203 |
+
example_image = video_info['frames'][r]
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
mfccs = video_info['mfccs'][r:r+16]
|
| 207 |
+
|
| 208 |
+
poses = []
|
| 209 |
+
video_array = []
|
| 210 |
+
for ind in range(1, 17):
|
| 211 |
+
|
| 212 |
+
t_pose = video_info['poses'][r+ind,:6]
|
| 213 |
+
poses.append(t_pose)
|
| 214 |
+
|
| 215 |
+
image = video_info['frames'][r + ind]
|
| 216 |
+
image_data = img_as_float32(io.imread(image))
|
| 217 |
+
video_array.append(image_data)
|
| 218 |
+
poses = np.array(poses)
|
| 219 |
+
video_array = np.array(video_array)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
# if self.transform is not None:
|
| 224 |
+
# video_array = self.transform(video_array)
|
| 225 |
+
|
| 226 |
+
out = {}
|
| 227 |
+
|
| 228 |
+
driving = np.array(video_array, dtype='float32')
|
| 229 |
+
spatial_size = np.array(driving.shape[1:3][::-1])[np.newaxis]
|
| 230 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 231 |
+
|
| 232 |
+
example_image_data = img_as_float32(io.imread(example_image))
|
| 233 |
+
example_image = np.array(example_image_data, dtype='float32')
|
| 234 |
+
|
| 235 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 236 |
+
out['driving_pose'] = driving_pose
|
| 237 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 238 |
+
# out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 239 |
+
# out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 240 |
+
out['driving_audio'] = mfccs
|
| 241 |
+
# out['name'] = video_name
|
| 242 |
+
out['transformed_driving'] = out['driving']
|
| 243 |
+
|
| 244 |
+
return out
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
class HDTFDataset(Dataset):
|
| 248 |
+
"""
|
| 249 |
+
Dataset of videos, each video can be represented as:
|
| 250 |
+
- an image of concatenated frames
|
| 251 |
+
- '.mp4' or '.gif'
|
| 252 |
+
- folder with all frames
|
| 253 |
+
"""
|
| 254 |
+
|
| 255 |
+
def __init__(self, name, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 256 |
+
random_seed=0, augmentation_params=None):
|
| 257 |
+
|
| 258 |
+
self.root_dir = "/data/liujin/dataset/HDTF/"
|
| 259 |
+
# self.audio_dir = os.path.join(root_dir,'lipread_atvg_audio_feature')
|
| 260 |
+
|
| 261 |
+
self.frame_shape = tuple(frame_shape)
|
| 262 |
+
|
| 263 |
+
self.is_train = is_train
|
| 264 |
+
self.split = 'train' if is_train else 'test'
|
| 265 |
+
|
| 266 |
+
self.used_video_frames_paths = self.get_used_frames_paths()
|
| 267 |
+
|
| 268 |
+
self.videos = self.get_video_items(self.used_video_frames_paths)
|
| 269 |
+
|
| 270 |
+
print('length of HDTF {} dataset is {}'.format(self.split, str(len(self.videos))))
|
| 271 |
+
|
| 272 |
+
if self.is_train:
|
| 273 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 274 |
+
else:
|
| 275 |
+
self.transform = None
|
| 276 |
+
|
| 277 |
+
def get_video_items(self, all_video_frames_paths):
|
| 278 |
+
print('total video items length is {}'.format(len(all_video_frames_paths)))
|
| 279 |
+
video_items = []
|
| 280 |
+
cur_lengh = 0
|
| 281 |
+
init_start_time = time.time()
|
| 282 |
+
|
| 283 |
+
for video_frames_path in tqdm(all_video_frames_paths):
|
| 284 |
+
video_items.append(self.construct_Video_Info(video_frames_path))
|
| 285 |
+
if len(video_items) - cur_lengh > 5000:
|
| 286 |
+
init_end_time = time.time()
|
| 287 |
+
|
| 288 |
+
print(len(video_items))
|
| 289 |
+
print('Cost {} seconds'.format(str(init_end_time - init_start_time)))
|
| 290 |
+
cur_lengh = len(video_items)
|
| 291 |
+
init_start_time = init_end_time
|
| 292 |
+
return video_items
|
| 293 |
+
|
| 294 |
+
def get_used_frames_paths(self):
|
| 295 |
+
|
| 296 |
+
|
| 297 |
+
video_frames_dir = os.path.join(self.root_dir, 'frames_256')
|
| 298 |
+
video_names = sorted(os.listdir(video_frames_dir))
|
| 299 |
+
length = len(video_names)
|
| 300 |
+
used_video_names = video_names[:int(0.9 * length)] if self.is_train else video_names[int(0.9 * length):]
|
| 301 |
+
|
| 302 |
+
used_frames_paths = [os.path.join(video_frames_dir, video_name) for video_name in used_video_names]
|
| 303 |
+
|
| 304 |
+
return used_frames_paths
|
| 305 |
+
|
| 306 |
+
def construct_Video_Info(self, cur_frames_path):
|
| 307 |
+
info = {}
|
| 308 |
+
|
| 309 |
+
cor_mfcc_path = cur_frames_path.replace('frames_256', 'atvg_audio_feature') + '.pkl'
|
| 310 |
+
with open(cor_mfcc_path, 'rb') as fp:
|
| 311 |
+
info['mfccs'] = pickle.load(fp) # N,28,12 ATVG / N,80,16,wav2lip
|
| 312 |
+
|
| 313 |
+
# cor_pose_path = cur_frames_path.replace('frames_256', '3dmm_256')
|
| 314 |
+
cor_pose_path = cur_frames_path.replace('frames_256', 'pose_3DDFA_256')
|
| 315 |
+
cur_video_pose = []
|
| 316 |
+
for pose_name in sorted(os.listdir(cor_pose_path)):
|
| 317 |
+
frame_pose_path = os.path.join(cor_pose_path, pose_name)
|
| 318 |
+
cur_pose_data = None
|
| 319 |
+
if frame_pose_path.endswith('npy'):
|
| 320 |
+
cur_pose_data = np.load(frame_pose_path)
|
| 321 |
+
elif frame_pose_path.endswith('mat'):
|
| 322 |
+
file_mat = loadmat(frame_pose_path)
|
| 323 |
+
coeff_3dmm = file_mat['coeff']
|
| 324 |
+
angles = coeff_3dmm[:, 224:227] # euler angles for pose
|
| 325 |
+
translation = coeff_3dmm[:, 254:257] # translation
|
| 326 |
+
cur_pose_data = np.concatenate([angles, translation], 1)
|
| 327 |
+
|
| 328 |
+
cur_video_pose.append(cur_pose_data)
|
| 329 |
+
cur_video_pose_array = np.concatenate(cur_video_pose, axis=0) # N,7
|
| 330 |
+
info['poses'] = cur_video_pose_array
|
| 331 |
+
|
| 332 |
+
frames = []
|
| 333 |
+
for frame_name in sorted(os.listdir(cur_frames_path)):
|
| 334 |
+
cur_frame_path = os.path.join(cur_frames_path, frame_name)
|
| 335 |
+
# frames.append(img_as_float32(io.imread(cur_frame_path)))
|
| 336 |
+
frames.append(cur_frame_path)
|
| 337 |
+
info['frames'] = frames
|
| 338 |
+
|
| 339 |
+
return info
|
| 340 |
+
|
| 341 |
+
def __len__(self):
|
| 342 |
+
return len(self.videos)
|
| 343 |
+
|
| 344 |
+
def __getitem__(self, idx):
|
| 345 |
+
|
| 346 |
+
video_info = self.videos[idx]
|
| 347 |
+
# mfcc loading
|
| 348 |
+
r = random.choice([x for x in range(3, 8)])
|
| 349 |
+
|
| 350 |
+
example_image = video_info['frames'][r]
|
| 351 |
+
|
| 352 |
+
mfccs = video_info['mfccs'][r:r + 16]
|
| 353 |
+
|
| 354 |
+
poses = []
|
| 355 |
+
video_array = []
|
| 356 |
+
for ind in range(1, 17):
|
| 357 |
+
t_pose = video_info['poses'][r + ind, :6]
|
| 358 |
+
poses.append(t_pose)
|
| 359 |
+
|
| 360 |
+
image = video_info['frames'][r + ind]
|
| 361 |
+
image_data = img_as_float32(io.imread(image))
|
| 362 |
+
video_array.append(image_data)
|
| 363 |
+
poses = np.array(poses)
|
| 364 |
+
video_array = np.array(video_array)
|
| 365 |
+
|
| 366 |
+
# if self.transform is not None:
|
| 367 |
+
# video_array = self.transform(video_array)
|
| 368 |
+
|
| 369 |
+
out = {}
|
| 370 |
+
|
| 371 |
+
driving = np.array(video_array, dtype='float32')
|
| 372 |
+
spatial_size = np.array(driving.shape[1:3][::-1])[np.newaxis]
|
| 373 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 374 |
+
|
| 375 |
+
example_image_data = img_as_float32(io.imread(example_image))
|
| 376 |
+
example_image = np.array(example_image_data, dtype='float32')
|
| 377 |
+
|
| 378 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 379 |
+
out['driving_pose'] = driving_pose
|
| 380 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 381 |
+
# out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 382 |
+
# out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 383 |
+
out['driving_audio'] = mfccs
|
| 384 |
+
# out['name'] = video_name
|
| 385 |
+
out['transformed_driving'] = out['driving']
|
| 386 |
+
|
| 387 |
+
return out
|
| 388 |
+
|
| 389 |
+
class VoxDataset(Dataset):
|
| 390 |
+
"""
|
| 391 |
+
Dataset of videos, each video can be represented as:
|
| 392 |
+
- an image of concatenated frames
|
| 393 |
+
- '.mp4' or '.gif'
|
| 394 |
+
- folder with all frames
|
| 395 |
+
"""
|
| 396 |
+
|
| 397 |
+
def __init__(self, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 398 |
+
random_seed=0, pairs_list=None, augmentation_params=None):
|
| 399 |
+
self.root_dir = root_dir
|
| 400 |
+
self.audio_dir = os.path.join(root_dir,'MFCC')
|
| 401 |
+
self.image_dir = os.path.join(root_dir,'align_img')
|
| 402 |
+
|
| 403 |
+
self.pose_dir = os.path.join(root_dir,'align_pose')
|
| 404 |
+
# assert len(os.listdir(self.audio_dir)) == len(os.listdir(self.image_dir)), 'audio and image length not equal'
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
self.frame_shape = tuple(frame_shape)
|
| 408 |
+
self.pairs_list = pairs_list
|
| 409 |
+
self.id_sampling = id_sampling
|
| 410 |
+
|
| 411 |
+
if os.path.exists(os.path.join(self.pose_dir, 'train_fo')):
|
| 412 |
+
assert os.path.exists(os.path.join(self.pose_dir, 'test_fo'))
|
| 413 |
+
print("Use predefined train-test split.")
|
| 414 |
+
if id_sampling:
|
| 415 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 416 |
+
os.listdir(os.path.join(self.image_dir, 'train'))}
|
| 417 |
+
train_videos = list(train_videos)
|
| 418 |
+
else:
|
| 419 |
+
train_videos = np.load('/mnt/lustre/share_data/jixinya/VoxCeleb1_Cut/right.npy')# get_list(self.pose_dir, 'train_fo')
|
| 420 |
+
|
| 421 |
+
self.image_dir = os.path.join(self.image_dir, 'train_fo' if is_train else 'test_fo')
|
| 422 |
+
self.audio_dir = os.path.join(self.audio_dir, 'train' if is_train else 'test')
|
| 423 |
+
self.pose_dir = os.path.join(self.pose_dir, 'train_fo' if is_train else 'test_fo')
|
| 424 |
+
else:
|
| 425 |
+
print("Use random train-test split.")
|
| 426 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 427 |
+
|
| 428 |
+
if is_train:
|
| 429 |
+
self.videos = train_videos
|
| 430 |
+
else:
|
| 431 |
+
self.videos = test_videos
|
| 432 |
+
|
| 433 |
+
self.is_train = is_train
|
| 434 |
+
|
| 435 |
+
if self.is_train:
|
| 436 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 437 |
+
else:
|
| 438 |
+
self.transform = None
|
| 439 |
+
|
| 440 |
+
def __len__(self):
|
| 441 |
+
return len(self.videos)
|
| 442 |
+
|
| 443 |
+
def __getitem__(self, idx):
|
| 444 |
+
if self.is_train and self.id_sampling:
|
| 445 |
+
name = self.videos[idx].split('.')[0]
|
| 446 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 447 |
+
else:
|
| 448 |
+
name = self.videos[idx].split('.')[0]
|
| 449 |
+
|
| 450 |
+
audio_path = os.path.join(self.audio_dir, name+'.npy')
|
| 451 |
+
pose_path = os.path.join(self.pose_dir,name+'.npy')
|
| 452 |
+
path = os.path.join(self.image_dir, name)
|
| 453 |
+
|
| 454 |
+
video_name = os.path.basename(path)
|
| 455 |
+
|
| 456 |
+
if os.path.isdir(path):
|
| 457 |
+
# if self.is_train and os.path.isdir(path):
|
| 458 |
+
frames = os.listdir(path)
|
| 459 |
+
num_frames = len(frames)
|
| 460 |
+
frame_idx = np.sort(np.random.choice(num_frames, replace=True, size=2))
|
| 461 |
+
video_array = [img_as_float32(io.imread(os.path.join(path, frames[idx]))) for idx in frame_idx]
|
| 462 |
+
mfcc = np.load(audio_path)
|
| 463 |
+
pose = np.load(pose_path)
|
| 464 |
+
|
| 465 |
+
# print(audio_path,pose_path,len(mfcc))
|
| 466 |
+
|
| 467 |
+
try:
|
| 468 |
+
len(mfcc) > 16
|
| 469 |
+
except:
|
| 470 |
+
print('wrongmfcc len:',audio_path)
|
| 471 |
+
if 16 < len(mfcc) < 24 :
|
| 472 |
+
r = 0
|
| 473 |
+
else:
|
| 474 |
+
|
| 475 |
+
r = random.choice([x for x in range(3, len(mfcc)-20)])
|
| 476 |
+
|
| 477 |
+
mfccs = []
|
| 478 |
+
poses = []
|
| 479 |
+
video_array = []
|
| 480 |
+
for ind in range(1, 17):
|
| 481 |
+
t_mfcc = mfcc[r+ind][:, 1:]
|
| 482 |
+
mfccs.append(t_mfcc)
|
| 483 |
+
t_pose = pose[r+ind,:-1]
|
| 484 |
+
poses.append(t_pose)
|
| 485 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 486 |
+
video_array.append(image)
|
| 487 |
+
mfccs = np.array(mfccs)
|
| 488 |
+
poses = np.array(poses)
|
| 489 |
+
video_array = np.array(video_array)
|
| 490 |
+
|
| 491 |
+
example_image = img_as_float32(io.imread(os.path.join(path, str(r)+'.png')))
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
else:
|
| 495 |
+
print('Wrong, data path not an existing file.')
|
| 496 |
+
|
| 497 |
+
if self.transform is not None:
|
| 498 |
+
video_array = self.transform(video_array)
|
| 499 |
+
|
| 500 |
+
out = {}
|
| 501 |
+
|
| 502 |
+
driving = np.array(video_array, dtype='float32')
|
| 503 |
+
|
| 504 |
+
spatial_size = np.array(driving.shape[1:3][::-1])[np.newaxis]
|
| 505 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 506 |
+
example_image = np.array(example_image, dtype='float32')
|
| 507 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 508 |
+
out['driving_pose'] = driving_pose
|
| 509 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 510 |
+
|
| 511 |
+
out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 512 |
+
# out['name'] = video_name
|
| 513 |
+
|
| 514 |
+
return out
|
| 515 |
+
|
| 516 |
+
class MeadDataset(Dataset):
|
| 517 |
+
"""
|
| 518 |
+
Dataset of videos, each video can be represented as:
|
| 519 |
+
- an image of concatenated frames
|
| 520 |
+
- '.mp4' or '.gif'
|
| 521 |
+
- folder with all frames
|
| 522 |
+
"""
|
| 523 |
+
|
| 524 |
+
def __init__(self, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 525 |
+
random_seed=0, augmentation_params=None):
|
| 526 |
+
self.root_dir = root_dir
|
| 527 |
+
|
| 528 |
+
self.audio_dir = os.path.join(root_dir,'MEAD_MFCC')
|
| 529 |
+
self.image_dir = os.path.join(root_dir,'MEAD_fomm_crop')
|
| 530 |
+
|
| 531 |
+
self.pose_dir = os.path.join(root_dir,'MEAD_fomm_pose_crop')
|
| 532 |
+
|
| 533 |
+
self.videos = np.load('/mnt/lustre/share_data/jixinya/MEAD/MEAD_fomm_audio_less_crop.npy')
|
| 534 |
+
self.dict = np.load('/mnt/lustre/share_data/jixinya/MEAD/MEAD_fomm_neu_dic_crop.npy',allow_pickle=True).item()
|
| 535 |
+
# self.videos = os.listdir(root_dir)
|
| 536 |
+
self.frame_shape = tuple(frame_shape)
|
| 537 |
+
|
| 538 |
+
self.id_sampling = id_sampling
|
| 539 |
+
if os.path.exists(os.path.join(root_dir, 'train')):
|
| 540 |
+
assert os.path.exists(os.path.join(root_dir, 'test'))
|
| 541 |
+
print("Use predefined train-test split.")
|
| 542 |
+
if id_sampling:
|
| 543 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 544 |
+
os.listdir(os.path.join(root_dir, 'train'))}
|
| 545 |
+
train_videos = list(train_videos)
|
| 546 |
+
else:
|
| 547 |
+
train_videos = os.listdir(os.path.join(root_dir, 'train'))
|
| 548 |
+
test_videos = os.listdir(os.path.join(root_dir, 'test'))
|
| 549 |
+
self.root_dir = os.path.join(self.root_dir, 'train' if is_train else 'test')
|
| 550 |
+
else:
|
| 551 |
+
print("Use random train-test split.")
|
| 552 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 553 |
+
|
| 554 |
+
if is_train:
|
| 555 |
+
self.videos = train_videos
|
| 556 |
+
else:
|
| 557 |
+
self.videos = test_videos
|
| 558 |
+
|
| 559 |
+
self.is_train = is_train
|
| 560 |
+
|
| 561 |
+
if self.is_train:
|
| 562 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 563 |
+
else:
|
| 564 |
+
self.transform = None
|
| 565 |
+
|
| 566 |
+
def __len__(self):
|
| 567 |
+
return len(self.videos)
|
| 568 |
+
|
| 569 |
+
def __getitem__(self, idx):
|
| 570 |
+
if self.is_train and self.id_sampling:
|
| 571 |
+
name = self.videos[idx]
|
| 572 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 573 |
+
else:
|
| 574 |
+
name = self.videos[idx]
|
| 575 |
+
path = os.path.join(self.image_dir, name)
|
| 576 |
+
|
| 577 |
+
video_name = os.path.basename(path)
|
| 578 |
+
id_name = path.split('/')[-2]
|
| 579 |
+
neu_list = self.dict[id_name]
|
| 580 |
+
neu_path = os.path.join(self.image_dir, np.random.choice(neu_list))
|
| 581 |
+
|
| 582 |
+
audio_path = os.path.join(self.audio_dir, name+'.npy')
|
| 583 |
+
pose_path = os.path.join(self.pose_dir,name+'.npy')
|
| 584 |
+
|
| 585 |
+
|
| 586 |
+
if self.is_train and os.path.isdir(path):
|
| 587 |
+
|
| 588 |
+
mfcc = np.load(audio_path)
|
| 589 |
+
pose_raw = np.load(pose_path)
|
| 590 |
+
one_euro_filter = OneEuroFilter(mincutoff=0.01, beta=0.7, dcutoff=1.0, freq=100)
|
| 591 |
+
pose = np.zeros((len(pose_raw),7))
|
| 592 |
+
|
| 593 |
+
for j in range(len(pose_raw)):
|
| 594 |
+
pose[j]=one_euro_filter.process(pose_raw[j])
|
| 595 |
+
# print(audio_path,pose_path,len(mfcc))
|
| 596 |
+
|
| 597 |
+
neu_frames = os.listdir(neu_path)
|
| 598 |
+
num_neu_frames = len(neu_frames)
|
| 599 |
+
frame_idx = np.random.choice(num_neu_frames)
|
| 600 |
+
example_image = img_as_float32(io.imread(os.path.join(neu_path, neu_frames[frame_idx])))
|
| 601 |
+
try:
|
| 602 |
+
len(mfcc) > 16
|
| 603 |
+
except:
|
| 604 |
+
print('wrongmfcc len:',audio_path)
|
| 605 |
+
if 16 < len(mfcc) < 24 :
|
| 606 |
+
r = 0
|
| 607 |
+
else:
|
| 608 |
+
|
| 609 |
+
r = random.choice([x for x in range(3, len(mfcc)-20)])
|
| 610 |
+
|
| 611 |
+
mfccs = []
|
| 612 |
+
poses = []
|
| 613 |
+
video_array = []
|
| 614 |
+
for ind in range(1, 17):
|
| 615 |
+
t_mfcc = mfcc[r+ind][:, 1:]
|
| 616 |
+
mfccs.append(t_mfcc)
|
| 617 |
+
t_pose = pose[r+ind,:-1]
|
| 618 |
+
poses.append(t_pose)
|
| 619 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 620 |
+
video_array.append(image)
|
| 621 |
+
mfccs = np.array(mfccs)
|
| 622 |
+
poses = np.array(poses)
|
| 623 |
+
video_array = np.array(video_array)
|
| 624 |
+
|
| 625 |
+
else:
|
| 626 |
+
print('Wrong, data path not an existing file.')
|
| 627 |
+
|
| 628 |
+
if self.transform is not None:
|
| 629 |
+
video_array = self.transform(video_array)
|
| 630 |
+
|
| 631 |
+
out = {}
|
| 632 |
+
if self.is_train:
|
| 633 |
+
|
| 634 |
+
driving = np.array(video_array, dtype='float32')
|
| 635 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 636 |
+
example_image = np.array(example_image, dtype='float32')
|
| 637 |
+
|
| 638 |
+
|
| 639 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 640 |
+
out['driving_pose'] = driving_pose
|
| 641 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 642 |
+
out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 643 |
+
|
| 644 |
+
# out['name'] = id_name+'/'+video_name
|
| 645 |
+
|
| 646 |
+
return out
|
| 647 |
+
|
| 648 |
+
|
| 649 |
+
class DatasetRepeater(Dataset):
|
| 650 |
+
"""
|
| 651 |
+
Pass several times over the same dataset for better i/o performance
|
| 652 |
+
"""
|
| 653 |
+
|
| 654 |
+
def __init__(self, dataset, num_repeats=100):
|
| 655 |
+
self.dataset = dataset
|
| 656 |
+
# self.dataset2 = dataset2
|
| 657 |
+
self.num_repeats = num_repeats
|
| 658 |
+
|
| 659 |
+
def __len__(self):
|
| 660 |
+
return self.num_repeats * self.dataset.__len__()
|
| 661 |
+
|
| 662 |
+
def __getitem__(self, idx):
|
| 663 |
+
# if idx % 5 == 0:
|
| 664 |
+
# return self.dataset2[idx % self.dataset2.__len__()]#% self.dataset.__len__()
|
| 665 |
+
# else:
|
| 666 |
+
# return self.dataset[idx % self.dataset.__len__()]
|
| 667 |
+
return self.dataset[idx % self.dataset.__len__()]
|
| 668 |
+
|
| 669 |
+
class TestsetRepeater(Dataset):
|
| 670 |
+
"""
|
| 671 |
+
Pass several times over the same dataset for better i/o performance
|
| 672 |
+
"""
|
| 673 |
+
|
| 674 |
+
def __init__(self, dataset, num_repeats=100):
|
| 675 |
+
self.dataset = dataset
|
| 676 |
+
|
| 677 |
+
self.num_repeats = num_repeats
|
| 678 |
+
|
| 679 |
+
def __len__(self):
|
| 680 |
+
return self.num_repeats * self.dataset.__len__()
|
| 681 |
+
|
| 682 |
+
def __getitem__(self, idx):
|
| 683 |
+
|
| 684 |
+
return self.dataset[idx % self.dataset.__len__()]#% self.dataset.__len__()
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
class PairedDataset(Dataset):
|
| 688 |
+
"""
|
| 689 |
+
Dataset of pairs for animation.
|
| 690 |
+
"""
|
| 691 |
+
|
| 692 |
+
def __init__(self, initial_dataset, number_of_pairs, seed=0):
|
| 693 |
+
self.initial_dataset = initial_dataset
|
| 694 |
+
pairs_list = self.initial_dataset.pairs_list
|
| 695 |
+
|
| 696 |
+
np.random.seed(seed)
|
| 697 |
+
|
| 698 |
+
if pairs_list is None:
|
| 699 |
+
max_idx = min(number_of_pairs, len(initial_dataset))
|
| 700 |
+
nx, ny = max_idx, max_idx
|
| 701 |
+
xy = np.mgrid[:nx, :ny].reshape(2, -1).T
|
| 702 |
+
number_of_pairs = min(xy.shape[0], number_of_pairs)
|
| 703 |
+
self.pairs = xy.take(np.random.choice(xy.shape[0], number_of_pairs, replace=False), axis=0)
|
| 704 |
+
else:
|
| 705 |
+
videos = self.initial_dataset.videos
|
| 706 |
+
name_to_index = {name: index for index, name in enumerate(videos)}
|
| 707 |
+
pairs = pd.read_csv(pairs_list)
|
| 708 |
+
pairs = pairs[np.logical_and(pairs['source'].isin(videos), pairs['driving'].isin(videos))]
|
| 709 |
+
|
| 710 |
+
number_of_pairs = min(pairs.shape[0], number_of_pairs)
|
| 711 |
+
self.pairs = []
|
| 712 |
+
self.start_frames = []
|
| 713 |
+
for ind in range(number_of_pairs):
|
| 714 |
+
self.pairs.append(
|
| 715 |
+
(name_to_index[pairs['driving'].iloc[ind]], name_to_index[pairs['source'].iloc[ind]]))
|
| 716 |
+
|
| 717 |
+
def __len__(self):
|
| 718 |
+
return len(self.pairs)
|
| 719 |
+
|
| 720 |
+
def __getitem__(self, idx):
|
| 721 |
+
pair = self.pairs[idx]
|
| 722 |
+
first = self.initial_dataset[pair[0]]
|
| 723 |
+
second = self.initial_dataset[pair[1]]
|
| 724 |
+
first = {'driving_' + key: value for key, value in first.items()}
|
| 725 |
+
second = {'source_' + key: value for key, value in second.items()}
|
| 726 |
+
|
| 727 |
+
return {**first, **second}
|
FONT/gradio_demo.py
ADDED
|
@@ -0,0 +1,560 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import matplotlib
|
| 2 |
+
matplotlib.use('Agg')
|
| 3 |
+
import os,sys
|
| 4 |
+
import yaml
|
| 5 |
+
from argparse import ArgumentParser
|
| 6 |
+
from tqdm import tqdm
|
| 7 |
+
from skimage import io, img_as_float32
|
| 8 |
+
import imageio
|
| 9 |
+
import numpy as np
|
| 10 |
+
from skimage.transform import resize
|
| 11 |
+
from skimage import img_as_ubyte
|
| 12 |
+
import torch
|
| 13 |
+
from FONT.filter1 import OneEuroFilter
|
| 14 |
+
import torch.utils
|
| 15 |
+
|
| 16 |
+
from torch.autograd import Variable
|
| 17 |
+
from FONT.modules.generator import OcclusionAwareGenerator
|
| 18 |
+
from FONT.modules.keypoint_detector import KPDetector, KPDetector_a
|
| 19 |
+
from FONT.modules.util import AT_net, Emotion_k, Emotion_map, AT_net2
|
| 20 |
+
from . import augmentation
|
| 21 |
+
|
| 22 |
+
from scipy.spatial import ConvexHull
|
| 23 |
+
import random
|
| 24 |
+
import python_speech_features
|
| 25 |
+
from pathlib import Path
|
| 26 |
+
import dlib
|
| 27 |
+
import cv2
|
| 28 |
+
from skimage.draw import circle
|
| 29 |
+
import matplotlib.pyplot as plt
|
| 30 |
+
import librosa
|
| 31 |
+
from skimage import transform as tf
|
| 32 |
+
import torch.nn.functional as F
|
| 33 |
+
|
| 34 |
+
detector = dlib.get_frontal_face_detector()
|
| 35 |
+
predictor = dlib.shape_predictor('./FONT/ckpt/shape_predictor_68_face_landmarks.dat')
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class FONT():
|
| 40 |
+
|
| 41 |
+
def __init__(self, checkpoint_path='./FONT/ckpt', config_path='./FONT/config/MEAD_emo_video_aug_delta_4_crop_random_crop.yaml'):
|
| 42 |
+
|
| 43 |
+
if torch.cuda.is_available():
|
| 44 |
+
device = "cuda"
|
| 45 |
+
else:
|
| 46 |
+
device = "cpu"
|
| 47 |
+
|
| 48 |
+
self.device = device
|
| 49 |
+
|
| 50 |
+
os.environ['TORCH_HOME'] = checkpoint_path
|
| 51 |
+
|
| 52 |
+
self.checkpoint_path = checkpoint_path
|
| 53 |
+
self.config_path = config_path
|
| 54 |
+
self.result_path = './results'
|
| 55 |
+
|
| 56 |
+
def load_checkpoints(self, checkpoint_path, audio_checkpoint_path, emo_checkpoint_path, kp_checkpoint_path):
|
| 57 |
+
|
| 58 |
+
with open(self.config_path) as f:
|
| 59 |
+
config = yaml.load(f, Loader=yaml.FullLoader)
|
| 60 |
+
|
| 61 |
+
generator = OcclusionAwareGenerator(**config['model_params']['generator_params'],
|
| 62 |
+
**config['model_params']['common_params'])
|
| 63 |
+
|
| 64 |
+
generator.to(self.device)
|
| 65 |
+
|
| 66 |
+
kp_detector = KPDetector(**config['model_params']['kp_detector_params'],
|
| 67 |
+
**config['model_params']['common_params'])
|
| 68 |
+
|
| 69 |
+
kp_detector.to(self.device)
|
| 70 |
+
|
| 71 |
+
kp_detector_a = KPDetector_a(**config['model_params']['kp_detector_params'],
|
| 72 |
+
**config['model_params']['audio_params'])
|
| 73 |
+
|
| 74 |
+
audio_feature = AT_net2()
|
| 75 |
+
|
| 76 |
+
emo_detector = Emotion_k(block_expansion=32, num_channels=3, max_features=1024,
|
| 77 |
+
num_blocks=5, scale_factor=0.25, num_classes=8)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
kp_detector_a.to(self.device)
|
| 81 |
+
audio_feature.to(self.device)
|
| 82 |
+
emo_detector.to(self.device)
|
| 83 |
+
|
| 84 |
+
if self.device == 'cpu':
|
| 85 |
+
checkpoint = torch.load(checkpoint_path, map_location=torch.device('cpu'))
|
| 86 |
+
audio_checkpoint = torch.load(audio_checkpoint_path, map_location=torch.device('cpu'))
|
| 87 |
+
emo_checkpoint = torch.load(emo_checkpoint_path, map_location=torch.device('cpu'))
|
| 88 |
+
kp_checkpoint = torch.load(kp_checkpoint_path, map_location=torch.device('cpu'))
|
| 89 |
+
else:
|
| 90 |
+
checkpoint = torch.load(checkpoint_path)
|
| 91 |
+
audio_checkpoint = torch.load(audio_checkpoint_path)
|
| 92 |
+
emo_checkpoint = torch.load(emo_checkpoint_path)
|
| 93 |
+
kp_checkpoint = torch.load(kp_checkpoint_path)
|
| 94 |
+
|
| 95 |
+
generator.load_state_dict(checkpoint['generator'])
|
| 96 |
+
kp_detector.load_state_dict(kp_checkpoint['kp_detector'])
|
| 97 |
+
audio_feature.load_state_dict(audio_checkpoint['audio_feature'], strict=False)
|
| 98 |
+
|
| 99 |
+
kp_detector_a.load_state_dict(audio_checkpoint['kp_detector_a'])
|
| 100 |
+
emo_detector.load_state_dict(emo_checkpoint['emo_detector'])
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
generator.eval()
|
| 104 |
+
kp_detector.eval()
|
| 105 |
+
audio_feature.eval()
|
| 106 |
+
kp_detector_a.eval()
|
| 107 |
+
emo_detector.eval()
|
| 108 |
+
return generator, kp_detector, kp_detector_a, audio_feature, emo_detector
|
| 109 |
+
|
| 110 |
+
def normalize_kp(self, kp_source, kp_driving, kp_driving_initial, adapt_movement_scale=False,
|
| 111 |
+
use_relative_movement=False, use_relative_jacobian=False):
|
| 112 |
+
if adapt_movement_scale:
|
| 113 |
+
source_area = ConvexHull(kp_source['value'][0].data.cpu().numpy()).volume
|
| 114 |
+
driving_area = ConvexHull(kp_driving_initial['value'][0].data.cpu().numpy()).volume
|
| 115 |
+
adapt_movement_scale = np.sqrt(source_area) / np.sqrt(driving_area)
|
| 116 |
+
else:
|
| 117 |
+
adapt_movement_scale = 1
|
| 118 |
+
|
| 119 |
+
kp_new = {k: v for k, v in kp_driving.items()}
|
| 120 |
+
|
| 121 |
+
if use_relative_movement:
|
| 122 |
+
kp_value_diff = (kp_driving['value'] - kp_driving_initial['value'])
|
| 123 |
+
kp_value_diff *= adapt_movement_scale
|
| 124 |
+
kp_new['value'] = kp_value_diff + kp_source['value']
|
| 125 |
+
|
| 126 |
+
if use_relative_jacobian:
|
| 127 |
+
jacobian_diff = torch.matmul(kp_driving['jacobian'], torch.inverse(kp_driving_initial['jacobian']))
|
| 128 |
+
kp_new['jacobian'] = torch.matmul(jacobian_diff, kp_source['jacobian'])
|
| 129 |
+
|
| 130 |
+
return kp_new
|
| 131 |
+
|
| 132 |
+
def shape_to_np(self, shape, dtype="int"):
|
| 133 |
+
# initialize the list of (x, y)-coordinates
|
| 134 |
+
coords = np.zeros((shape.num_parts, 2), dtype=dtype)
|
| 135 |
+
|
| 136 |
+
# loop over all facial landmarks and convert them
|
| 137 |
+
# to a 2-tuple of (x, y)-coordinates
|
| 138 |
+
for i in range(0, shape.num_parts):
|
| 139 |
+
coords[i] = (shape.part(i).x, shape.part(i).y)
|
| 140 |
+
|
| 141 |
+
# return the list of (x, y)-coordinates
|
| 142 |
+
return coords
|
| 143 |
+
|
| 144 |
+
def get_aligned_image(self, driving_video):
|
| 145 |
+
aligned_array = []
|
| 146 |
+
|
| 147 |
+
video_array = np.array(driving_video)
|
| 148 |
+
source_image = video_array[0]
|
| 149 |
+
# aligned_array.append(source_image)
|
| 150 |
+
source_image = np.array(source_image * 255, dtype=np.uint8)
|
| 151 |
+
gray = cv2.cvtColor(source_image, cv2.COLOR_BGR2GRAY)
|
| 152 |
+
rects = detector(gray, 1) # detect human face
|
| 153 |
+
for (i, rect) in enumerate(rects):
|
| 154 |
+
template = predictor(gray, rect) # detect 68 points
|
| 155 |
+
template = shape_to_np(template)
|
| 156 |
+
|
| 157 |
+
# if opt.emotion == 'surprised' or opt.emotion == 'fear':
|
| 158 |
+
# template = template - [0, 10]
|
| 159 |
+
for i in range(len(video_array)):
|
| 160 |
+
image = np.array(video_array[i] * 255, dtype=np.uint8)
|
| 161 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| 162 |
+
rects = detector(gray, 1) # detect human face
|
| 163 |
+
for (j, rect) in enumerate(rects):
|
| 164 |
+
shape = predictor(gray, rect) # detect 68 points
|
| 165 |
+
shape = shape_to_np(shape)
|
| 166 |
+
|
| 167 |
+
pts2 = np.float32(template[:35, :])
|
| 168 |
+
pts1 = np.float32(shape[:35, :]) # eye and nose
|
| 169 |
+
|
| 170 |
+
# pts2 = np.float32(np.concatenate((template[:16,:],template[27:36,:]),axis = 0))
|
| 171 |
+
# pts1 = np.float32(np.concatenate((shape[:16,:],shape[27:36,:]),axis = 0)) #eye and nose
|
| 172 |
+
# pts1 = np.float32(landmark[17:35,:])
|
| 173 |
+
tform = tf.SimilarityTransform()
|
| 174 |
+
tform.estimate(pts2, pts1) # Set the transformation matrix with the explicit parameters.
|
| 175 |
+
dst = tf.warp(image, tform, output_shape=(256, 256))
|
| 176 |
+
|
| 177 |
+
dst = np.array(dst, dtype=np.float32)
|
| 178 |
+
aligned_array.append(dst)
|
| 179 |
+
|
| 180 |
+
return aligned_array
|
| 181 |
+
|
| 182 |
+
def get_transformed_image(self, driving_video):
|
| 183 |
+
video_array = np.array(driving_video)
|
| 184 |
+
with open(self.config_path) as f:
|
| 185 |
+
config = yaml.load(f, Loader=yaml.FullLoader)
|
| 186 |
+
transformations = AllAugmentationTransform(**config['dataset_params']['augmentation_params'])
|
| 187 |
+
transformed_array = transformations(video_array)
|
| 188 |
+
return transformed_array
|
| 189 |
+
|
| 190 |
+
def make_animation_smooth(self, source_image, deco_out, kp_loss, generator, kp_detector,
|
| 191 |
+
kp_detector_a, emo_detector, relative=True, adapt_movement_scale=True):
|
| 192 |
+
with torch.no_grad():
|
| 193 |
+
predictions = []
|
| 194 |
+
|
| 195 |
+
source = torch.tensor(source_image[np.newaxis].astype(np.float32)).permute(0, 3, 1, 2)
|
| 196 |
+
if self.device == 'cuda':
|
| 197 |
+
source = source.cuda()
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
kp_source = kp_detector(source)
|
| 201 |
+
|
| 202 |
+
save_dpi = (100, 100)
|
| 203 |
+
# save kp source image
|
| 204 |
+
# source = kp_source['value'].data.cpu().numpy()
|
| 205 |
+
# kp_array = source[0]
|
| 206 |
+
# image = np.zeros((256,256,3))
|
| 207 |
+
# spatial_size = np.array(image.shape[:2][::-1])[np.newaxis]
|
| 208 |
+
# kp_array = spatial_size * (kp_array + 1) / 2
|
| 209 |
+
# num_kp = kp_array.shape[0]
|
| 210 |
+
colormap = plt.get_cmap('gist_rainbow')
|
| 211 |
+
# for kp_ind, kp in enumerate(kp_array):
|
| 212 |
+
# rr, cc = circle(kp[1], kp[0], 5, shape=image.shape[:2])
|
| 213 |
+
# image[rr, cc] = np.array(colormap(kp_ind / num_kp))[:3]
|
| 214 |
+
# imageio.imsave('./result/kp_source.png', image)
|
| 215 |
+
|
| 216 |
+
kp_driving_initial = kp_detector_a(deco_out[:, 0])
|
| 217 |
+
|
| 218 |
+
emo_driving_all = []
|
| 219 |
+
features = []
|
| 220 |
+
kp_driving_all = []
|
| 221 |
+
for frame_idx in tqdm(range(len(deco_out[0]))):
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
kp_driving = kp_detector_a(deco_out[:, frame_idx])
|
| 225 |
+
kp_driving_all.append(kp_driving)
|
| 226 |
+
# if opt.add_emo:
|
| 227 |
+
# value = kp_driving['value']
|
| 228 |
+
# jacobian = kp_driving['jacobian']
|
| 229 |
+
# if opt.type == 'linear_3':
|
| 230 |
+
# emo_driving, _ = emo_detector(transformed_frame, value, jacobian)
|
| 231 |
+
# features.append(emo_detector.feature(transformed_frame).data.cpu().numpy())
|
| 232 |
+
#
|
| 233 |
+
# emo_driving_all.append(emo_driving)
|
| 234 |
+
features = np.array(features)
|
| 235 |
+
# if opt.add_emo:
|
| 236 |
+
# one_euro_filter_v = OneEuroFilter(mincutoff=1, beta=0.2, dcutoff=1.0, freq=100) # 1 0.4
|
| 237 |
+
# one_euro_filter_j = OneEuroFilter(mincutoff=1, beta=0.2, dcutoff=1.0, freq=100) # 1 0.4
|
| 238 |
+
#
|
| 239 |
+
# for j in range(len(emo_driving_all)):
|
| 240 |
+
# emo_driving_all[j]['value'] = one_euro_filter_v.process(
|
| 241 |
+
# emo_driving_all[j]['value'].cpu() * 100) / 100
|
| 242 |
+
# emo_driving_all[j]['value'] = emo_driving_all[j]['value'].cuda()
|
| 243 |
+
# emo_driving_all[j]['jacobian'] = one_euro_filter_j.process(
|
| 244 |
+
# emo_driving_all[j]['jacobian'].cpu() * 100) / 100
|
| 245 |
+
# emo_driving_all[j]['jacobian'] = emo_driving_all[j]['jacobian'].cuda()
|
| 246 |
+
|
| 247 |
+
one_euro_filter_v = OneEuroFilter(mincutoff=0.05, beta=8, dcutoff=1.0, freq=100)
|
| 248 |
+
one_euro_filter_j = OneEuroFilter(mincutoff=0.05, beta=8, dcutoff=1.0, freq=100)
|
| 249 |
+
|
| 250 |
+
for j in range(len(kp_driving_all)):
|
| 251 |
+
kp_driving_all[j]['value'] = one_euro_filter_v.process(kp_driving_all[j]['value'].cpu() * 10) / 10
|
| 252 |
+
kp_driving_all[j]['value'] = kp_driving_all[j]['value'].cuda()
|
| 253 |
+
kp_driving_all[j]['jacobian'] = one_euro_filter_j.process(kp_driving_all[j]['jacobian'].cpu() * 10) / 10
|
| 254 |
+
kp_driving_all[j]['jacobian'] = kp_driving_all[j]['jacobian'].cuda()
|
| 255 |
+
|
| 256 |
+
for frame_idx in tqdm(range(len(deco_out[0]))):
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
kp_driving = kp_driving_all[frame_idx]
|
| 260 |
+
|
| 261 |
+
# kp_driving_real = kp_detector(driving_frame)
|
| 262 |
+
|
| 263 |
+
# kp_driving['value'] = (1-opt.weight)*kp_driving['value'] + opt.weight*kp_driving_real['value']
|
| 264 |
+
# kp_driving['jacobian'] = (1-opt.weight)*kp_driving['jacobian'] + opt.weight*kp_driving_real['jacobian']
|
| 265 |
+
|
| 266 |
+
# if opt.add_emo:
|
| 267 |
+
# emo_driving = emo_driving_all[frame_idx]
|
| 268 |
+
# if opt.type == 'linear_3':
|
| 269 |
+
# kp_driving['value'][:, 1] = kp_driving['value'][:, 1] + emo_driving['value'][:, 0] * 0.2
|
| 270 |
+
# kp_driving['jacobian'][:, 1] = kp_driving['jacobian'][:, 1] + emo_driving['jacobian'][:,
|
| 271 |
+
# 0] * 0.2
|
| 272 |
+
# kp_driving['value'][:, 4] = kp_driving['value'][:, 4] + emo_driving['value'][:, 1]
|
| 273 |
+
# kp_driving['jacobian'][:, 4] = kp_driving['jacobian'][:, 4] + emo_driving['jacobian'][:, 1]
|
| 274 |
+
# kp_driving['value'][:, 6] = kp_driving['value'][:, 6] + emo_driving['value'][:, 2]
|
| 275 |
+
# kp_driving['jacobian'][:, 6] = kp_driving['jacobian'][:, 6] + emo_driving['jacobian'][:, 2]
|
| 276 |
+
# # kp_driving['value'][:,8] = kp_driving['value'][:,8] + emo_driving['value'][:,3]
|
| 277 |
+
# # kp_driving['jacobian'][:,8] = kp_driving['jacobian'][:,8] + emo_driving['jacobian'][:,3]
|
| 278 |
+
|
| 279 |
+
kp_norm = self.normalize_kp(kp_source=kp_source, kp_driving=kp_driving,
|
| 280 |
+
kp_driving_initial=kp_driving_initial, use_relative_movement=relative,
|
| 281 |
+
use_relative_jacobian=relative, adapt_movement_scale=adapt_movement_scale)
|
| 282 |
+
out = generator(source, kp_source=kp_source, kp_driving=kp_norm)
|
| 283 |
+
|
| 284 |
+
# occlusion_map = out['occlusion_map'].data.cpu().repeat(1, 3, 1, 1)
|
| 285 |
+
# occlusion_map = F.interpolate(occlusion_map, size=(256,256)).numpy()
|
| 286 |
+
# occlusion_map = np.transpose(occlusion_map, [0, 2, 3, 1])
|
| 287 |
+
# print(occlusion_map.shape)
|
| 288 |
+
# imageio.imsave('./result/occlusion.png', occlusion_map[0], dpi=save_dpi)
|
| 289 |
+
#
|
| 290 |
+
# prediction = out['prediction'].data.cpu().numpy()
|
| 291 |
+
# prediction = np.transpose(prediction, [0, 2, 3, 1])
|
| 292 |
+
# print(prediction.shape)
|
| 293 |
+
# imageio.imsave('./result/prediction.png', prediction[0], dpi=save_dpi)
|
| 294 |
+
#
|
| 295 |
+
# full_mask = []
|
| 296 |
+
# for i in range(out['sparse_deformed'].shape[1]):
|
| 297 |
+
# mask = out['mask'][:, i:(i + 1)].data.cpu().repeat(1, 3, 1, 1)
|
| 298 |
+
# mask = F.interpolate(mask, size=(256,256))
|
| 299 |
+
# mask = np.transpose(mask.numpy(), (0, 2, 3, 1))
|
| 300 |
+
# if i != 0:
|
| 301 |
+
# color = np.array(colormap((i - 1) / (out['sparse_deformed'].shape[1] - 1)))[:3]
|
| 302 |
+
# else:
|
| 303 |
+
# color = np.array((0, 0, 0))
|
| 304 |
+
#
|
| 305 |
+
# color = color.reshape((1, 1, 1, 3))
|
| 306 |
+
#
|
| 307 |
+
# full_mask.append(mask * color)
|
| 308 |
+
# motion_flow = sum(full_mask)
|
| 309 |
+
# print(motion_flow.shape)
|
| 310 |
+
# imageio.imsave('./result/motion_flow.png', motion_flow[0], dpi=save_dpi)
|
| 311 |
+
# quit()
|
| 312 |
+
|
| 313 |
+
predictions.append(np.transpose(out['prediction'].data.cpu().numpy(), [0, 2, 3, 1])[0])
|
| 314 |
+
return predictions, features
|
| 315 |
+
|
| 316 |
+
def test_auido(self, example_image, audio_feature, all_pose, driving_audio_path):
|
| 317 |
+
with open(self.config_path) as f:
|
| 318 |
+
para = yaml.load(f, Loader=yaml.FullLoader)
|
| 319 |
+
|
| 320 |
+
# encoder = audio_feature()
|
| 321 |
+
|
| 322 |
+
audio_feature = audio_feature.to(self.device)
|
| 323 |
+
audio_feature.eval()
|
| 324 |
+
# decoder.eval()
|
| 325 |
+
test_file = driving_audio_path
|
| 326 |
+
pose = all_pose[:, :6]
|
| 327 |
+
|
| 328 |
+
one_euro_filter = OneEuroFilter(mincutoff=0.004, beta=0.7, dcutoff=1.0, freq=100)
|
| 329 |
+
|
| 330 |
+
for j in range(len(pose)):
|
| 331 |
+
pose[j] = one_euro_filter.process(pose[j])
|
| 332 |
+
# pose[j]=pose[0]
|
| 333 |
+
|
| 334 |
+
example_image = np.array(example_image, dtype='float32').transpose((2, 0, 1))
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
# get atvg audio feature
|
| 339 |
+
speech, sr = librosa.load(test_file, sr=16000)
|
| 340 |
+
# mfcc = python_speech_features.mfcc(speech ,16000,winstep=0.01)
|
| 341 |
+
speech = np.insert(speech, 0, np.zeros(1920))
|
| 342 |
+
speech = np.append(speech, np.zeros(1920))
|
| 343 |
+
mfcc = python_speech_features.mfcc(speech, 16000, winstep=0.01)
|
| 344 |
+
ind = 3
|
| 345 |
+
fake_lmark = []
|
| 346 |
+
input_mfcc = []
|
| 347 |
+
while ind <= int(mfcc.shape[0] / 4) - 4:
|
| 348 |
+
t_mfcc = mfcc[(ind - 3) * 4: (ind + 4) * 4, 1:]
|
| 349 |
+
t_mfcc = torch.FloatTensor(t_mfcc).cuda()
|
| 350 |
+
input_mfcc.append(t_mfcc)
|
| 351 |
+
ind += 1
|
| 352 |
+
input_mfcc = torch.stack(input_mfcc, dim=0) # N,28,12
|
| 353 |
+
|
| 354 |
+
print('=======================================')
|
| 355 |
+
print('Start to generate images')
|
| 356 |
+
|
| 357 |
+
with torch.no_grad():
|
| 358 |
+
if (len(pose) < len(input_mfcc)):
|
| 359 |
+
gap = len(input_mfcc) - len(pose)
|
| 360 |
+
n = int((gap / len(pose) / 2)) + 2
|
| 361 |
+
pose = np.concatenate((pose, pose[::-1, :]), axis=0)
|
| 362 |
+
pose = np.tile(pose, (n, 1))
|
| 363 |
+
if (len(pose) > len(input_mfcc)):
|
| 364 |
+
pose = pose[:len(input_mfcc), :]
|
| 365 |
+
|
| 366 |
+
pose = Variable(torch.FloatTensor(pose.astype(float)))
|
| 367 |
+
example_image = Variable(torch.FloatTensor(example_image.astype(float)))
|
| 368 |
+
|
| 369 |
+
if self.device == 'cuda':
|
| 370 |
+
pose = pose.cuda()
|
| 371 |
+
example_image = example_image.cuda()
|
| 372 |
+
input_mfcc = input_mfcc.cuda()
|
| 373 |
+
|
| 374 |
+
pose = pose.unsqueeze(0)
|
| 375 |
+
example_image = example_image.unsqueeze(0)
|
| 376 |
+
input_mfcc = input_mfcc.unsqueeze(0)
|
| 377 |
+
|
| 378 |
+
deco_out = audio_feature(example_image, input_mfcc, pose, para['train_params']['jaco_net'], 1.6)
|
| 379 |
+
|
| 380 |
+
# ATNET
|
| 381 |
+
# deco_out = audio_feature(example_image, input_mfcc, pose, para['train_params']['jaco_net'])
|
| 382 |
+
|
| 383 |
+
return deco_out
|
| 384 |
+
|
| 385 |
+
def save(self, path, frames, format):
|
| 386 |
+
|
| 387 |
+
if format == '.png':
|
| 388 |
+
if not os.path.exists(path):
|
| 389 |
+
os.makedirs(path)
|
| 390 |
+
for j, frame in enumerate(frames):
|
| 391 |
+
imageio.imsave(path + '/' + str(j) + '.png', frame)
|
| 392 |
+
# imageio.imsave(os.path.join(path, str(j) + '.png'), frames[j])
|
| 393 |
+
else:
|
| 394 |
+
print("Unknown format %s" % format)
|
| 395 |
+
exit()
|
| 396 |
+
|
| 397 |
+
class VideoWriter(object):
|
| 398 |
+
def __init__(self, path, width, height, fps):
|
| 399 |
+
fourcc = cv2.VideoWriter_fourcc(*'XVID')
|
| 400 |
+
self.path = path
|
| 401 |
+
self.out = cv2.VideoWriter(self.path, fourcc, fps, (width, height))
|
| 402 |
+
|
| 403 |
+
def write_frame(self, frame):
|
| 404 |
+
self.out.write(frame)
|
| 405 |
+
|
| 406 |
+
def end(self):
|
| 407 |
+
self.out.release()
|
| 408 |
+
|
| 409 |
+
def get_pose_from_audio(self, driving_audio):
|
| 410 |
+
pose = './FONT/test/pose/7.npy'
|
| 411 |
+
pose_long = './FONT/test/pose_long/0zn70Ak8lRc_Daniel_Auteuil_0zn70Ak8lRc_0002.npy'
|
| 412 |
+
return pose, pose_long
|
| 413 |
+
|
| 414 |
+
def concatenate(self, number, imgs, save_path):
|
| 415 |
+
width, height = imgs.shape[-3:-1]
|
| 416 |
+
imgs = imgs.reshape(number, -1, width, height, 3)
|
| 417 |
+
if number == 2:
|
| 418 |
+
left = imgs[0]
|
| 419 |
+
right = imgs[1]
|
| 420 |
+
|
| 421 |
+
im_all = []
|
| 422 |
+
for i in range(len(left)):
|
| 423 |
+
im = np.concatenate((left[i], right[i]), axis=1)
|
| 424 |
+
im_all.append(im)
|
| 425 |
+
if number == 3:
|
| 426 |
+
left = imgs[0]
|
| 427 |
+
middle = imgs[1]
|
| 428 |
+
right = imgs[2]
|
| 429 |
+
|
| 430 |
+
im_all = []
|
| 431 |
+
for i in range(len(left)):
|
| 432 |
+
im = np.concatenate((left[i], middle[i], right[i]), axis=1)
|
| 433 |
+
im_all.append(im)
|
| 434 |
+
if number == 4:
|
| 435 |
+
left = imgs[0]
|
| 436 |
+
left2 = imgs[1]
|
| 437 |
+
right = imgs[2]
|
| 438 |
+
right2 = imgs[3]
|
| 439 |
+
|
| 440 |
+
im_all = []
|
| 441 |
+
for i in range(len(left)):
|
| 442 |
+
im = np.concatenate((left[i], left2[i], right[i], right2[i]), axis=1)
|
| 443 |
+
im_all.append(im)
|
| 444 |
+
if number == 5:
|
| 445 |
+
left = imgs[0]
|
| 446 |
+
left2 = imgs[1]
|
| 447 |
+
middle = imgs[2]
|
| 448 |
+
right = imgs[3]
|
| 449 |
+
right2 = imgs[4]
|
| 450 |
+
|
| 451 |
+
im_all = []
|
| 452 |
+
for i in range(len(left)):
|
| 453 |
+
im = np.concatenate((left[i], left2[i], middle[i], right[i], right2[i]), axis=1)
|
| 454 |
+
im_all.append(im)
|
| 455 |
+
|
| 456 |
+
imageio.mimsave(save_path, [img_as_ubyte(frame) for frame in im_all], fps=25)
|
| 457 |
+
|
| 458 |
+
def add_audio(self, video_name=None, audio_dir=None):
|
| 459 |
+
|
| 460 |
+
command = 'ffmpeg -i ' + video_name + ' -i ' + audio_dir + ' -vcodec copy -acodec copy -y ' + video_name.replace(
|
| 461 |
+
'.mp4', '.mov')
|
| 462 |
+
print(command)
|
| 463 |
+
os.system(command)
|
| 464 |
+
|
| 465 |
+
def smooth_pose(self, pose_file, pose_long):
|
| 466 |
+
start = np.load(pose_file)
|
| 467 |
+
video_pose = np.load(pose_long)
|
| 468 |
+
if video_pose.shape[-1] == 6:
|
| 469 |
+
start = start[:, :6]
|
| 470 |
+
|
| 471 |
+
delta = video_pose - video_pose[0, :]
|
| 472 |
+
# print(len(delta))
|
| 473 |
+
|
| 474 |
+
pose = np.repeat(start, len(delta), axis=0)
|
| 475 |
+
all_pose = pose + delta
|
| 476 |
+
|
| 477 |
+
return all_pose
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
|
| 481 |
+
def test(self, source_image_path, driving_audio_path, result_dir='./results/'):
|
| 482 |
+
|
| 483 |
+
pose_file, pose_given = self.get_pose_from_audio(driving_audio_path)
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
pose_dim = np.load(pose_file).shape[-1]
|
| 487 |
+
all_pose = np.load(pose_file).reshape(-1, pose_dim)
|
| 488 |
+
all_pose = self.smooth_pose(pose_file, pose_given)
|
| 489 |
+
|
| 490 |
+
source_image = img_as_float32(io.imread(source_image_path))
|
| 491 |
+
source_image = resize(source_image, (256, 256))[..., :3]
|
| 492 |
+
|
| 493 |
+
# reader = imageio.get_reader(driving_video)
|
| 494 |
+
# fps = reader.get_meta_data()['fps']
|
| 495 |
+
# driving_video = []
|
| 496 |
+
# try:
|
| 497 |
+
# for im in reader:
|
| 498 |
+
# driving_video.append(im)
|
| 499 |
+
# except RuntimeError:
|
| 500 |
+
# pass
|
| 501 |
+
# reader.close()
|
| 502 |
+
|
| 503 |
+
# driving_video = [resize(frame, (256, 256))[..., :3] for frame in driving_video]
|
| 504 |
+
# driving_video = self.get_aligned_image(driving_video)
|
| 505 |
+
# transformed_video = self.get_transformed_image(driving_video)
|
| 506 |
+
# transformed_video = np.array(transformed_video)
|
| 507 |
+
|
| 508 |
+
checkpoint = os.path.join(self.checkpoint_path, "124_52000.pth.tar")
|
| 509 |
+
audio_checkpoint = os.path.join(self.checkpoint_path, "1-6000.pth.tar")
|
| 510 |
+
emo_checkpoint = os.path.join(self.checkpoint_path, "5-3000.pth.tar")
|
| 511 |
+
|
| 512 |
+
generator, kp_detector, kp_detector_a, audio_feature, emo_detector = \
|
| 513 |
+
self.load_checkpoints(checkpoint_path=checkpoint, audio_checkpoint_path=audio_checkpoint,
|
| 514 |
+
emo_checkpoint_path=emo_checkpoint, kp_checkpoint_path=checkpoint)
|
| 515 |
+
|
| 516 |
+
deco_out = self.test_auido(source_image, audio_feature, all_pose, driving_audio_path) # 1,N,32+3,64,64
|
| 517 |
+
|
| 518 |
+
# if len(driving_video) < len(deco_out[0]):
|
| 519 |
+
# driving_video = np.resize(driving_video, (len(deco_out[0]), 256, 256, 3))
|
| 520 |
+
# transformed_video = np.resize(transformed_video, (len(deco_out[0]), 256, 256, 3))
|
| 521 |
+
#
|
| 522 |
+
# else:
|
| 523 |
+
# driving_video = driving_video[:len(deco_out[0])]
|
| 524 |
+
predictions, _ = self.make_animation_smooth(source_image, deco_out, 0,
|
| 525 |
+
generator, kp_detector, kp_detector_a, emo_detector)
|
| 526 |
+
|
| 527 |
+
imageio.mimsave(os.path.join(self.result_path, 'neutral.mp4'), [img_as_ubyte(frame) for frame in predictions],
|
| 528 |
+
fps=30)
|
| 529 |
+
predictions = np.array(predictions)
|
| 530 |
+
|
| 531 |
+
# opt.add_emo = True
|
| 532 |
+
# predictions1, _ = make_animation_smooth(source_image, driving_video, transformed_video, deco_out, opt.kp_loss,
|
| 533 |
+
# generator, kp_detector, kp_detector_a, emo_detector, opt,
|
| 534 |
+
# relative=opt.relative, adapt_movement_scale=opt.adapt_scale,
|
| 535 |
+
# cpu=opt.cpu)
|
| 536 |
+
|
| 537 |
+
# imageio.mimsave(os.path.join(self.result_path, 'emotion.mp4'), [img_as_ubyte(frame) for frame in predictions1],
|
| 538 |
+
# fps=fps)
|
| 539 |
+
# add_audio(os.path.join(self.result_path, 'emotion.mp4'), opt.in_file)
|
| 540 |
+
# predictions1 = np.array(predictions1)
|
| 541 |
+
# all_imgs = np.concatenate((driving_video, predictions, predictions1), axis=0)
|
| 542 |
+
save_path = os.path.join(self.result_path, 'neutral.mp4')
|
| 543 |
+
# concatenate(3, all_imgs, save_path)
|
| 544 |
+
self.add_audio(save_path, driving_audio_path)
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
|
| 548 |
+
# del self.preprocess_model
|
| 549 |
+
# del self.audio_to_coeff
|
| 550 |
+
# del self.animate_from_coeff
|
| 551 |
+
|
| 552 |
+
if torch.cuda.is_available():
|
| 553 |
+
torch.cuda.empty_cache()
|
| 554 |
+
torch.cuda.synchronize()
|
| 555 |
+
|
| 556 |
+
import gc;
|
| 557 |
+
gc.collect()
|
| 558 |
+
|
| 559 |
+
return save_path
|
| 560 |
+
|
FONT/hparams.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from glob import glob
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
def get_image_list(data_root, split):
|
| 5 |
+
filelist = []
|
| 6 |
+
|
| 7 |
+
with open('filelists/{}.txt'.format(split)) as f:
|
| 8 |
+
for line in f:
|
| 9 |
+
line = line.strip()
|
| 10 |
+
if ' ' in line: line = line.split()[0]
|
| 11 |
+
filelist.append(os.path.join(data_root, line))
|
| 12 |
+
|
| 13 |
+
return filelist
|
| 14 |
+
|
| 15 |
+
class HParams:
|
| 16 |
+
def __init__(self, **kwargs):
|
| 17 |
+
self.data = {}
|
| 18 |
+
|
| 19 |
+
for key, value in kwargs.items():
|
| 20 |
+
self.data[key] = value
|
| 21 |
+
|
| 22 |
+
def __getattr__(self, key):
|
| 23 |
+
if key not in self.data:
|
| 24 |
+
raise AttributeError("'HParams' object has no attribute %s" % key)
|
| 25 |
+
return self.data[key]
|
| 26 |
+
|
| 27 |
+
def set_hparam(self, key, value):
|
| 28 |
+
self.data[key] = value
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
# Default hyperparameters
|
| 32 |
+
hparams = HParams(
|
| 33 |
+
num_mels=80, # Number of mel-spectrogram channels and local conditioning dimensionality
|
| 34 |
+
# network
|
| 35 |
+
rescale=True, # Whether to rescale audio prior to preprocessing
|
| 36 |
+
rescaling_max=0.9, # Rescaling value
|
| 37 |
+
|
| 38 |
+
# Use LWS (https://github.com/Jonathan-LeRoux/lws) for STFT and phase reconstruction
|
| 39 |
+
# It"s preferred to set True to use with https://github.com/r9y9/wavenet_vocoder
|
| 40 |
+
# Does not work if n_ffit is not multiple of hop_size!!
|
| 41 |
+
use_lws=False,
|
| 42 |
+
|
| 43 |
+
n_fft=800, # Extra window size is filled with 0 paddings to match this parameter
|
| 44 |
+
hop_size=200, # For 16000Hz, 200 = 12.5 ms (0.0125 * sample_rate)
|
| 45 |
+
win_size=800, # For 16000Hz, 800 = 50 ms (If None, win_size = n_fft) (0.05 * sample_rate)
|
| 46 |
+
sample_rate=16000, # 16000Hz (corresponding to librispeech) (sox --i <filename>)
|
| 47 |
+
|
| 48 |
+
frame_shift_ms=None, # Can replace hop_size parameter. (Recommended: 12.5)
|
| 49 |
+
|
| 50 |
+
# Mel and Linear spectrograms normalization/scaling and clipping
|
| 51 |
+
signal_normalization=True,
|
| 52 |
+
# Whether to normalize mel spectrograms to some predefined range (following below parameters)
|
| 53 |
+
allow_clipping_in_normalization=True, # Only relevant if mel_normalization = True
|
| 54 |
+
symmetric_mels=True,
|
| 55 |
+
# Whether to scale the data to be symmetric around 0. (Also multiplies the output range by 2,
|
| 56 |
+
# faster and cleaner convergence)
|
| 57 |
+
max_abs_value=4.,
|
| 58 |
+
# max absolute value of data. If symmetric, data will be [-max, max] else [0, max] (Must not
|
| 59 |
+
# be too big to avoid gradient explosion,
|
| 60 |
+
# not too small for fast convergence)
|
| 61 |
+
# Contribution by @begeekmyfriend
|
| 62 |
+
# Spectrogram Pre-Emphasis (Lfilter: Reduce spectrogram noise and helps model certitude
|
| 63 |
+
# levels. Also allows for better G&L phase reconstruction)
|
| 64 |
+
preemphasize=True, # whether to apply filter
|
| 65 |
+
preemphasis=0.97, # filter coefficient.
|
| 66 |
+
|
| 67 |
+
# Limits
|
| 68 |
+
min_level_db=-100,
|
| 69 |
+
ref_level_db=20,
|
| 70 |
+
fmin=55,
|
| 71 |
+
# Set this to 55 if your speaker is male! if female, 95 should help taking off noise. (To
|
| 72 |
+
# test depending on dataset. Pitch info: male~[65, 260], female~[100, 525])
|
| 73 |
+
fmax=7600, # To be increased/reduced depending on data.
|
| 74 |
+
|
| 75 |
+
###################### Our training parameters #################################
|
| 76 |
+
img_size=96,
|
| 77 |
+
fps=25,
|
| 78 |
+
|
| 79 |
+
batch_size=16,
|
| 80 |
+
initial_learning_rate=1e-4,
|
| 81 |
+
nepochs=200000000000000000, ### ctrl + c, stop whenever eval loss is consistently greater than train loss for ~10 epochs
|
| 82 |
+
num_workers=16,
|
| 83 |
+
checkpoint_interval=3000,
|
| 84 |
+
eval_interval=3000,
|
| 85 |
+
save_optimizer_state=True,
|
| 86 |
+
|
| 87 |
+
syncnet_wt=0.0, # is initially zero, will be set automatically to 0.03 later. Leads to faster convergence.
|
| 88 |
+
syncnet_batch_size=64,
|
| 89 |
+
syncnet_lr=1e-4,
|
| 90 |
+
syncnet_eval_interval=10000,
|
| 91 |
+
syncnet_checkpoint_interval=10000,
|
| 92 |
+
|
| 93 |
+
disc_wt=0.07,
|
| 94 |
+
disc_initial_learning_rate=1e-4,
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def hparams_debug_string():
|
| 99 |
+
values = hparams.values()
|
| 100 |
+
hp = [" %s: %s" % (name, values[name]) for name in sorted(values) if name != "sentences"]
|
| 101 |
+
return "Hyperparameters:\n" + "\n".join(hp)
|
FONT/logger.py
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
import imageio
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
from skimage.draw import circle
|
| 8 |
+
|
| 9 |
+
import matplotlib.pyplot as plt
|
| 10 |
+
import collections
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class Logger:
|
| 14 |
+
def __init__(self, log_dir, checkpoint_freq=50, visualizer_params=None, zfill_num=8, log_file_name='log.txt'):
|
| 15 |
+
|
| 16 |
+
self.loss_list = []
|
| 17 |
+
self.cpk_dir = log_dir
|
| 18 |
+
self.visualizations_dir = os.path.join(log_dir, 'train-vis')
|
| 19 |
+
if not os.path.exists(self.visualizations_dir):
|
| 20 |
+
os.makedirs(self.visualizations_dir)
|
| 21 |
+
self.log_file = open(os.path.join(log_dir, log_file_name), 'a')
|
| 22 |
+
self.zfill_num = zfill_num
|
| 23 |
+
self.visualizer = Visualizer(**visualizer_params)
|
| 24 |
+
self.checkpoint_freq = checkpoint_freq
|
| 25 |
+
self.epoch = 0
|
| 26 |
+
self.best_loss = float('inf')
|
| 27 |
+
self.names = None
|
| 28 |
+
|
| 29 |
+
def log_scores(self, loss_names):
|
| 30 |
+
loss_mean = np.array(self.loss_list).mean(axis=0)
|
| 31 |
+
|
| 32 |
+
loss_string = "; ".join(["%s - %.5f" % (name, value) for name, value in zip(loss_names, loss_mean)])
|
| 33 |
+
loss_string = str(str(self.epoch)+str(self.step).zfill(self.zfill_num)) + ") " + loss_string
|
| 34 |
+
|
| 35 |
+
print(loss_string, file=self.log_file)
|
| 36 |
+
self.loss_list = []
|
| 37 |
+
self.log_file.flush()
|
| 38 |
+
|
| 39 |
+
def visualize_rec(self, inp, out):
|
| 40 |
+
# image = self.visualizer.visualize(inp['driving'], inp['source'], out)
|
| 41 |
+
image = self.visualizer.visualize(inp['driving'][:,-1], inp['transformed_driving'][:,-1], inp['example_image'], out)
|
| 42 |
+
imageio.imsave(os.path.join(self.visualizations_dir, "%s-%s-rec.png" % (str(self.epoch),str(self.step).zfill(self.zfill_num))), image)
|
| 43 |
+
|
| 44 |
+
def save_cpk(self, emergent=False):
|
| 45 |
+
cpk = {k: v.state_dict() for k, v in self.models.items()}
|
| 46 |
+
cpk['epoch'] = self.epoch
|
| 47 |
+
cpk['step'] = self.step
|
| 48 |
+
cpk_path = os.path.join(self.cpk_dir, '%s-%s-checkpoint.pth.tar' % (str(self.epoch),str(self.step).zfill(self.zfill_num)))
|
| 49 |
+
if not (os.path.exists(cpk_path) and emergent):
|
| 50 |
+
torch.save(cpk, cpk_path)
|
| 51 |
+
|
| 52 |
+
@staticmethod
|
| 53 |
+
def load_cpk(checkpoint_path, generator=None, discriminator=None, kp_detector=None, audio_feature=None,
|
| 54 |
+
optimizer_generator=None, optimizer_discriminator=None, optimizer_kp_detector=None, optimizer_audio_feature = None):
|
| 55 |
+
checkpoint = torch.load(checkpoint_path)
|
| 56 |
+
if generator is not None:
|
| 57 |
+
generator.load_state_dict(checkpoint['generator'])
|
| 58 |
+
if kp_detector is not None:
|
| 59 |
+
kp_detector.load_state_dict(checkpoint['kp_detector'])
|
| 60 |
+
if discriminator is not None:
|
| 61 |
+
try:
|
| 62 |
+
discriminator.load_state_dict(checkpoint['discriminator'])
|
| 63 |
+
except:
|
| 64 |
+
print ('No discriminator in the state-dict. Dicriminator will be randomly initialized')
|
| 65 |
+
# if audio_feature is not None:
|
| 66 |
+
# audio_feature.load_state_dict(checkpoint['audio_feature'])
|
| 67 |
+
if optimizer_generator is not None:
|
| 68 |
+
optimizer_generator.load_state_dict(checkpoint['optimizer_generator'])
|
| 69 |
+
if optimizer_discriminator is not None:
|
| 70 |
+
try:
|
| 71 |
+
optimizer_discriminator.load_state_dict(checkpoint['optimizer_discriminator'])
|
| 72 |
+
except RuntimeError as e:
|
| 73 |
+
print ('No discriminator optimizer in the state-dict. Optimizer will be not initialized')
|
| 74 |
+
if optimizer_kp_detector is not None:
|
| 75 |
+
optimizer_kp_detector.load_state_dict(checkpoint['optimizer_kp_detector'])
|
| 76 |
+
# if optimizer_audio_feature is not None:
|
| 77 |
+
# a = checkpoint['optimizer_kp_detector']['param_groups']
|
| 78 |
+
# a[0].pop('params')
|
| 79 |
+
# optimizer_audio_feature.load_state_dict(checkpoint['optimizer_audio_feature'])
|
| 80 |
+
|
| 81 |
+
return checkpoint['epoch']
|
| 82 |
+
|
| 83 |
+
def __enter__(self):
|
| 84 |
+
return self
|
| 85 |
+
|
| 86 |
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
| 87 |
+
if 'models' in self.__dict__:
|
| 88 |
+
self.save_cpk()
|
| 89 |
+
self.log_file.close()
|
| 90 |
+
|
| 91 |
+
def log_iter(self, losses):
|
| 92 |
+
losses = collections.OrderedDict(losses.items())
|
| 93 |
+
if self.names is None:
|
| 94 |
+
self.names = list(losses.keys())
|
| 95 |
+
self.loss_list.append(list(losses.values()))
|
| 96 |
+
|
| 97 |
+
def log_epoch(self, epoch, step, models, inp, out):
|
| 98 |
+
self.epoch = epoch
|
| 99 |
+
self.step = step
|
| 100 |
+
self.models = models
|
| 101 |
+
if (self.epoch + 1) % self.checkpoint_freq == 0:
|
| 102 |
+
self.save_cpk()
|
| 103 |
+
self.log_scores(self.names)
|
| 104 |
+
self.visualize_rec(inp, out)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class Visualizer:
|
| 108 |
+
def __init__(self, kp_size=5, draw_border=False, colormap='gist_rainbow'):
|
| 109 |
+
self.kp_size = kp_size
|
| 110 |
+
self.draw_border = draw_border
|
| 111 |
+
self.colormap = plt.get_cmap(colormap)
|
| 112 |
+
|
| 113 |
+
def draw_image_with_kp(self, image, kp_array):
|
| 114 |
+
image = np.copy(image)
|
| 115 |
+
spatial_size = np.array(image.shape[:2][::-1])[np.newaxis]
|
| 116 |
+
kp_array = spatial_size * (kp_array + 1) / 2
|
| 117 |
+
num_kp = kp_array.shape[0]
|
| 118 |
+
for kp_ind, kp in enumerate(kp_array):
|
| 119 |
+
rr, cc = circle(kp[1], kp[0], self.kp_size, shape=image.shape[:2])
|
| 120 |
+
image[rr, cc] = np.array(self.colormap(kp_ind / num_kp))[:3]
|
| 121 |
+
return image
|
| 122 |
+
|
| 123 |
+
def create_image_column_with_kp(self, images, kp):
|
| 124 |
+
image_array = np.array([self.draw_image_with_kp(v, k) for v, k in zip(images, kp)])
|
| 125 |
+
return self.create_image_column(image_array)
|
| 126 |
+
|
| 127 |
+
def create_image_column(self, images):
|
| 128 |
+
if self.draw_border:
|
| 129 |
+
images = np.copy(images)
|
| 130 |
+
images[:, :, [0, -1]] = (1, 1, 1)
|
| 131 |
+
images[:, :, [0, -1]] = (1, 1, 1)
|
| 132 |
+
return np.concatenate(list(images), axis=0)
|
| 133 |
+
|
| 134 |
+
def create_image_grid(self, *args):
|
| 135 |
+
out = []
|
| 136 |
+
for arg in args:
|
| 137 |
+
if type(arg) == tuple:
|
| 138 |
+
out.append(self.create_image_column_with_kp(arg[0], arg[1]))
|
| 139 |
+
else:
|
| 140 |
+
out.append(self.create_image_column(arg))
|
| 141 |
+
return np.concatenate(out, axis=1)
|
| 142 |
+
|
| 143 |
+
def visualize(self, driving, transformed_driving, source, out):
|
| 144 |
+
images = []
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
# Source image with keypoints
|
| 148 |
+
source = source.data.cpu()
|
| 149 |
+
kp_source = out['kp_source']['value'].data.cpu().numpy()
|
| 150 |
+
source = np.transpose(source, [0, 2, 3, 1])
|
| 151 |
+
images.append((source, kp_source))
|
| 152 |
+
|
| 153 |
+
# Equivariance visualization
|
| 154 |
+
if 'transformed_frame' in out:
|
| 155 |
+
transformed = out['transformed_frame'].data.cpu().numpy()
|
| 156 |
+
transformed = np.transpose(transformed, [0, 2, 3, 1])
|
| 157 |
+
transformed_kp = out['transformed_kp']['value'].data.cpu().numpy()
|
| 158 |
+
images.append((transformed, transformed_kp))
|
| 159 |
+
|
| 160 |
+
# Equivariance visualization
|
| 161 |
+
transformed_driving = transformed_driving.data.cpu().numpy()
|
| 162 |
+
transformed_driving = np.transpose(transformed_driving, [0, 2, 3, 1])
|
| 163 |
+
images.append(transformed_driving)
|
| 164 |
+
|
| 165 |
+
# Driving image with keypoints
|
| 166 |
+
kp_driving = out['kp_driving'][-1]['value'].data.cpu().numpy() #[-1]['value']
|
| 167 |
+
driving = driving.data.cpu().numpy()
|
| 168 |
+
driving = np.transpose(driving, [0, 2, 3, 1])
|
| 169 |
+
images.append((driving, kp_driving))
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
# Deformed image
|
| 174 |
+
if 'deformed' in out:
|
| 175 |
+
deformed = out['deformed'].data.cpu().numpy()
|
| 176 |
+
deformed = np.transpose(deformed, [0, 2, 3, 1])
|
| 177 |
+
images.append(deformed)
|
| 178 |
+
|
| 179 |
+
# Result with and without keypoints
|
| 180 |
+
prediction = out['prediction'].data.cpu().numpy()
|
| 181 |
+
prediction = np.transpose(prediction, [0, 2, 3, 1])
|
| 182 |
+
if 'kp_norm' in out:
|
| 183 |
+
kp_norm = out['kp_norm']['value'].data.cpu().numpy()
|
| 184 |
+
images.append((prediction, kp_norm))
|
| 185 |
+
images.append(prediction)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
## Occlusion map
|
| 189 |
+
if 'occlusion_map' in out:
|
| 190 |
+
occlusion_map = out['occlusion_map'].data.cpu().repeat(1, 3, 1, 1)
|
| 191 |
+
occlusion_map = F.interpolate(occlusion_map, size=source.shape[1:3]).numpy()
|
| 192 |
+
occlusion_map = np.transpose(occlusion_map, [0, 2, 3, 1])
|
| 193 |
+
images.append(occlusion_map)
|
| 194 |
+
|
| 195 |
+
# Deformed images according to each individual transform
|
| 196 |
+
if 'sparse_deformed' in out:
|
| 197 |
+
full_mask = []
|
| 198 |
+
for i in range(out['sparse_deformed'].shape[1]):
|
| 199 |
+
image = out['sparse_deformed'][:, i].data.cpu()
|
| 200 |
+
image = F.interpolate(image, size=source.shape[1:3])
|
| 201 |
+
mask = out['mask'][:, i:(i+1)].data.cpu().repeat(1, 3, 1, 1)
|
| 202 |
+
mask = F.interpolate(mask, size=source.shape[1:3])
|
| 203 |
+
image = np.transpose(image.numpy(), (0, 2, 3, 1))
|
| 204 |
+
mask = np.transpose(mask.numpy(), (0, 2, 3, 1))
|
| 205 |
+
|
| 206 |
+
if i != 0:
|
| 207 |
+
color = np.array(self.colormap((i - 1) / (out['sparse_deformed'].shape[1] - 1)))[:3]
|
| 208 |
+
else:
|
| 209 |
+
color = np.array((0, 0, 0))
|
| 210 |
+
|
| 211 |
+
color = color.reshape((1, 1, 1, 3))
|
| 212 |
+
|
| 213 |
+
images.append(image)
|
| 214 |
+
if i != 0:
|
| 215 |
+
images.append(mask * color)
|
| 216 |
+
else:
|
| 217 |
+
images.append(mask)
|
| 218 |
+
|
| 219 |
+
full_mask.append(mask * color)
|
| 220 |
+
|
| 221 |
+
images.append(sum(full_mask))
|
| 222 |
+
|
| 223 |
+
image = self.create_image_grid(*images)
|
| 224 |
+
image = (255 * image).astype(np.uint8)
|
| 225 |
+
return image
|
FONT/modules/conv.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
from torch.nn import functional as F
|
| 4 |
+
|
| 5 |
+
class Conv2d(nn.Module):
|
| 6 |
+
def __init__(self, cin, cout, kernel_size, stride, padding, residual=False, *args, **kwargs):
|
| 7 |
+
super().__init__(*args, **kwargs)
|
| 8 |
+
self.conv_block = nn.Sequential(
|
| 9 |
+
nn.Conv2d(cin, cout, kernel_size, stride, padding),
|
| 10 |
+
nn.BatchNorm2d(cout)
|
| 11 |
+
)
|
| 12 |
+
self.act = nn.ReLU()
|
| 13 |
+
self.residual = residual
|
| 14 |
+
|
| 15 |
+
def forward(self, x):
|
| 16 |
+
out = self.conv_block(x)
|
| 17 |
+
if self.residual:
|
| 18 |
+
out += x
|
| 19 |
+
return self.act(out)
|
| 20 |
+
|
| 21 |
+
class nonorm_Conv2d(nn.Module):
|
| 22 |
+
def __init__(self, cin, cout, kernel_size, stride, padding, residual=False, *args, **kwargs):
|
| 23 |
+
super().__init__(*args, **kwargs)
|
| 24 |
+
self.conv_block = nn.Sequential(
|
| 25 |
+
nn.Conv2d(cin, cout, kernel_size, stride, padding),
|
| 26 |
+
)
|
| 27 |
+
self.act = nn.LeakyReLU(0.01, inplace=True)
|
| 28 |
+
|
| 29 |
+
def forward(self, x):
|
| 30 |
+
out = self.conv_block(x)
|
| 31 |
+
return self.act(out)
|
| 32 |
+
|
| 33 |
+
class Conv2dTranspose(nn.Module):
|
| 34 |
+
def __init__(self, cin, cout, kernel_size, stride, padding, output_padding=0, *args, **kwargs):
|
| 35 |
+
super().__init__(*args, **kwargs)
|
| 36 |
+
self.conv_block = nn.Sequential(
|
| 37 |
+
nn.ConvTranspose2d(cin, cout, kernel_size, stride, padding, output_padding),
|
| 38 |
+
nn.BatchNorm2d(cout)
|
| 39 |
+
)
|
| 40 |
+
self.act = nn.ReLU()
|
| 41 |
+
|
| 42 |
+
def forward(self, x):
|
| 43 |
+
out = self.conv_block(x)
|
| 44 |
+
return self.act(out)
|
FONT/modules/dense_motion.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch.nn.functional as F
|
| 3 |
+
import torch
|
| 4 |
+
from .util import Hourglass, AntiAliasInterpolation2d, make_coordinate_grid, kp2gaussian
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class DenseMotionNetwork(nn.Module):
|
| 8 |
+
"""
|
| 9 |
+
Module that predicting a dense motion from sparse motion representation given by kp_source and kp_driving
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def __init__(self, block_expansion, num_blocks, max_features, num_kp, num_channels, estimate_occlusion_map=False,
|
| 13 |
+
scale_factor=1, kp_variance=0.01):
|
| 14 |
+
super(DenseMotionNetwork, self).__init__()
|
| 15 |
+
self.hourglass = Hourglass(block_expansion=block_expansion, in_features=(num_kp + 1) * (num_channels + 1),
|
| 16 |
+
max_features=max_features, num_blocks=num_blocks)
|
| 17 |
+
|
| 18 |
+
self.mask = nn.Conv2d(self.hourglass.out_filters, num_kp + 1, kernel_size=(7, 7), padding=(3, 3))
|
| 19 |
+
|
| 20 |
+
if estimate_occlusion_map:
|
| 21 |
+
self.occlusion = nn.Conv2d(self.hourglass.out_filters, 1, kernel_size=(7, 7), padding=(3, 3))
|
| 22 |
+
else:
|
| 23 |
+
self.occlusion = None
|
| 24 |
+
|
| 25 |
+
self.num_kp = num_kp
|
| 26 |
+
self.scale_factor = scale_factor
|
| 27 |
+
self.kp_variance = kp_variance
|
| 28 |
+
|
| 29 |
+
if self.scale_factor != 1:
|
| 30 |
+
self.down = AntiAliasInterpolation2d(num_channels, self.scale_factor)
|
| 31 |
+
|
| 32 |
+
def create_heatmap_representations(self, source_image, kp_driving, kp_source):
|
| 33 |
+
"""
|
| 34 |
+
Eq 6. in the paper H_k(z)
|
| 35 |
+
"""
|
| 36 |
+
spatial_size = source_image.shape[2:]
|
| 37 |
+
gaussian_driving = kp2gaussian(kp_driving, spatial_size=spatial_size, kp_variance=self.kp_variance)
|
| 38 |
+
gaussian_source = kp2gaussian(kp_source, spatial_size=spatial_size, kp_variance=self.kp_variance)
|
| 39 |
+
heatmap = gaussian_driving - gaussian_source #[4,10,H,W]
|
| 40 |
+
|
| 41 |
+
#adding background feature
|
| 42 |
+
zeros = torch.zeros(heatmap.shape[0], 1, spatial_size[0], spatial_size[1]).type(heatmap.type())
|
| 43 |
+
heatmap = torch.cat([zeros, heatmap], dim=1)
|
| 44 |
+
heatmap = heatmap.unsqueeze(2) #[4,11,1,h,w]
|
| 45 |
+
return heatmap
|
| 46 |
+
|
| 47 |
+
def create_sparse_motions(self, source_image, kp_driving, kp_source):
|
| 48 |
+
"""
|
| 49 |
+
Eq 4. in the paper T_{s<-d}(z)
|
| 50 |
+
"""
|
| 51 |
+
bs, _, h, w = source_image.shape
|
| 52 |
+
identity_grid = make_coordinate_grid((h, w), type=kp_source['value'].type())
|
| 53 |
+
identity_grid = identity_grid.view(1, 1, h, w, 2)
|
| 54 |
+
coordinate_grid = identity_grid - kp_driving['value'].view(bs, self.num_kp, 1, 1, 2) #[4,10,64,64,2]
|
| 55 |
+
if 'jacobian' in kp_driving:
|
| 56 |
+
jacobian = torch.matmul(kp_source['jacobian'], torch.inverse(kp_driving['jacobian']))
|
| 57 |
+
jacobian = jacobian.unsqueeze(-3).unsqueeze(-3)
|
| 58 |
+
jacobian = jacobian.repeat(1, 1, h, w, 1, 1)
|
| 59 |
+
coordinate_grid = torch.matmul(jacobian, coordinate_grid.unsqueeze(-1))
|
| 60 |
+
coordinate_grid = coordinate_grid.squeeze(-1)
|
| 61 |
+
|
| 62 |
+
driving_to_source = coordinate_grid + kp_source['value'].view(bs, self.num_kp, 1, 1, 2)
|
| 63 |
+
|
| 64 |
+
#adding background feature
|
| 65 |
+
identity_grid = identity_grid.repeat(bs, 1, 1, 1, 1)
|
| 66 |
+
sparse_motions = torch.cat([identity_grid, driving_to_source], dim=1)
|
| 67 |
+
return sparse_motions
|
| 68 |
+
|
| 69 |
+
def create_deformed_source_image(self, source_image, sparse_motions):
|
| 70 |
+
"""
|
| 71 |
+
Eq 7. in the paper \hat{T}_{s<-d}(z)
|
| 72 |
+
"""
|
| 73 |
+
bs, _, h, w = source_image.shape
|
| 74 |
+
source_repeat = source_image.unsqueeze(1).unsqueeze(1).repeat(1, self.num_kp + 1, 1, 1, 1, 1)
|
| 75 |
+
source_repeat = source_repeat.view(bs * (self.num_kp + 1), -1, h, w)
|
| 76 |
+
sparse_motions = sparse_motions.view((bs * (self.num_kp + 1), h, w, -1))
|
| 77 |
+
sparse_deformed = F.grid_sample(source_repeat, sparse_motions)
|
| 78 |
+
sparse_deformed = sparse_deformed.view((bs, self.num_kp + 1, -1, h, w))
|
| 79 |
+
return sparse_deformed
|
| 80 |
+
|
| 81 |
+
def forward(self, source_image, kp_driving, kp_source):
|
| 82 |
+
if self.scale_factor != 1:
|
| 83 |
+
source_image = self.down(source_image) #[4,3,H*scale,W*scale]
|
| 84 |
+
|
| 85 |
+
bs, _, h, w = source_image.shape
|
| 86 |
+
|
| 87 |
+
out_dict = dict()
|
| 88 |
+
heatmap_representation = self.create_heatmap_representations(source_image, kp_driving, kp_source) #[4,11,1,64,64]
|
| 89 |
+
sparse_motion = self.create_sparse_motions(source_image, kp_driving, kp_source) #[4,11,64,64,2]
|
| 90 |
+
deformed_source = self.create_deformed_source_image(source_image, sparse_motion) #[4,11,3,64,64]
|
| 91 |
+
out_dict['sparse_deformed'] = deformed_source
|
| 92 |
+
|
| 93 |
+
input = torch.cat([heatmap_representation, deformed_source], dim=2)
|
| 94 |
+
input = input.view(bs, -1, h, w) #[4,11*4,64,64]
|
| 95 |
+
|
| 96 |
+
prediction = self.hourglass(input) #[4,108,64,64]
|
| 97 |
+
|
| 98 |
+
mask = self.mask(prediction)
|
| 99 |
+
mask = F.softmax(mask, dim=1) #[4,11,64,64]
|
| 100 |
+
out_dict['mask'] = mask
|
| 101 |
+
mask = mask.unsqueeze(2)
|
| 102 |
+
sparse_motion = sparse_motion.permute(0, 1, 4, 2, 3)
|
| 103 |
+
deformation = (sparse_motion * mask).sum(dim=1)
|
| 104 |
+
deformation = deformation.permute(0, 2, 3, 1) #[4,64,64,2]
|
| 105 |
+
|
| 106 |
+
out_dict['deformation'] = deformation
|
| 107 |
+
|
| 108 |
+
# Sec. 3.2 in the paper
|
| 109 |
+
if self.occlusion:
|
| 110 |
+
occlusion_map = torch.sigmoid(self.occlusion(prediction))
|
| 111 |
+
out_dict['occlusion_map'] = occlusion_map #[4,1,64,64]
|
| 112 |
+
|
| 113 |
+
return out_dict
|
FONT/modules/discriminator.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch.nn.functional as F
|
| 3 |
+
from modules.util import kp2gaussian
|
| 4 |
+
import torch
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class DownBlock2d(nn.Module):
|
| 8 |
+
"""
|
| 9 |
+
Simple block for processing video (encoder).
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def __init__(self, in_features, out_features, norm=False, kernel_size=4, pool=False, sn=False):
|
| 13 |
+
super(DownBlock2d, self).__init__()
|
| 14 |
+
self.conv = nn.Conv2d(in_channels=in_features, out_channels=out_features, kernel_size=kernel_size)
|
| 15 |
+
|
| 16 |
+
if sn:
|
| 17 |
+
self.conv = nn.utils.spectral_norm(self.conv)
|
| 18 |
+
|
| 19 |
+
if norm:
|
| 20 |
+
self.norm = nn.InstanceNorm2d(out_features, affine=True)
|
| 21 |
+
else:
|
| 22 |
+
self.norm = None
|
| 23 |
+
self.pool = pool
|
| 24 |
+
|
| 25 |
+
def forward(self, x):
|
| 26 |
+
out = x
|
| 27 |
+
out = self.conv(out)
|
| 28 |
+
if self.norm:
|
| 29 |
+
out = self.norm(out)
|
| 30 |
+
out = F.leaky_relu(out, 0.2)
|
| 31 |
+
if self.pool:
|
| 32 |
+
out = F.avg_pool2d(out, (2, 2))
|
| 33 |
+
return out
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class Discriminator(nn.Module):
|
| 37 |
+
"""
|
| 38 |
+
Discriminator similar to Pix2Pix
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
def __init__(self, num_channels=3, block_expansion=64, num_blocks=4, max_features=512,
|
| 42 |
+
sn=False, use_kp=False, num_kp=10, kp_variance=0.01, **kwargs):
|
| 43 |
+
super(Discriminator, self).__init__()
|
| 44 |
+
|
| 45 |
+
down_blocks = []
|
| 46 |
+
for i in range(num_blocks):
|
| 47 |
+
down_blocks.append(
|
| 48 |
+
DownBlock2d(num_channels + num_kp * use_kp if i == 0 else min(max_features, block_expansion * (2 ** i)),
|
| 49 |
+
min(max_features, block_expansion * (2 ** (i + 1))),
|
| 50 |
+
norm=(i != 0), kernel_size=4, pool=(i != num_blocks - 1), sn=sn))
|
| 51 |
+
|
| 52 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 53 |
+
self.conv = nn.Conv2d(self.down_blocks[-1].conv.out_channels, out_channels=1, kernel_size=1)
|
| 54 |
+
if sn:
|
| 55 |
+
self.conv = nn.utils.spectral_norm(self.conv)
|
| 56 |
+
self.use_kp = use_kp
|
| 57 |
+
self.kp_variance = kp_variance
|
| 58 |
+
|
| 59 |
+
def forward(self, x, kp=None):
|
| 60 |
+
feature_maps = []
|
| 61 |
+
out = x
|
| 62 |
+
if self.use_kp:
|
| 63 |
+
heatmap = kp2gaussian(kp, x.shape[2:], self.kp_variance)
|
| 64 |
+
out = torch.cat([out, heatmap], dim=1)
|
| 65 |
+
|
| 66 |
+
for down_block in self.down_blocks:
|
| 67 |
+
feature_maps.append(down_block(out))
|
| 68 |
+
out = feature_maps[-1]
|
| 69 |
+
prediction_map = self.conv(out)
|
| 70 |
+
|
| 71 |
+
return feature_maps, prediction_map
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class MultiScaleDiscriminator(nn.Module):
|
| 75 |
+
"""
|
| 76 |
+
Multi-scale (scale) discriminator
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
def __init__(self, scales=(), **kwargs):
|
| 80 |
+
super(MultiScaleDiscriminator, self).__init__()
|
| 81 |
+
self.scales = scales
|
| 82 |
+
discs = {}
|
| 83 |
+
for scale in scales:
|
| 84 |
+
discs[str(scale).replace('.', '-')] = Discriminator(**kwargs)
|
| 85 |
+
self.discs = nn.ModuleDict(discs)
|
| 86 |
+
|
| 87 |
+
def forward(self, x, kp=None):
|
| 88 |
+
out_dict = {}
|
| 89 |
+
for scale, disc in self.discs.items():
|
| 90 |
+
scale = str(scale).replace('-', '.')
|
| 91 |
+
key = 'prediction_' + scale
|
| 92 |
+
feature_maps, prediction_map = disc(x[key], kp)
|
| 93 |
+
out_dict['feature_maps_' + scale] = feature_maps
|
| 94 |
+
out_dict['prediction_map_' + scale] = prediction_map
|
| 95 |
+
return out_dict
|
FONT/modules/frames_dataset.py
ADDED
|
@@ -0,0 +1,451 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from skimage import io, img_as_float32, transform
|
| 3 |
+
from skimage.color import gray2rgb
|
| 4 |
+
from sklearn.model_selection import train_test_split
|
| 5 |
+
from imageio import mimread
|
| 6 |
+
|
| 7 |
+
import numpy as np
|
| 8 |
+
from torch.utils.data import Dataset
|
| 9 |
+
import pandas as pd
|
| 10 |
+
from augmentation import AllAugmentationTransform
|
| 11 |
+
import glob
|
| 12 |
+
import pickle
|
| 13 |
+
import random
|
| 14 |
+
def read_video(name, frame_shape):
|
| 15 |
+
"""
|
| 16 |
+
Read video which can be:
|
| 17 |
+
- an image of concatenated frames
|
| 18 |
+
- '.mp4' and'.gif'
|
| 19 |
+
- folder with videos
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
if os.path.isdir(name):
|
| 23 |
+
frames = sorted(os.listdir(name))
|
| 24 |
+
num_frames = len(frames)
|
| 25 |
+
video_array = np.array(
|
| 26 |
+
[img_as_float32(io.imread(os.path.join(name, frames[idx]))) for idx in range(num_frames)])
|
| 27 |
+
elif name.lower().endswith('.png') or name.lower().endswith('.jpg'):
|
| 28 |
+
image = io.imread(name)
|
| 29 |
+
|
| 30 |
+
if len(image.shape) == 2 or image.shape[2] == 1:
|
| 31 |
+
image = gray2rgb(image)
|
| 32 |
+
|
| 33 |
+
if image.shape[2] == 4:
|
| 34 |
+
image = image[..., :3]
|
| 35 |
+
|
| 36 |
+
image = img_as_float32(image)
|
| 37 |
+
|
| 38 |
+
video_array = np.moveaxis(image, 1, 0)
|
| 39 |
+
|
| 40 |
+
video_array = video_array.reshape((-1,) + frame_shape)
|
| 41 |
+
video_array = np.moveaxis(video_array, 1, 2)
|
| 42 |
+
elif name.lower().endswith('.gif') or name.lower().endswith('.mp4') or name.lower().endswith('.mov'):
|
| 43 |
+
video = np.array(mimread(name))
|
| 44 |
+
if len(video.shape) == 3:
|
| 45 |
+
video = np.array([gray2rgb(frame) for frame in video])
|
| 46 |
+
if video.shape[-1] == 4:
|
| 47 |
+
video = video[..., :3]
|
| 48 |
+
video_array = img_as_float32(video)
|
| 49 |
+
else:
|
| 50 |
+
raise Exception("Unknown file extensions %s" % name)
|
| 51 |
+
|
| 52 |
+
return video_array
|
| 53 |
+
|
| 54 |
+
def get_list(ipath,base_name):
|
| 55 |
+
#ipath = '/mnt/lustre/share/jixinya/LRW/pose/train_fo/'
|
| 56 |
+
ipath = os.path.join(ipath,base_name)
|
| 57 |
+
name_list = os.listdir(ipath)
|
| 58 |
+
image_path = os.path.join('/mnt/lustre/share/jixinya/LRW/Image/',base_name)
|
| 59 |
+
all = []
|
| 60 |
+
for k in range(len(name_list)):
|
| 61 |
+
name = name_list[k]
|
| 62 |
+
path_ = os.path.join(ipath,name)
|
| 63 |
+
Dir = os.listdir(path_)
|
| 64 |
+
for i in range(len(Dir)):
|
| 65 |
+
word = Dir[i]
|
| 66 |
+
path = os.path.join(path_, word)
|
| 67 |
+
if os.path.exists(os.path.join(image_path,name,word.split('.')[0])):
|
| 68 |
+
all.append(name+'/'+word.split('.')[0])
|
| 69 |
+
#print(k,name,i,word)
|
| 70 |
+
print('get list '+os.path.basename(ipath))
|
| 71 |
+
return all
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
class AudioDataset(Dataset):
|
| 75 |
+
"""
|
| 76 |
+
Dataset of videos, each video can be represented as:
|
| 77 |
+
- an image of concatenated frames
|
| 78 |
+
- '.mp4' or '.gif'
|
| 79 |
+
- folder with all frames
|
| 80 |
+
"""
|
| 81 |
+
|
| 82 |
+
def __init__(self, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 83 |
+
random_seed=0, pairs_list=None, augmentation_params=None):
|
| 84 |
+
self.root_dir = root_dir
|
| 85 |
+
self.audio_dir = os.path.join(root_dir,'MFCC')
|
| 86 |
+
self.image_dir = os.path.join(root_dir,'Image')
|
| 87 |
+
self.landmark_dir = os.path.join(root_dir,'Landmark')
|
| 88 |
+
self.pose_dir = os.path.join(root_dir,'pose')
|
| 89 |
+
# assert len(os.listdir(self.audio_dir)) == len(os.listdir(self.image_dir)), 'audio and image length not equal'
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
df=open('../LRW/list/test_fo.txt','rb')
|
| 93 |
+
self.videos=pickle.load(df)
|
| 94 |
+
df.close()
|
| 95 |
+
# self.videos=np.load('../LRW/list/train_fo.npy')
|
| 96 |
+
# self.videos = os.listdir(self.landmark_dir)
|
| 97 |
+
self.frame_shape = tuple(frame_shape)
|
| 98 |
+
self.pairs_list = pairs_list
|
| 99 |
+
self.id_sampling = id_sampling
|
| 100 |
+
self.pca = np.load('../LRW/list/U_106.npy')[:, :16]
|
| 101 |
+
self.mean = np.load('../LRW/list/mean_106.npy')
|
| 102 |
+
|
| 103 |
+
if os.path.exists(os.path.join(self.pose_dir, 'train_fo')):
|
| 104 |
+
assert os.path.exists(os.path.join(self.pose_dir, 'test_fo'))
|
| 105 |
+
print("Use predefined train-test split.")
|
| 106 |
+
if id_sampling:
|
| 107 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 108 |
+
os.listdir(os.path.join(self.image_dir, 'train'))}
|
| 109 |
+
train_videos = list(train_videos)
|
| 110 |
+
else:
|
| 111 |
+
train_videos = np.load('../LRW/list/train_fo.npy')# get_list(self.pose_dir, 'train_fo')
|
| 112 |
+
df=open('../LRW/list/test_fo.txt','rb')
|
| 113 |
+
test_videos=pickle.load(df)
|
| 114 |
+
df.close()
|
| 115 |
+
# test_videos = np.load('../LRW/list/train_fo.npy')
|
| 116 |
+
#get_list(self.pose_dir, 'test_fo')
|
| 117 |
+
# self.root_dir = os.path.join(self.root_dir, 'train' if is_train else 'test')
|
| 118 |
+
self.landmark_dir = os.path.join(self.landmark_dir, 'train_fo' if is_train else 'test_fo')
|
| 119 |
+
self.image_dir = os.path.join(self.image_dir, 'train_fo' if is_train else 'test_fo')
|
| 120 |
+
self.audio_dir = os.path.join(self.audio_dir, 'train' if is_train else 'test')
|
| 121 |
+
self.pose_dir = os.path.join(self.pose_dir, 'train_fo' if is_train else 'test_fo')
|
| 122 |
+
else:
|
| 123 |
+
print("Use random train-test split.")
|
| 124 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 125 |
+
|
| 126 |
+
if is_train:
|
| 127 |
+
self.videos = train_videos
|
| 128 |
+
else:
|
| 129 |
+
self.videos = test_videos
|
| 130 |
+
|
| 131 |
+
self.is_train = is_train
|
| 132 |
+
|
| 133 |
+
if self.is_train:
|
| 134 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 135 |
+
else:
|
| 136 |
+
self.transform = None
|
| 137 |
+
|
| 138 |
+
def __len__(self):
|
| 139 |
+
return len(self.videos)
|
| 140 |
+
|
| 141 |
+
def __getitem__(self, idx):
|
| 142 |
+
if self.is_train and self.id_sampling:
|
| 143 |
+
name = self.videos[idx].split('.')[0]
|
| 144 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 145 |
+
else:
|
| 146 |
+
name = self.videos[idx].split('.')[0]
|
| 147 |
+
landmark_path = os.path.join(self.landmark_dir, name+'.npy')
|
| 148 |
+
|
| 149 |
+
audio_path = os.path.join(self.audio_dir, name)
|
| 150 |
+
pose_path = os.path.join(self.pose_dir,name)
|
| 151 |
+
path = os.path.join(self.image_dir, name)
|
| 152 |
+
|
| 153 |
+
video_name = os.path.basename(path)
|
| 154 |
+
|
| 155 |
+
if os.path.isdir(path):
|
| 156 |
+
# if self.is_train and os.path.isdir(path):
|
| 157 |
+
|
| 158 |
+
lmark = np.load(landmark_path).reshape(-1,212)/255
|
| 159 |
+
if np.isnan(lmark).sum() or np.isinf(lmark).sum():
|
| 160 |
+
print('Wrong lmark '+ video_name, file=open('log/wrong.txt', 'a'))
|
| 161 |
+
lmark = np.zeros((29,212))
|
| 162 |
+
lmark = lmark - self.mean
|
| 163 |
+
lmark = np.dot(lmark, self.pca)
|
| 164 |
+
|
| 165 |
+
# mfcc loading
|
| 166 |
+
|
| 167 |
+
r = random.choice([x for x in range(3, 8)])
|
| 168 |
+
example_landmark = lmark[r, :]
|
| 169 |
+
example_image = img_as_float32(io.imread(os.path.join(path, str(r)+'.png')))
|
| 170 |
+
# example_mfcc = mfcc[(r - 3) * 4: (r + 4) * 4, 1:]
|
| 171 |
+
|
| 172 |
+
mfccs = []
|
| 173 |
+
for ind in range(1, 17):
|
| 174 |
+
# t_mfcc = mfcc[(r + ind - 3) * 4: (r + ind + 4) * 4, 1:]
|
| 175 |
+
try:
|
| 176 |
+
t_mfcc = np.load(os.path.join(audio_path,str(r + ind)+'.npy'),allow_pickle=True)[:, 1:]
|
| 177 |
+
if np.isnan(t_mfcc).sum() or np.isinf(t_mfcc).sum():
|
| 178 |
+
print('Wrong mfcc '+ video_name+str(r+ind), file=open('log/wrong.txt', 'a'))
|
| 179 |
+
t_mfcc = np.zeros((28,13))[:,1:]
|
| 180 |
+
except:
|
| 181 |
+
t_mfcc = np.zeros((28,13))[:,1:]
|
| 182 |
+
mfccs.append(t_mfcc)
|
| 183 |
+
mfccs = np.array(mfccs)
|
| 184 |
+
if not self.is_train:
|
| 185 |
+
poses = []
|
| 186 |
+
video_array = []
|
| 187 |
+
for ind in range(1, 17):
|
| 188 |
+
# t_mfcc = mfcc[(r + ind - 3) * 4: (r + ind + 4) * 4, 1:]
|
| 189 |
+
t_pose = np.load(os.path.join(pose_path,str(r + ind)+'.npy'))[:-1]
|
| 190 |
+
poses.append(t_pose)
|
| 191 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 192 |
+
video_array.append(image)
|
| 193 |
+
poses = np.array(poses)
|
| 194 |
+
video_array = np.array(video_array)
|
| 195 |
+
else:
|
| 196 |
+
poses = []
|
| 197 |
+
video_array = []
|
| 198 |
+
for ind in range(1, 17):
|
| 199 |
+
# t_mfcc = mfcc[(r + ind - 3) * 4: (r + ind + 4) * 4, 1:]
|
| 200 |
+
t_pose = np.load(os.path.join(self.pose_dir,name+'.npy'))[r+ind,:-1]
|
| 201 |
+
if np.isnan(t_pose).sum() or np.isinf(t_pose).sum():
|
| 202 |
+
print('Wrong pose '+ video_name, file=open('log/wrong.txt', 'a'))
|
| 203 |
+
t_pose = np.zeros((6,))
|
| 204 |
+
poses.append(t_pose)
|
| 205 |
+
image = img_as_float32(io.imread(os.path.join(path, str(r + ind)+'.png')))
|
| 206 |
+
video_array.append(image)
|
| 207 |
+
poses = np.array(poses)
|
| 208 |
+
video_array = np.array(video_array)
|
| 209 |
+
|
| 210 |
+
#mfccs = torch.FloatTensor(mfccs)
|
| 211 |
+
landmark = lmark[r + 1: r + 17, :]
|
| 212 |
+
index_32 = [0,4,8,12,16,20,24,28,32,33,35,67,68,40,42,52,55,72,73,58,61,75,76,46,47,51,84,87,90,93,98,102]
|
| 213 |
+
driving_landmark = np.load(landmark_path)[r + 1: r + 17, :][:,index_32]
|
| 214 |
+
source_landmark = np.load(landmark_path)[r, :][index_32]
|
| 215 |
+
else:
|
| 216 |
+
video_array = read_video(path, frame_shape=self.frame_shape)
|
| 217 |
+
num_frames = len(video_array)
|
| 218 |
+
frame_idx = np.sort(np.random.choice(num_frames, replace=True, size=2)) if self.is_train else range(
|
| 219 |
+
num_frames)
|
| 220 |
+
video_array = video_array[frame_idx]
|
| 221 |
+
|
| 222 |
+
if self.transform is not None:
|
| 223 |
+
video_array = self.transform(video_array)
|
| 224 |
+
|
| 225 |
+
out = {}
|
| 226 |
+
if True:#self.is_train:
|
| 227 |
+
# a = img_as_float32(io.imread('/media/thea/Data/first-order-model/images_512/102.jpg'))
|
| 228 |
+
# source = np.array(a, dtype='float32')
|
| 229 |
+
|
| 230 |
+
driving = np.array(video_array, dtype='float32')
|
| 231 |
+
|
| 232 |
+
spatial_size = np.array(driving.shape[1:3][::-1])[np.newaxis]
|
| 233 |
+
# example_landmark = np.array(2*example_landmark / spatial_size -1, dtype='float32')
|
| 234 |
+
driving_landmark = np.array(2*driving_landmark / spatial_size -1, dtype='float32')
|
| 235 |
+
source_landmark = np.array(2*source_landmark / spatial_size -1, dtype='float32')
|
| 236 |
+
driving_pose = np.array(poses, dtype='float32')
|
| 237 |
+
example_landmark = np.array(example_landmark, dtype='float32')
|
| 238 |
+
example_image = np.array(example_image, dtype='float32')
|
| 239 |
+
# source_cube = np.array(transform.resize(cube_array[0], (64,64)), dtype='float32')
|
| 240 |
+
# driving_cube = np.array(transform.resize(cube_array[1], (64,64)), dtype='float32')
|
| 241 |
+
# source_heatmap = np.array(heatmap_array[0] , dtype='float32')
|
| 242 |
+
# driving_heatmap = np.array(heatmap_array[1] , dtype='float32')
|
| 243 |
+
# out['source_cube'] = source_cube
|
| 244 |
+
# out['driving_cube'] = driving_cube
|
| 245 |
+
out['example_landmark'] = example_landmark
|
| 246 |
+
out['example_image'] = example_image.transpose((2, 0, 1))
|
| 247 |
+
out['driving_landmark'] = driving_landmark
|
| 248 |
+
out['source_landmark'] = source_landmark
|
| 249 |
+
out['driving_pose'] = driving_pose
|
| 250 |
+
# out['source_heatmap'] = source_heatmap
|
| 251 |
+
# out['driving_heatmap'] = driving_heatmap
|
| 252 |
+
out['driving'] = driving.transpose((0, 3, 1, 2))
|
| 253 |
+
# out['source'] = source.transpose((2, 0, 1))
|
| 254 |
+
|
| 255 |
+
# out['source_audio'] = np.array(audio_array[0], dtype='float32')
|
| 256 |
+
out['driving_audio'] = np.array(mfccs, dtype='float32')
|
| 257 |
+
out['gt_landmark'] = np.array(landmark, dtype='float32')
|
| 258 |
+
out['pca'] = np.array(self.pca, dtype='float32')
|
| 259 |
+
out['mean'] = np.array(self.mean, dtype='float32')
|
| 260 |
+
|
| 261 |
+
|
| 262 |
+
out['name'] = video_name
|
| 263 |
+
|
| 264 |
+
return out
|
| 265 |
+
|
| 266 |
+
class FramesDataset(Dataset):
|
| 267 |
+
"""
|
| 268 |
+
Dataset of videos, each video can be represented as:
|
| 269 |
+
- an image of concatenated frames
|
| 270 |
+
- '.mp4' or '.gif'
|
| 271 |
+
- folder with all frames
|
| 272 |
+
"""
|
| 273 |
+
|
| 274 |
+
def __init__(self, root_dir, frame_shape=(256, 256, 3), id_sampling=False, is_train=True,
|
| 275 |
+
random_seed=0, pairs_list=None, augmentation_params=None):
|
| 276 |
+
self.root_dir = root_dir
|
| 277 |
+
self.audio_dir = os.path.join(root_dir,'audio/')
|
| 278 |
+
self.image_dir = os.path.join(root_dir,'image/')
|
| 279 |
+
self.landmark_dir = os.path.join(root_dir,'cube/')
|
| 280 |
+
# assert len(os.listdir(self.audio_dir)) == len(os.listdir(self.image_dir)), 'audio and image length not equal'
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
df=open('/media/thea/新加卷/MEAD/neutral/train.txt','rb')
|
| 284 |
+
self.videos=pickle.load(df)
|
| 285 |
+
df.close()
|
| 286 |
+
# self.videos = os.listdir(self.landmark_dir)
|
| 287 |
+
self.frame_shape = tuple(frame_shape)
|
| 288 |
+
self.pairs_list = pairs_list
|
| 289 |
+
self.id_sampling = id_sampling
|
| 290 |
+
if os.path.exists(os.path.join(self.image_dir, 'train')):
|
| 291 |
+
assert os.path.exists(os.path.join(self.image_dir, 'test'))
|
| 292 |
+
print("Use predefined train-test split.")
|
| 293 |
+
if id_sampling:
|
| 294 |
+
train_videos = {os.path.basename(video).split('#')[0] for video in
|
| 295 |
+
os.listdir(os.path.join(self.image_dir, 'train'))}
|
| 296 |
+
train_videos = list(train_videos)
|
| 297 |
+
else:
|
| 298 |
+
train_videos = os.listdir(os.path.join(self.image_dir, 'train'))
|
| 299 |
+
test_videos = os.listdir(os.path.join(self.image_dir, 'test'))
|
| 300 |
+
self.root_dir = os.path.join(self.root_dir, 'train' if is_train else 'test')
|
| 301 |
+
self.landmark_dir = os.path.join(self.landmark_dir, 'train' if is_train else 'test')
|
| 302 |
+
self.image_dir = os.path.join(self.image_dir, 'train' if is_train else 'test')
|
| 303 |
+
self.audio_dir = os.path.join(self.audio_dir, 'train' if is_train else 'test')
|
| 304 |
+
|
| 305 |
+
else:
|
| 306 |
+
print("Use random train-test split.")
|
| 307 |
+
train_videos, test_videos = train_test_split(self.videos, random_state=random_seed, test_size=0.2)
|
| 308 |
+
|
| 309 |
+
if is_train:
|
| 310 |
+
self.videos = train_videos
|
| 311 |
+
else:
|
| 312 |
+
self.videos = test_videos
|
| 313 |
+
|
| 314 |
+
self.is_train = is_train
|
| 315 |
+
|
| 316 |
+
if self.is_train:
|
| 317 |
+
self.transform = AllAugmentationTransform(**augmentation_params)
|
| 318 |
+
else:
|
| 319 |
+
self.transform = None
|
| 320 |
+
|
| 321 |
+
def __len__(self):
|
| 322 |
+
return len(self.videos)
|
| 323 |
+
|
| 324 |
+
def __getitem__(self, idx):
|
| 325 |
+
if self.is_train and self.id_sampling:
|
| 326 |
+
name = self.videos[idx].split('.')[0]
|
| 327 |
+
path = np.random.choice(glob.glob(os.path.join(self.root_dir, name + '*.mp4')))
|
| 328 |
+
else:
|
| 329 |
+
name = self.videos[idx].split('.')[0]
|
| 330 |
+
landmark_path = os.path.join(self.landmark_dir, name)
|
| 331 |
+
|
| 332 |
+
audio_path = os.path.join(self.audio_dir, name)
|
| 333 |
+
path = os.path.join(self.image_dir, name)
|
| 334 |
+
|
| 335 |
+
video_name = os.path.basename(path)
|
| 336 |
+
|
| 337 |
+
if self.is_train and os.path.isdir(path):
|
| 338 |
+
frames = os.listdir(audio_path)
|
| 339 |
+
num_frames = len(frames)
|
| 340 |
+
frame_idx = np.sort(np.random.choice(num_frames-1, replace=True, size=2))
|
| 341 |
+
# landmark = np.load(landmark_path)#+'.npy'
|
| 342 |
+
# assert len(os.listdir(path)) == len(landmark), video_name+' length not equal'
|
| 343 |
+
video_array = [img_as_float32(io.imread(os.path.join(path, str(idx)+'.png'))) for idx in frame_idx]
|
| 344 |
+
cube_array = [img_as_float32(io.imread(os.path.join(landmark_path, str(idx)+'.jpg'))) for idx in frame_idx]
|
| 345 |
+
audio_array = [np.load(os.path.join(audio_path, str(idx)+'.npy'))[:,1:] for idx in frame_idx]
|
| 346 |
+
index_20 = [0,16,32,35,40,52,55,58,61,46,72,73,75,76,84,87,90,93,98,102]
|
| 347 |
+
index_32 = [0,4,8,12,16,20,24,28,32,33,35,67,68,40,42,52,55,72,73,58,61,75,76,46,47,51,84,87,90,93,98,102]
|
| 348 |
+
# landmark_array = [landmark[idx] for idx in frame_idx]
|
| 349 |
+
# landmark_array = [landmark[idx][index_32] for idx in frame_idx]
|
| 350 |
+
else:
|
| 351 |
+
video_array = read_video(path, frame_shape=self.frame_shape)
|
| 352 |
+
num_frames = len(video_array)
|
| 353 |
+
frame_idx = np.sort(np.random.choice(num_frames, replace=True, size=2)) if self.is_train else range(
|
| 354 |
+
num_frames)
|
| 355 |
+
video_array = video_array[frame_idx]
|
| 356 |
+
|
| 357 |
+
if self.transform is not None:
|
| 358 |
+
video_array = self.transform(video_array)
|
| 359 |
+
|
| 360 |
+
out = {}
|
| 361 |
+
if self.is_train:
|
| 362 |
+
# a = img_as_float32(io.imread('/media/thea/Data/first-order-model/images_512/102.jpg'))
|
| 363 |
+
# source = np.array(a, dtype='float32')
|
| 364 |
+
source = np.array(video_array[0], dtype='float32')
|
| 365 |
+
driving = np.array(video_array[1], dtype='float32')
|
| 366 |
+
|
| 367 |
+
spatial_size = np.array(source.shape[:2][::-1])[np.newaxis]
|
| 368 |
+
# source_landmark = np.array(2*landmark_array[0] / spatial_size -1, dtype='float32')
|
| 369 |
+
# driving_landmark = np.array(2*landmark_array[1] / spatial_size -1, dtype='float32')
|
| 370 |
+
source_cube = np.array(transform.resize(cube_array[0], (64,64)), dtype='float32')
|
| 371 |
+
driving_cube = np.array(transform.resize(cube_array[1], (64,64)), dtype='float32')
|
| 372 |
+
# source_heatmap = np.array(heatmap_array[0] , dtype='float32')
|
| 373 |
+
# driving_heatmap = np.array(heatmap_array[1] , dtype='float32')
|
| 374 |
+
out['source_cube'] = source_cube
|
| 375 |
+
out['driving_cube'] = driving_cube
|
| 376 |
+
# out['source_landmark'] = source_landmark
|
| 377 |
+
# out['driving_landmark'] = driving_landmark
|
| 378 |
+
# out['source_heatmap'] = source_heatmap
|
| 379 |
+
# out['driving_heatmap'] = driving_heatmap
|
| 380 |
+
out['driving'] = driving.transpose((2, 0, 1))
|
| 381 |
+
out['source'] = source.transpose((2, 0, 1))
|
| 382 |
+
|
| 383 |
+
out['source_audio'] = np.array(audio_array[0], dtype='float32')
|
| 384 |
+
out['driving_audio'] = np.array(audio_array[1], dtype='float32')
|
| 385 |
+
|
| 386 |
+
else:
|
| 387 |
+
video = np.array(video_array, dtype='float32')
|
| 388 |
+
out['video'] = video.transpose((3, 0, 1, 2))
|
| 389 |
+
|
| 390 |
+
out['name'] = video_name
|
| 391 |
+
|
| 392 |
+
return out
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
class DatasetRepeater(Dataset):
|
| 396 |
+
"""
|
| 397 |
+
Pass several times over the same dataset for better i/o performance
|
| 398 |
+
"""
|
| 399 |
+
|
| 400 |
+
def __init__(self, dataset, num_repeats=100):
|
| 401 |
+
self.dataset = dataset
|
| 402 |
+
self.num_repeats = num_repeats
|
| 403 |
+
|
| 404 |
+
def __len__(self):
|
| 405 |
+
return self.num_repeats * self.dataset.__len__()
|
| 406 |
+
|
| 407 |
+
def __getitem__(self, idx):
|
| 408 |
+
return self.dataset[idx % self.dataset.__len__()]#% self.dataset.__len__()
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
class PairedDataset(Dataset):
|
| 412 |
+
"""
|
| 413 |
+
Dataset of pairs for animation.
|
| 414 |
+
"""
|
| 415 |
+
|
| 416 |
+
def __init__(self, initial_dataset, number_of_pairs, seed=0):
|
| 417 |
+
self.initial_dataset = initial_dataset
|
| 418 |
+
pairs_list = self.initial_dataset.pairs_list
|
| 419 |
+
|
| 420 |
+
np.random.seed(seed)
|
| 421 |
+
|
| 422 |
+
if pairs_list is None:
|
| 423 |
+
max_idx = min(number_of_pairs, len(initial_dataset))
|
| 424 |
+
nx, ny = max_idx, max_idx
|
| 425 |
+
xy = np.mgrid[:nx, :ny].reshape(2, -1).T
|
| 426 |
+
number_of_pairs = min(xy.shape[0], number_of_pairs)
|
| 427 |
+
self.pairs = xy.take(np.random.choice(xy.shape[0], number_of_pairs, replace=False), axis=0)
|
| 428 |
+
else:
|
| 429 |
+
videos = self.initial_dataset.videos
|
| 430 |
+
name_to_index = {name: index for index, name in enumerate(videos)}
|
| 431 |
+
pairs = pd.read_csv(pairs_list)
|
| 432 |
+
pairs = pairs[np.logical_and(pairs['source'].isin(videos), pairs['driving'].isin(videos))]
|
| 433 |
+
|
| 434 |
+
number_of_pairs = min(pairs.shape[0], number_of_pairs)
|
| 435 |
+
self.pairs = []
|
| 436 |
+
self.start_frames = []
|
| 437 |
+
for ind in range(number_of_pairs):
|
| 438 |
+
self.pairs.append(
|
| 439 |
+
(name_to_index[pairs['driving'].iloc[ind]], name_to_index[pairs['source'].iloc[ind]]))
|
| 440 |
+
|
| 441 |
+
def __len__(self):
|
| 442 |
+
return len(self.pairs)
|
| 443 |
+
|
| 444 |
+
def __getitem__(self, idx):
|
| 445 |
+
pair = self.pairs[idx]
|
| 446 |
+
first = self.initial_dataset[pair[0]]
|
| 447 |
+
second = self.initial_dataset[pair[1]]
|
| 448 |
+
first = {'driving_' + key: value for key, value in first.items()}
|
| 449 |
+
second = {'source_' + key: value for key, value in second.items()}
|
| 450 |
+
|
| 451 |
+
return {**first, **second}
|
FONT/modules/function.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Created on Thu Sep 30 17:45:24 2021
|
| 5 |
+
|
| 6 |
+
@author: SENSETIME\jixinya1
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import torch
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def calc_mean_std(feat, eps=1e-5):
|
| 13 |
+
# eps is a small value added to the variance to avoid divide-by-zero.
|
| 14 |
+
size = feat.size()
|
| 15 |
+
assert (len(size) == 4)
|
| 16 |
+
N, C = size[:2]
|
| 17 |
+
feat_var = feat.view(N, C, -1).var(dim=2) + eps
|
| 18 |
+
feat_std = feat_var.sqrt().view(N, C, 1, 1)
|
| 19 |
+
feat_mean = feat.view(N, C, -1).mean(dim=2).view(N, C, 1, 1)
|
| 20 |
+
return feat_mean, feat_std
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def adaptive_instance_normalization(content_feat, style_feat):
|
| 24 |
+
assert (content_feat.size()[:2] == style_feat.size()[:2])
|
| 25 |
+
size = content_feat.size()
|
| 26 |
+
style_mean, style_std = calc_mean_std(style_feat)
|
| 27 |
+
content_mean, content_std = calc_mean_std(content_feat)
|
| 28 |
+
|
| 29 |
+
normalized_feat = (content_feat - content_mean.expand(
|
| 30 |
+
size)) / content_std.expand(size)
|
| 31 |
+
return normalized_feat * style_std.expand(size) + style_mean.expand(size)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _calc_feat_flatten_mean_std(feat):
|
| 35 |
+
# takes 3D feat (C, H, W), return mean and std of array within channels
|
| 36 |
+
assert (feat.size()[0] == 3)
|
| 37 |
+
assert (isinstance(feat, torch.FloatTensor))
|
| 38 |
+
feat_flatten = feat.view(3, -1)
|
| 39 |
+
mean = feat_flatten.mean(dim=-1, keepdim=True)
|
| 40 |
+
std = feat_flatten.std(dim=-1, keepdim=True)
|
| 41 |
+
return feat_flatten, mean, std
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _mat_sqrt(x):
|
| 45 |
+
U, D, V = torch.svd(x)
|
| 46 |
+
return torch.mm(torch.mm(U, D.pow(0.5).diag()), V.t())
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def coral(source, target):
|
| 50 |
+
# assume both source and target are 3D array (C, H, W)
|
| 51 |
+
# Note: flatten -> f
|
| 52 |
+
|
| 53 |
+
source_f, source_f_mean, source_f_std = _calc_feat_flatten_mean_std(source)
|
| 54 |
+
source_f_norm = (source_f - source_f_mean.expand_as(
|
| 55 |
+
source_f)) / source_f_std.expand_as(source_f)
|
| 56 |
+
source_f_cov_eye = \
|
| 57 |
+
torch.mm(source_f_norm, source_f_norm.t()) + torch.eye(3)
|
| 58 |
+
|
| 59 |
+
target_f, target_f_mean, target_f_std = _calc_feat_flatten_mean_std(target)
|
| 60 |
+
target_f_norm = (target_f - target_f_mean.expand_as(
|
| 61 |
+
target_f)) / target_f_std.expand_as(target_f)
|
| 62 |
+
target_f_cov_eye = \
|
| 63 |
+
torch.mm(target_f_norm, target_f_norm.t()) + torch.eye(3)
|
| 64 |
+
|
| 65 |
+
source_f_norm_transfer = torch.mm(
|
| 66 |
+
_mat_sqrt(target_f_cov_eye),
|
| 67 |
+
torch.mm(torch.inverse(_mat_sqrt(source_f_cov_eye)),
|
| 68 |
+
source_f_norm)
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
source_f_transfer = source_f_norm_transfer * \
|
| 72 |
+
target_f_std.expand_as(source_f_norm) + \
|
| 73 |
+
target_f_mean.expand_as(source_f_norm)
|
| 74 |
+
|
| 75 |
+
return source_f_transfer.view(source.size())
|
FONT/modules/generator.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from .util import ResBlock2d, SameBlock2d, UpBlock2d, DownBlock2d
|
| 5 |
+
from .dense_motion import DenseMotionNetwork
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class OcclusionAwareGenerator(nn.Module):
|
| 9 |
+
"""
|
| 10 |
+
Generator that given source image and and keypoints try to transform image according to movement trajectories
|
| 11 |
+
induced by keypoints. Generator follows Johnson architecture.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
def __init__(self, num_channels, num_kp, block_expansion, max_features, num_down_blocks,
|
| 15 |
+
num_bottleneck_blocks, estimate_occlusion_map=False, dense_motion_params=None, estimate_jacobian=False):
|
| 16 |
+
super(OcclusionAwareGenerator, self).__init__()
|
| 17 |
+
|
| 18 |
+
if dense_motion_params is not None:
|
| 19 |
+
self.dense_motion_network = DenseMotionNetwork(num_kp=num_kp, num_channels=num_channels,
|
| 20 |
+
estimate_occlusion_map=estimate_occlusion_map,
|
| 21 |
+
**dense_motion_params)
|
| 22 |
+
else:
|
| 23 |
+
self.dense_motion_network = None
|
| 24 |
+
|
| 25 |
+
self.first = SameBlock2d(num_channels, block_expansion, kernel_size=(7, 7), padding=(3, 3))
|
| 26 |
+
|
| 27 |
+
down_blocks = []
|
| 28 |
+
for i in range(num_down_blocks):
|
| 29 |
+
in_features = min(max_features, block_expansion * (2 ** i))
|
| 30 |
+
out_features = min(max_features, block_expansion * (2 ** (i + 1)))
|
| 31 |
+
down_blocks.append(DownBlock2d(in_features, out_features, kernel_size=(3, 3), padding=(1, 1)))
|
| 32 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 33 |
+
|
| 34 |
+
up_blocks = []
|
| 35 |
+
for i in range(num_down_blocks):
|
| 36 |
+
in_features = min(max_features, block_expansion * (2 ** (num_down_blocks - i)))
|
| 37 |
+
out_features = min(max_features, block_expansion * (2 ** (num_down_blocks - i - 1)))
|
| 38 |
+
up_blocks.append(UpBlock2d(in_features, out_features, kernel_size=(3, 3), padding=(1, 1)))
|
| 39 |
+
self.up_blocks = nn.ModuleList(up_blocks)
|
| 40 |
+
|
| 41 |
+
self.bottleneck = torch.nn.Sequential()
|
| 42 |
+
in_features = min(max_features, block_expansion * (2 ** num_down_blocks))
|
| 43 |
+
for i in range(num_bottleneck_blocks):
|
| 44 |
+
self.bottleneck.add_module('r' + str(i), ResBlock2d(in_features, kernel_size=(3, 3), padding=(1, 1)))
|
| 45 |
+
|
| 46 |
+
self.final = nn.Conv2d(block_expansion, num_channels, kernel_size=(7, 7), padding=(3, 3))
|
| 47 |
+
self.estimate_occlusion_map = estimate_occlusion_map
|
| 48 |
+
self.num_channels = num_channels
|
| 49 |
+
|
| 50 |
+
def deform_input(self, inp, deformation):
|
| 51 |
+
_, h_old, w_old, _ = deformation.shape
|
| 52 |
+
_, _, h, w = inp.shape
|
| 53 |
+
if h_old != h or w_old != w:
|
| 54 |
+
deformation = deformation.permute(0, 3, 1, 2)
|
| 55 |
+
deformation = F.interpolate(deformation, size=(h, w), mode='bilinear')
|
| 56 |
+
deformation = deformation.permute(0, 2, 3, 1)
|
| 57 |
+
return F.grid_sample(inp, deformation)
|
| 58 |
+
|
| 59 |
+
def forward(self, source_image, kp_driving, kp_source):
|
| 60 |
+
# Encoding (downsampling) part
|
| 61 |
+
out = self.first(source_image) #[4,64,H,W]
|
| 62 |
+
for i in range(len(self.down_blocks)):
|
| 63 |
+
out = self.down_blocks[i](out) #[4,256,H/4,W/4]
|
| 64 |
+
|
| 65 |
+
# Transforming feature representation according to deformation and occlusion
|
| 66 |
+
output_dict = {}
|
| 67 |
+
if self.dense_motion_network is not None:
|
| 68 |
+
dense_motion = self.dense_motion_network(source_image=source_image, kp_driving=kp_driving,
|
| 69 |
+
kp_source=kp_source)
|
| 70 |
+
output_dict['mask'] = dense_motion['mask']
|
| 71 |
+
output_dict['sparse_deformed'] = dense_motion['sparse_deformed']
|
| 72 |
+
|
| 73 |
+
if 'occlusion_map' in dense_motion:
|
| 74 |
+
occlusion_map = dense_motion['occlusion_map']
|
| 75 |
+
output_dict['occlusion_map'] = occlusion_map
|
| 76 |
+
else:
|
| 77 |
+
occlusion_map = None
|
| 78 |
+
deformation = dense_motion['deformation']
|
| 79 |
+
out = self.deform_input(out, deformation)
|
| 80 |
+
|
| 81 |
+
if occlusion_map is not None:
|
| 82 |
+
if out.shape[2] != occlusion_map.shape[2] or out.shape[3] != occlusion_map.shape[3]:
|
| 83 |
+
occlusion_map = F.interpolate(occlusion_map, size=out.shape[2:], mode='bilinear')
|
| 84 |
+
out = out * occlusion_map
|
| 85 |
+
|
| 86 |
+
output_dict["deformed"] = self.deform_input(source_image, deformation)
|
| 87 |
+
|
| 88 |
+
# Decoding part
|
| 89 |
+
out = self.bottleneck(out) #[4,256,64,64]
|
| 90 |
+
for i in range(len(self.up_blocks)):
|
| 91 |
+
out = self.up_blocks[i](out)
|
| 92 |
+
out = self.final(out)
|
| 93 |
+
out = torch.sigmoid(out) #[4,3,256,256]
|
| 94 |
+
|
| 95 |
+
output_dict["prediction"] = out
|
| 96 |
+
|
| 97 |
+
return output_dict
|
FONT/modules/keypoint_detector.py
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from .util import Hourglass, make_coordinate_grid, AntiAliasInterpolation2d, Ct_encoder, EmotionNet, AF2F, AF2F_s, draw_heatmap
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class KPDetector(nn.Module):
|
| 8 |
+
"""
|
| 9 |
+
Detecting a keypoints. Return keypoint position and jacobian near each keypoint.
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
def __init__(self, block_expansion, num_kp, num_channels, max_features,
|
| 13 |
+
num_blocks, temperature, estimate_jacobian=False, scale_factor=1,
|
| 14 |
+
single_jacobian_map=False, pad=0):
|
| 15 |
+
super(KPDetector, self).__init__()
|
| 16 |
+
|
| 17 |
+
self.predictor = Hourglass(block_expansion, in_features=num_channels,
|
| 18 |
+
max_features=max_features, num_blocks=num_blocks)
|
| 19 |
+
|
| 20 |
+
self.kp = nn.Conv2d(in_channels=self.predictor.out_filters, out_channels=num_kp, kernel_size=(7, 7),
|
| 21 |
+
padding=pad)
|
| 22 |
+
|
| 23 |
+
if estimate_jacobian:
|
| 24 |
+
self.num_jacobian_maps = 1 if single_jacobian_map else num_kp
|
| 25 |
+
self.jacobian = nn.Conv2d(in_channels=self.predictor.out_filters,
|
| 26 |
+
out_channels=4 * self.num_jacobian_maps, kernel_size=(7, 7), padding=pad)
|
| 27 |
+
self.jacobian.weight.data.zero_()
|
| 28 |
+
self.jacobian.bias.data.copy_(torch.tensor([1, 0, 0, 1] * self.num_jacobian_maps, dtype=torch.float))
|
| 29 |
+
else:
|
| 30 |
+
self.jacobian = None
|
| 31 |
+
|
| 32 |
+
self.temperature = temperature
|
| 33 |
+
self.scale_factor = scale_factor
|
| 34 |
+
if self.scale_factor != 1:
|
| 35 |
+
self.down = AntiAliasInterpolation2d(num_channels, self.scale_factor)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def gaussian2kp(self, heatmap):
|
| 41 |
+
"""
|
| 42 |
+
Extract the mean and from a heatmap
|
| 43 |
+
"""
|
| 44 |
+
shape = heatmap.shape
|
| 45 |
+
heatmap = heatmap.unsqueeze(-1) #[4,10,58,58,1]
|
| 46 |
+
grid = make_coordinate_grid(shape[2:], heatmap.type()).unsqueeze_(0).unsqueeze_(0) #[1,1,58,58,2]
|
| 47 |
+
value = (heatmap * grid).sum(dim=(2, 3)) #[4,10,2]
|
| 48 |
+
kp = {'value': value}
|
| 49 |
+
|
| 50 |
+
return kp
|
| 51 |
+
|
| 52 |
+
def audio_feature(self, x, heatmap):
|
| 53 |
+
|
| 54 |
+
# prediction = self.kp(x) #[4,10,H/4-6, W/4-6]
|
| 55 |
+
|
| 56 |
+
# final_shape = prediction.shape
|
| 57 |
+
# heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 58 |
+
# heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 59 |
+
# heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 60 |
+
|
| 61 |
+
# out = self.gaussian2kp(heatmap)
|
| 62 |
+
final_shape = heatmap.squeeze(2).shape
|
| 63 |
+
|
| 64 |
+
if self.jacobian is not None:
|
| 65 |
+
jacobian_map = self.jacobian(x) ##[4,40,H/4-6, W/4-6]
|
| 66 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], self.num_jacobian_maps, 4, final_shape[2],
|
| 67 |
+
final_shape[3])
|
| 68 |
+
heatmap = heatmap.unsqueeze(2)
|
| 69 |
+
|
| 70 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 71 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 72 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 73 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 74 |
+
|
| 75 |
+
return jacobian
|
| 76 |
+
|
| 77 |
+
def forward(self, x): #torch.Size([4, 3, H, W])
|
| 78 |
+
if self.scale_factor != 1:
|
| 79 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 80 |
+
|
| 81 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 82 |
+
prediction = self.kp(feature_map) #[4,10,H/4-6, W/4-6]
|
| 83 |
+
|
| 84 |
+
final_shape = prediction.shape
|
| 85 |
+
|
| 86 |
+
heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 87 |
+
heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 88 |
+
heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 89 |
+
|
| 90 |
+
out = self.gaussian2kp(heatmap)
|
| 91 |
+
out['heatmap'] = heatmap
|
| 92 |
+
|
| 93 |
+
if self.jacobian is not None:
|
| 94 |
+
jacobian_map = self.jacobian(feature_map) ##[4,40,H/4-6, W/4-6]
|
| 95 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], self.num_jacobian_maps, 4, final_shape[2],
|
| 96 |
+
final_shape[3])
|
| 97 |
+
heatmap = heatmap.unsqueeze(2)
|
| 98 |
+
|
| 99 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 100 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 101 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 102 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 103 |
+
out['jacobian'] = jacobian
|
| 104 |
+
|
| 105 |
+
return out
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class KPDetector_a(nn.Module):
|
| 111 |
+
"""
|
| 112 |
+
Detecting a keypoints. Return keypoint position and jacobian near each keypoint.
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
def __init__(self, block_expansion, num_kp, num_channels,num_channels_a, max_features,
|
| 116 |
+
num_blocks, temperature, estimate_jacobian=False, scale_factor=1,
|
| 117 |
+
single_jacobian_map=False, pad=0):
|
| 118 |
+
super(KPDetector_a, self).__init__()
|
| 119 |
+
|
| 120 |
+
self.predictor = Hourglass(block_expansion, in_features=num_channels_a,
|
| 121 |
+
max_features=max_features, num_blocks=num_blocks)
|
| 122 |
+
|
| 123 |
+
self.kp = nn.Conv2d(in_channels=self.predictor.out_filters, out_channels=num_kp, kernel_size=(7, 7),
|
| 124 |
+
padding=pad)
|
| 125 |
+
|
| 126 |
+
if estimate_jacobian:
|
| 127 |
+
self.num_jacobian_maps = 1 if single_jacobian_map else num_kp
|
| 128 |
+
self.jacobian = nn.Conv2d(in_channels=self.predictor.out_filters,
|
| 129 |
+
out_channels=4 * self.num_jacobian_maps, kernel_size=(7, 7), padding=pad)
|
| 130 |
+
self.jacobian.weight.data.zero_()
|
| 131 |
+
self.jacobian.bias.data.copy_(torch.tensor([1, 0, 0, 1] * self.num_jacobian_maps, dtype=torch.float))
|
| 132 |
+
else:
|
| 133 |
+
self.jacobian = None
|
| 134 |
+
|
| 135 |
+
self.temperature = temperature
|
| 136 |
+
self.scale_factor = scale_factor
|
| 137 |
+
if self.scale_factor != 1:
|
| 138 |
+
self.down = AntiAliasInterpolation2d(num_channels, self.scale_factor)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def gaussian2kp(self, heatmap):
|
| 144 |
+
"""
|
| 145 |
+
Extract the mean and from a heatmap
|
| 146 |
+
"""
|
| 147 |
+
shape = heatmap.shape
|
| 148 |
+
heatmap = heatmap.unsqueeze(-1) #[4,10,58,58,1]
|
| 149 |
+
grid = make_coordinate_grid(shape[2:], heatmap.type()).unsqueeze_(0).unsqueeze_(0) #[1,1,58,58,2]
|
| 150 |
+
value = (heatmap * grid).sum(dim=(2, 3)) #[4,10,2]
|
| 151 |
+
kp = {'value': value}
|
| 152 |
+
|
| 153 |
+
return kp
|
| 154 |
+
|
| 155 |
+
def audio_feature(self, x, heatmap):
|
| 156 |
+
|
| 157 |
+
# prediction = self.kp(x) #[4,10,H/4-6, W/4-6]
|
| 158 |
+
|
| 159 |
+
# final_shape = prediction.shape
|
| 160 |
+
# heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 161 |
+
# heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 162 |
+
# heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 163 |
+
|
| 164 |
+
# out = self.gaussian2kp(heatmap)
|
| 165 |
+
final_shape = heatmap.squeeze(2).shape
|
| 166 |
+
|
| 167 |
+
if self.jacobian is not None:
|
| 168 |
+
jacobian_map = self.jacobian(x) ##[4,40,H/4-6, W/4-6]
|
| 169 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], self.num_jacobian_maps, 4, final_shape[2],
|
| 170 |
+
final_shape[3])
|
| 171 |
+
heatmap = heatmap.unsqueeze(2)
|
| 172 |
+
|
| 173 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 174 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 175 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 176 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 177 |
+
|
| 178 |
+
return jacobian
|
| 179 |
+
|
| 180 |
+
def forward(self, feature_map): #torch.Size([4, 3, H, W])
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
prediction = self.kp(feature_map) #[4,10,H/4-6, W/4-6]
|
| 184 |
+
|
| 185 |
+
final_shape = prediction.shape
|
| 186 |
+
|
| 187 |
+
heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 188 |
+
heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 189 |
+
heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 190 |
+
|
| 191 |
+
out = self.gaussian2kp(heatmap)
|
| 192 |
+
out['heatmap'] = heatmap #B,10,58,58
|
| 193 |
+
|
| 194 |
+
if self.jacobian is not None:
|
| 195 |
+
jacobian_map = self.jacobian(feature_map) ##[4,40,H/4-6, W/4-6]
|
| 196 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], self.num_jacobian_maps, 4, final_shape[2],
|
| 197 |
+
final_shape[3])
|
| 198 |
+
heatmap = heatmap.unsqueeze(2)
|
| 199 |
+
|
| 200 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 201 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 202 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 203 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 204 |
+
out['jacobian'] = jacobian #B,10,2,2
|
| 205 |
+
|
| 206 |
+
return out
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
class Audio_Feature(nn.Module):
|
| 210 |
+
def __init__(self):
|
| 211 |
+
super(Audio_Feature, self).__init__()
|
| 212 |
+
|
| 213 |
+
self.con_encoder = Ct_encoder()
|
| 214 |
+
self.emo_encoder = EmotionNet()
|
| 215 |
+
self.decoder = AF2F_s()
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def forward(self, x):
|
| 220 |
+
x = x.unsqueeze(1)
|
| 221 |
+
|
| 222 |
+
c = self.con_encoder(x)
|
| 223 |
+
e = self.emo_encoder(x)
|
| 224 |
+
|
| 225 |
+
# d = torch.cat([c, e], dim=1)
|
| 226 |
+
d = self.decoder(c)
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
return d
|
| 230 |
+
'''
|
| 231 |
+
def forward(self, x, cube, audio): #torch.Size([4, 3, H, W])
|
| 232 |
+
if self.scale_factor != 1:
|
| 233 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 234 |
+
|
| 235 |
+
cube = cube.unsqueeze(1)
|
| 236 |
+
feature = torch.cat([x,cube,audio],dim=1)
|
| 237 |
+
feature_map = self.predictor(feature) #[4,3+32,H/4, W/4]
|
| 238 |
+
prediction = self.kp(feature_map) #[4,10,H/4-6, W/4-6]
|
| 239 |
+
|
| 240 |
+
final_shape = prediction.shape
|
| 241 |
+
heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 242 |
+
heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 243 |
+
heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 244 |
+
|
| 245 |
+
out = self.gaussian2kp(heatmap)
|
| 246 |
+
out['heatmap'] = heatmap
|
| 247 |
+
if self.jacobian is not None:
|
| 248 |
+
jacobian_map = self.jacobian(feature_map) ##[4,40,H/4-6, W/4-6]
|
| 249 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], self.num_jacobian_maps, 4, final_shape[2],
|
| 250 |
+
final_shape[3])
|
| 251 |
+
heatmap = heatmap.unsqueeze(2)
|
| 252 |
+
|
| 253 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 254 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 255 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 256 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 257 |
+
out['jacobian'] = jacobian
|
| 258 |
+
|
| 259 |
+
return out
|
| 260 |
+
'''
|
FONT/modules/model.py
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from modules.util import AntiAliasInterpolation2d, make_coordinate_grid
|
| 5 |
+
from torchvision import models
|
| 6 |
+
import numpy as np
|
| 7 |
+
from torch.autograd import grad
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class Vgg19(torch.nn.Module):
|
| 11 |
+
"""
|
| 12 |
+
Vgg19 network for perceptual loss. See Sec 3.3.
|
| 13 |
+
"""
|
| 14 |
+
def __init__(self, requires_grad=False):
|
| 15 |
+
super(Vgg19, self).__init__()
|
| 16 |
+
vgg_pretrained_features = models.vgg19(pretrained=True).features
|
| 17 |
+
self.slice1 = torch.nn.Sequential()
|
| 18 |
+
self.slice2 = torch.nn.Sequential()
|
| 19 |
+
self.slice3 = torch.nn.Sequential()
|
| 20 |
+
self.slice4 = torch.nn.Sequential()
|
| 21 |
+
self.slice5 = torch.nn.Sequential()
|
| 22 |
+
for x in range(2):
|
| 23 |
+
self.slice1.add_module(str(x), vgg_pretrained_features[x])
|
| 24 |
+
for x in range(2, 7):
|
| 25 |
+
self.slice2.add_module(str(x), vgg_pretrained_features[x])
|
| 26 |
+
for x in range(7, 12):
|
| 27 |
+
self.slice3.add_module(str(x), vgg_pretrained_features[x])
|
| 28 |
+
for x in range(12, 21):
|
| 29 |
+
self.slice4.add_module(str(x), vgg_pretrained_features[x])
|
| 30 |
+
for x in range(21, 30):
|
| 31 |
+
self.slice5.add_module(str(x), vgg_pretrained_features[x])
|
| 32 |
+
|
| 33 |
+
self.mean = torch.nn.Parameter(data=torch.Tensor(np.array([0.485, 0.456, 0.406]).reshape((1, 3, 1, 1))),
|
| 34 |
+
requires_grad=False)
|
| 35 |
+
self.std = torch.nn.Parameter(data=torch.Tensor(np.array([0.229, 0.224, 0.225]).reshape((1, 3, 1, 1))),
|
| 36 |
+
requires_grad=False)
|
| 37 |
+
|
| 38 |
+
if not requires_grad:
|
| 39 |
+
for param in self.parameters():
|
| 40 |
+
param.requires_grad = False
|
| 41 |
+
|
| 42 |
+
def forward(self, X):
|
| 43 |
+
X = (X - self.mean) / self.std
|
| 44 |
+
h_relu1 = self.slice1(X)
|
| 45 |
+
h_relu2 = self.slice2(h_relu1)
|
| 46 |
+
h_relu3 = self.slice3(h_relu2)
|
| 47 |
+
h_relu4 = self.slice4(h_relu3)
|
| 48 |
+
h_relu5 = self.slice5(h_relu4)
|
| 49 |
+
out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]
|
| 50 |
+
return out
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class ImagePyramide(torch.nn.Module):
|
| 54 |
+
"""
|
| 55 |
+
Create image pyramide for computing pyramide perceptual loss. See Sec 3.3
|
| 56 |
+
"""
|
| 57 |
+
def __init__(self, scales, num_channels):
|
| 58 |
+
super(ImagePyramide, self).__init__()
|
| 59 |
+
downs = {}
|
| 60 |
+
for scale in scales:
|
| 61 |
+
downs[str(scale).replace('.', '-')] = AntiAliasInterpolation2d(num_channels, scale)
|
| 62 |
+
self.downs = nn.ModuleDict(downs)
|
| 63 |
+
|
| 64 |
+
def forward(self, x):
|
| 65 |
+
out_dict = {}
|
| 66 |
+
for scale, down_module in self.downs.items():
|
| 67 |
+
out_dict['prediction_' + str(scale).replace('-', '.')] = down_module(x)
|
| 68 |
+
return out_dict
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Transform:
|
| 72 |
+
"""
|
| 73 |
+
Random tps transformation for equivariance constraints. See Sec 3.3
|
| 74 |
+
"""
|
| 75 |
+
def __init__(self, bs, **kwargs):
|
| 76 |
+
noise = torch.normal(mean=0, std=kwargs['sigma_affine'] * torch.ones([bs, 2, 3]))
|
| 77 |
+
self.theta = noise + torch.eye(2, 3).view(1, 2, 3)
|
| 78 |
+
self.bs = bs
|
| 79 |
+
|
| 80 |
+
if ('sigma_tps' in kwargs) and ('points_tps' in kwargs):
|
| 81 |
+
self.tps = True
|
| 82 |
+
self.control_points = make_coordinate_grid((kwargs['points_tps'], kwargs['points_tps']), type=noise.type())
|
| 83 |
+
self.control_points = self.control_points.unsqueeze(0)
|
| 84 |
+
self.control_params = torch.normal(mean=0,
|
| 85 |
+
std=kwargs['sigma_tps'] * torch.ones([bs, 1, kwargs['points_tps'] ** 2]))
|
| 86 |
+
else:
|
| 87 |
+
self.tps = False
|
| 88 |
+
|
| 89 |
+
def transform_frame(self, frame):
|
| 90 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 91 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 92 |
+
grid = self.warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 93 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 94 |
+
|
| 95 |
+
def inverse_transform_frame(self, frame):
|
| 96 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 97 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 98 |
+
grid = self.inverse_warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 99 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 100 |
+
|
| 101 |
+
def warp_coordinates(self, coordinates):
|
| 102 |
+
theta = self.theta.type(coordinates.type())
|
| 103 |
+
theta = theta.unsqueeze(1)
|
| 104 |
+
transformed = torch.matmul(theta[:, :, :, :2], coordinates.unsqueeze(-1)) + theta[:, :, :, 2:]
|
| 105 |
+
transformed = transformed.squeeze(-1)
|
| 106 |
+
|
| 107 |
+
if self.tps:
|
| 108 |
+
control_points = self.control_points.type(coordinates.type())
|
| 109 |
+
control_params = self.control_params.type(coordinates.type())
|
| 110 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 111 |
+
distances = torch.abs(distances).sum(-1)
|
| 112 |
+
|
| 113 |
+
result = distances ** 2
|
| 114 |
+
result = result * torch.log(distances + 1e-6)
|
| 115 |
+
result = result * control_params
|
| 116 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 117 |
+
transformed = transformed + result
|
| 118 |
+
|
| 119 |
+
return transformed
|
| 120 |
+
|
| 121 |
+
def inverse_warp_coordinates(self, coordinates):
|
| 122 |
+
theta = self.theta.type(coordinates.type())
|
| 123 |
+
theta = theta.unsqueeze(1)
|
| 124 |
+
a = torch.FloatTensor([[[[0,0,1]]]]).repeat([self.bs,1,1,1]).cuda()
|
| 125 |
+
c = torch.cat((theta,a),2)
|
| 126 |
+
d = c.inverse()[:,:,:2,:]
|
| 127 |
+
d = d.type(coordinates.type())
|
| 128 |
+
transformed = torch.matmul(d[:, :, :, :2], coordinates.unsqueeze(-1)) + d[:, :, :, 2:]
|
| 129 |
+
transformed = transformed.squeeze(-1)
|
| 130 |
+
|
| 131 |
+
if self.tps:
|
| 132 |
+
control_points = self.control_points.type(coordinates.type())
|
| 133 |
+
control_params = self.control_params.type(coordinates.type())
|
| 134 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 135 |
+
distances = torch.abs(distances).sum(-1)
|
| 136 |
+
|
| 137 |
+
result = distances ** 2
|
| 138 |
+
result = result * torch.log(distances + 1e-6)
|
| 139 |
+
result = result * control_params
|
| 140 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 141 |
+
transformed = transformed + result
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
return transformed
|
| 145 |
+
|
| 146 |
+
def jacobian(self, coordinates):
|
| 147 |
+
coordinates.requires_grad=True
|
| 148 |
+
new_coordinates = self.warp_coordinates(coordinates)#[4,10,2]
|
| 149 |
+
grad_x = grad(new_coordinates[..., 0].sum(), coordinates, create_graph=True)
|
| 150 |
+
grad_y = grad(new_coordinates[..., 1].sum(), coordinates, create_graph=True)
|
| 151 |
+
jacobian = torch.cat([grad_x[0].unsqueeze(-2), grad_y[0].unsqueeze(-2)], dim=-2)
|
| 152 |
+
return jacobian
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def detach_kp(kp):
|
| 156 |
+
return {key: value.detach() for key, value in kp.items()}
|
| 157 |
+
|
| 158 |
+
class TrainPart1Model(torch.nn.Module):
|
| 159 |
+
"""
|
| 160 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
def __init__(self, kp_extractor, kp_extractor_a, audio_feature, generator, discriminator, train_params, device_ids):
|
| 164 |
+
super(TrainPart1Model, self).__init__()
|
| 165 |
+
self.kp_extractor = kp_extractor
|
| 166 |
+
self.kp_extractor_a = kp_extractor_a
|
| 167 |
+
|
| 168 |
+
self.audio_feature = audio_feature
|
| 169 |
+
self.generator = generator
|
| 170 |
+
self.discriminator = discriminator
|
| 171 |
+
self.train_params = train_params
|
| 172 |
+
self.scales = train_params['scales']
|
| 173 |
+
self.disc_scales = self.discriminator.scales
|
| 174 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 175 |
+
if torch.cuda.is_available():
|
| 176 |
+
self.pyramid = self.pyramid.cuda()
|
| 177 |
+
|
| 178 |
+
self.loss_weights = train_params['loss_weights']
|
| 179 |
+
|
| 180 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 181 |
+
self.vgg = Vgg19()
|
| 182 |
+
if torch.cuda.is_available():
|
| 183 |
+
self.vgg = self.vgg.cuda()
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
self.mse_loss_fn = nn.MSELoss().cuda()
|
| 187 |
+
def forward(self, x):
|
| 188 |
+
|
| 189 |
+
kp_source = self.kp_extractor(x['example_image'])
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
kp_driving = []
|
| 193 |
+
for i in range(16):
|
| 194 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i]))
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
kp_driving_a = [] #x['example_image'],
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
# print(x['example_image'].shape, x['driving_audio'].shape, x['driving_pose'].shape)
|
| 202 |
+
deco_out = self.audio_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 203 |
+
loss_values = {}
|
| 204 |
+
|
| 205 |
+
if self.loss_weights['audio'] != 0:
|
| 206 |
+
|
| 207 |
+
kp_driving_a = []
|
| 208 |
+
for i in range(16):
|
| 209 |
+
kp_driving_a.append(self.kp_extractor_a(deco_out[:,i]))#
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
loss_value = 0
|
| 213 |
+
loss_heatmap = 0
|
| 214 |
+
loss_jacobian = 0
|
| 215 |
+
loss_perceptual = 0
|
| 216 |
+
for i in range(len(kp_driving)):
|
| 217 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'] - kp_driving_a[i]['jacobian']).mean())*self.loss_weights['audio']
|
| 218 |
+
|
| 219 |
+
# loss_jacobian = loss_jacobian*self.loss_weights['audio']
|
| 220 |
+
loss_heatmap += (torch.abs(kp_driving[i]['heatmap'] - kp_driving_a[i]['heatmap']).mean())*self.loss_weights['audio']*100
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
loss_value += (torch.abs(kp_driving[i]['value'].detach() - kp_driving_a[i]['value']).mean())*self.loss_weights['audio']
|
| 224 |
+
|
| 225 |
+
loss_values['loss_value'] = loss_value/len(kp_driving)
|
| 226 |
+
loss_values['loss_heatmap'] = loss_heatmap/len(kp_driving)
|
| 227 |
+
loss_values['loss_jacobian'] = loss_jacobian/len(kp_driving)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
if self.train_params['generator'] == 'not':
|
| 231 |
+
# loss_values['perceptual'] = self.mse_loss_fn(deco_out,deco_out)
|
| 232 |
+
for i in range(1): #0,len(kp_driving),4
|
| 233 |
+
|
| 234 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving_a[i])
|
| 235 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving_a})
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
elif self.train_params['generator'] == 'visual':
|
| 239 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 240 |
+
|
| 241 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving[i])
|
| 242 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 243 |
+
|
| 244 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 245 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 246 |
+
|
| 247 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 248 |
+
value_total = 0
|
| 249 |
+
for scale in self.scales:
|
| 250 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 251 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 252 |
+
|
| 253 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 254 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 255 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 256 |
+
loss_perceptual += value_total
|
| 257 |
+
|
| 258 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 259 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 260 |
+
elif self.train_params['generator'] == 'audio':
|
| 261 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 262 |
+
|
| 263 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving_a[i])
|
| 264 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving_a})
|
| 265 |
+
|
| 266 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 267 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 268 |
+
|
| 269 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 270 |
+
value_total = 0
|
| 271 |
+
for scale in self.scales:
|
| 272 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 273 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 274 |
+
|
| 275 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 276 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 277 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 278 |
+
loss_perceptual += value_total
|
| 279 |
+
|
| 280 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 281 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 282 |
+
else:
|
| 283 |
+
print('wrong train_params: ', self.train_params['generator'])
|
| 284 |
+
|
| 285 |
+
return loss_values,generated
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
class TrainPart2Model(torch.nn.Module):
|
| 289 |
+
"""
|
| 290 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 291 |
+
"""
|
| 292 |
+
|
| 293 |
+
def __init__(self, kp_extractor, emo_feature, kp_extractor_a, audio_feature, generator, discriminator, train_params, device_ids):
|
| 294 |
+
super(TrainPart2Model, self).__init__()
|
| 295 |
+
self.kp_extractor = kp_extractor
|
| 296 |
+
self.kp_extractor_a = kp_extractor_a
|
| 297 |
+
|
| 298 |
+
self.audio_feature = audio_feature
|
| 299 |
+
self.emo_feature = emo_feature
|
| 300 |
+
self.generator = generator
|
| 301 |
+
self.discriminator = discriminator
|
| 302 |
+
self.train_params = train_params
|
| 303 |
+
self.scales = train_params['scales']
|
| 304 |
+
self.disc_scales = self.discriminator.scales
|
| 305 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 306 |
+
if torch.cuda.is_available():
|
| 307 |
+
self.pyramid = self.pyramid.cuda()
|
| 308 |
+
|
| 309 |
+
self.loss_weights = train_params['loss_weights']
|
| 310 |
+
|
| 311 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 312 |
+
self.vgg = Vgg19()
|
| 313 |
+
if torch.cuda.is_available():
|
| 314 |
+
self.vgg = self.vgg.cuda()
|
| 315 |
+
|
| 316 |
+
self.mse_loss_fn = nn.MSELoss().cuda()
|
| 317 |
+
self.CroEn_loss = nn.CrossEntropyLoss().cuda()
|
| 318 |
+
def forward(self, x):
|
| 319 |
+
|
| 320 |
+
kp_source = self.kp_extractor(x['example_image'])
|
| 321 |
+
|
| 322 |
+
kp_driving = []
|
| 323 |
+
kp_emo = []
|
| 324 |
+
for i in range(16):
|
| 325 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i]))
|
| 326 |
+
# kp_emo.append(self.emo_detector(x['driving'][:,i]))
|
| 327 |
+
|
| 328 |
+
kp_driving_a = [] #x['example_image'],
|
| 329 |
+
deco_out = self.audio_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 330 |
+
# emo_out = self.emo_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 331 |
+
loss_values = {}
|
| 332 |
+
|
| 333 |
+
if self.loss_weights['emo'] != 0:
|
| 334 |
+
|
| 335 |
+
kp_driving_a = []
|
| 336 |
+
fakes = []
|
| 337 |
+
for i in range(16):
|
| 338 |
+
kp_driving_a.append(self.kp_extractor_a(deco_out[:,i]))#
|
| 339 |
+
value = self.kp_extractor_a(deco_out[:,i])['value']
|
| 340 |
+
jacobian = self.kp_extractor_a(deco_out[:,i])['jacobian']
|
| 341 |
+
if self.train_params['type'] == 'linear_4' :
|
| 342 |
+
out, fake = self.emo_feature(x['transformed_driving'][:,i],value,jacobian)
|
| 343 |
+
kp_emo.append(out)
|
| 344 |
+
fakes.append(fake)
|
| 345 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 346 |
+
elif self.train_params['type'] == 'linear_10':
|
| 347 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 348 |
+
|
| 349 |
+
out, fake = self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian)
|
| 350 |
+
kp_emo.append(out)
|
| 351 |
+
fakes.append(fake)
|
| 352 |
+
elif self.train_params['type'] == 'linear_4_new':
|
| 353 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 354 |
+
|
| 355 |
+
out, fake = self.emo_feature.linear_4(x['transformed_driving'][:,i],value,jacobian)
|
| 356 |
+
kp_emo.append(out)
|
| 357 |
+
fakes.append(fake)
|
| 358 |
+
elif self.train_params['type'] == 'linear_np_4':
|
| 359 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 360 |
+
|
| 361 |
+
out, fake = self.emo_feature.linear_np_4(x['transformed_driving'][:,i],value,jacobian)
|
| 362 |
+
kp_emo.append(out)
|
| 363 |
+
fakes.append(fake)
|
| 364 |
+
elif self.train_params['type'] == 'linear_np_10':
|
| 365 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 366 |
+
|
| 367 |
+
out, fake = self.emo_feature.linear_np_10(x['transformed_driving'][:,i],value,jacobian)
|
| 368 |
+
kp_emo.append(out)
|
| 369 |
+
fakes.append(fake)
|
| 370 |
+
|
| 371 |
+
loss_value = 0
|
| 372 |
+
|
| 373 |
+
loss_jacobian = 0
|
| 374 |
+
|
| 375 |
+
loss_classify = 0
|
| 376 |
+
kp_all = kp_driving_a
|
| 377 |
+
|
| 378 |
+
for i in range(len(kp_driving)):
|
| 379 |
+
|
| 380 |
+
if self.train_params['type'] == 'linear_4' or self.train_params['type'] == 'linear_4_new' or self.train_params['type'] == 'linear_np_4':
|
| 381 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,1] - kp_driving_a[i]['jacobian'][:,1] -kp_emo[i]['jacobian'][:,0]).mean())*self.loss_weights['emo']
|
| 382 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,4] - kp_driving_a[i]['jacobian'][:,4] -kp_emo[i]['jacobian'][:,1]).mean())*self.loss_weights['emo']
|
| 383 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,6] - kp_driving_a[i]['jacobian'][:,6] -kp_emo[i]['jacobian'][:,2]).mean())*self.loss_weights['emo']
|
| 384 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,8] - kp_driving_a[i]['jacobian'][:,8] -kp_emo[i]['jacobian'][:,3]).mean())*self.loss_weights['emo']
|
| 385 |
+
|
| 386 |
+
loss_classify += self.CroEn_loss(fakes[i],x['emotion'])
|
| 387 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,1] .detach() - kp_driving_a[i]['value'][:,1] - kp_emo[i]['value'][:,0] ).mean())*self.loss_weights['emo']
|
| 388 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,4] .detach() - kp_driving_a[i]['value'][:,4] - kp_emo[i]['value'][:,1] ).mean())*self.loss_weights['emo']
|
| 389 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,6] .detach() - kp_driving_a[i]['value'][:,6] - kp_emo[i]['value'][:,2] ).mean())*self.loss_weights['emo']
|
| 390 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,8] .detach() - kp_driving_a[i]['value'][:,8] - kp_emo[i]['value'][:,3] ).mean())*self.loss_weights['emo']
|
| 391 |
+
kp_all[i]['jacobian'][:,1] = kp_emo[i]['jacobian'][:,0] + kp_driving_a[i]['jacobian'][:,1]
|
| 392 |
+
kp_all[i]['jacobian'][:,4] = kp_emo[i]['jacobian'][:,1] + kp_driving_a[i]['jacobian'][:,4]
|
| 393 |
+
kp_all[i]['jacobian'][:,6] = kp_emo[i]['jacobian'][:,2] + kp_driving_a[i]['jacobian'][:,6]
|
| 394 |
+
kp_all[i]['jacobian'][:,8] = kp_emo[i]['jacobian'][:,3] + kp_driving_a[i]['jacobian'][:,8]
|
| 395 |
+
kp_all[i]['value'][:,1] = kp_emo[i]['value'][:,0] + kp_driving_a[i]['value'][:,1]
|
| 396 |
+
kp_all[i]['value'][:,4] = kp_emo[i]['value'][:,1] + kp_driving_a[i]['value'][:,4]
|
| 397 |
+
kp_all[i]['value'][:,6] = kp_emo[i]['value'][:,2] + kp_driving_a[i]['value'][:,6]
|
| 398 |
+
kp_all[i]['value'][:,8] = kp_emo[i]['value'][:,3] + kp_driving_a[i]['value'][:,8]
|
| 399 |
+
elif self.train_params['type'] == 'linear_10' or self.train_params['type'] == 'linear_np_10':
|
| 400 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'] - kp_driving_a[i]['jacobian'] -kp_emo[i]['jacobian']).mean())*self.loss_weights['emo']
|
| 401 |
+
|
| 402 |
+
loss_classify += self.CroEn_loss(fakes[i],x['emotion'])
|
| 403 |
+
loss_value += (torch.abs(kp_driving[i]['value'].detach() - kp_driving_a[i]['value'] - kp_emo[i]['value'] ).mean())*self.loss_weights['emo']
|
| 404 |
+
|
| 405 |
+
# kp_all[i]['value'] = kp_emo[i]['value'] + kp_driving_a[i]['value']
|
| 406 |
+
|
| 407 |
+
loss_values['loss_value'] = loss_value/len(kp_driving)
|
| 408 |
+
# loss_values['loss_heatmap'] = loss_heatmap/len(kp_driving)
|
| 409 |
+
loss_values['loss_jacobian'] = loss_jacobian/len(kp_driving)
|
| 410 |
+
if self.train_params['classify'] == True:
|
| 411 |
+
loss_values['loss_classify'] = loss_classify/len(kp_driving)
|
| 412 |
+
else:
|
| 413 |
+
loss_values['loss_classify'] = torch.tensor(0, device = loss_values['loss_value'].device)
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
return loss_values,generated
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
class GeneratorFullModel(torch.nn.Module):
|
| 423 |
+
"""
|
| 424 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 425 |
+
"""
|
| 426 |
+
|
| 427 |
+
def __init__(self, kp_extractor, kp_extractor_a, audio_feature, generator, discriminator, train_params):
|
| 428 |
+
super(GeneratorFullModel, self).__init__()
|
| 429 |
+
self.kp_extractor = kp_extractor
|
| 430 |
+
self.kp_extractor_a = kp_extractor_a
|
| 431 |
+
# self.content_encoder = content_encoder
|
| 432 |
+
# self.emotion_encoder = emotion_encoder
|
| 433 |
+
self.audio_feature = audio_feature
|
| 434 |
+
self.generator = generator
|
| 435 |
+
self.discriminator = discriminator
|
| 436 |
+
self.train_params = train_params
|
| 437 |
+
self.scales = train_params['scales']
|
| 438 |
+
self.disc_scales = self.discriminator.scales
|
| 439 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 440 |
+
if torch.cuda.is_available():
|
| 441 |
+
self.pyramid = self.pyramid.cuda()
|
| 442 |
+
|
| 443 |
+
self.loss_weights = train_params['loss_weights']
|
| 444 |
+
|
| 445 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 446 |
+
self.vgg = Vgg19()
|
| 447 |
+
if torch.cuda.is_available():
|
| 448 |
+
self.vgg = self.vgg.cuda()
|
| 449 |
+
|
| 450 |
+
self.pca = torch.FloatTensor(np.load('.../LRW/list/U_106.npy'))[:, :16].cuda()
|
| 451 |
+
self.mean = torch.FloatTensor(np.load('.../LRW/list/mean_106.npy')).cuda()
|
| 452 |
+
|
| 453 |
+
def forward(self, x):
|
| 454 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 455 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 456 |
+
# kp_source = self.kp_extractor(x['source'])
|
| 457 |
+
# kp_source_a = self.kp_extractor_a(x['source'], x['source_cube'], source_a_f)
|
| 458 |
+
# driving_a_f = self.audio_feature(self.content_encoder(x['driving_audio'].unsqueeze(1)), self.emotion_encoder(x['driving_audio'].unsqueeze(1)))
|
| 459 |
+
# driving_a_f = self.audio_feature(x['driving_audio'])
|
| 460 |
+
# kp_driving = self.kp_extractor(x['driving'])
|
| 461 |
+
# kp_driving_a = self.kp_extractor_a(x['driving'], x['driving_cube'], driving_a_f)
|
| 462 |
+
|
| 463 |
+
kp_driving = []
|
| 464 |
+
for i in range(16):
|
| 465 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i],x['driving_landmark'][:,i],self.loss_weights['equivariance_value']))
|
| 466 |
+
|
| 467 |
+
kp_driving_a = []
|
| 468 |
+
fc_out, deco_out = self.audio_feature(x['example_landmark'], x['driving_audio'], x['driving_pose'])
|
| 469 |
+
fake_lmark=fc_out + x['example_landmark'].expand_as(fc_out)
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
fake_lmark = torch.mm( fake_lmark, self.pca.t() )
|
| 473 |
+
fake_lmark = fake_lmark + self.mean.expand_as(fake_lmark)
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
fake_lmark = fake_lmark.unsqueeze(0)
|
| 477 |
+
|
| 478 |
+
# for i in range(16):
|
| 479 |
+
# kp_driving_a.append()
|
| 480 |
+
|
| 481 |
+
# generated = self.generator(x['source'], kp_source=kp_source, kp_driving=kp_driving)
|
| 482 |
+
# generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 483 |
+
|
| 484 |
+
loss_values = {}
|
| 485 |
+
|
| 486 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 487 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 488 |
+
|
| 489 |
+
if self.loss_weights['audio'] != 0:
|
| 490 |
+
value = torch.abs(kp_source['jacobian'].detach() - kp_source_a['jacobian'].detach()).mean() + torch.abs(kp_driving['jacobian'].detach() - kp_driving_a['jacobian']).mean()
|
| 491 |
+
value = value/2
|
| 492 |
+
loss_values['jacobian'] = value*self.loss_weights['audio']
|
| 493 |
+
value = torch.abs(kp_source['heatmap'].detach() - kp_source_a['heatmap'].detach()).mean() + torch.abs(kp_driving['heatmap'].detach() - kp_driving_a['heatmap']).mean()
|
| 494 |
+
value = value/2
|
| 495 |
+
loss_values['heatmap'] = value*self.loss_weights['audio']
|
| 496 |
+
value = torch.abs(kp_source['value'].detach() - kp_source_a['value'].detach()).mean() + torch.abs(kp_driving['value'].detach() - kp_driving_a['value']).mean()
|
| 497 |
+
value = value/2
|
| 498 |
+
loss_values['value'] = value*self.loss_weights['audio']
|
| 499 |
+
|
| 500 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 501 |
+
value_total = 0
|
| 502 |
+
for scale in self.scales:
|
| 503 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 504 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 505 |
+
|
| 506 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 507 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 508 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 509 |
+
loss_values['perceptual'] = value_total
|
| 510 |
+
|
| 511 |
+
if self.loss_weights['generator_gan'] != 0:
|
| 512 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 513 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 514 |
+
value_total = 0
|
| 515 |
+
for scale in self.disc_scales:
|
| 516 |
+
key = 'prediction_map_%s' % scale
|
| 517 |
+
value = ((1 - discriminator_maps_generated[key]) ** 2).mean()
|
| 518 |
+
value_total += self.loss_weights['generator_gan'] * value
|
| 519 |
+
loss_values['gen_gan'] = value_total
|
| 520 |
+
|
| 521 |
+
if sum(self.loss_weights['feature_matching']) != 0:
|
| 522 |
+
value_total = 0
|
| 523 |
+
for scale in self.disc_scales:
|
| 524 |
+
key = 'feature_maps_%s' % scale
|
| 525 |
+
for i, (a, b) in enumerate(zip(discriminator_maps_real[key], discriminator_maps_generated[key])):
|
| 526 |
+
if self.loss_weights['feature_matching'][i] == 0:
|
| 527 |
+
continue
|
| 528 |
+
value = torch.abs(a - b).mean()
|
| 529 |
+
value_total += self.loss_weights['feature_matching'][i] * value
|
| 530 |
+
loss_values['feature_matching'] = value_total
|
| 531 |
+
|
| 532 |
+
if (self.loss_weights['equivariance_value'] + self.loss_weights['equivariance_jacobian']) != 0:
|
| 533 |
+
transform = Transform(x['driving'].shape[0], **self.train_params['transform_params'])
|
| 534 |
+
transformed_frame = transform.transform_frame(x['driving'])
|
| 535 |
+
transformed_landmark = transform.inverse_warp_coordinates(x['driving_landmark'])
|
| 536 |
+
transformed_kp = self.kp_extractor(transformed_frame)
|
| 537 |
+
|
| 538 |
+
generated['transformed_frame'] = transformed_frame
|
| 539 |
+
generated['transformed_kp'] = transformed_kp
|
| 540 |
+
|
| 541 |
+
## Value loss part
|
| 542 |
+
if self.loss_weights['equivariance_value'] != 0:
|
| 543 |
+
value = torch.abs(kp_driving['value'] - transform.warp_coordinates(transformed_kp['value'])).mean()
|
| 544 |
+
loss_values['equivariance_value'] = self.loss_weights['equivariance_value'] * value
|
| 545 |
+
|
| 546 |
+
## jacobian loss part
|
| 547 |
+
if self.loss_weights['equivariance_jacobian'] != 0:
|
| 548 |
+
jacobian_transformed = torch.matmul(transform.jacobian(transformed_kp['value']),
|
| 549 |
+
transformed_kp['jacobian'])
|
| 550 |
+
|
| 551 |
+
normed_driving = torch.inverse(kp_driving['jacobian'])
|
| 552 |
+
normed_transformed = jacobian_transformed
|
| 553 |
+
value = torch.matmul(normed_driving, normed_transformed)
|
| 554 |
+
|
| 555 |
+
eye = torch.eye(2).view(1, 1, 2, 2).type(value.type())
|
| 556 |
+
|
| 557 |
+
value = torch.abs(eye - value).mean()
|
| 558 |
+
loss_values['equivariance_jacobian'] = self.loss_weights['equivariance_jacobian'] * value
|
| 559 |
+
|
| 560 |
+
return loss_values, generated
|
| 561 |
+
|
| 562 |
+
|
| 563 |
+
class DiscriminatorFullModel(torch.nn.Module):
|
| 564 |
+
"""
|
| 565 |
+
Merge all discriminator related updates into single model for better multi-gpu usage
|
| 566 |
+
"""
|
| 567 |
+
|
| 568 |
+
def __init__(self, kp_extractor, generator, discriminator, train_params):
|
| 569 |
+
super(DiscriminatorFullModel, self).__init__()
|
| 570 |
+
self.kp_extractor = kp_extractor
|
| 571 |
+
self.generator = generator
|
| 572 |
+
self.discriminator = discriminator
|
| 573 |
+
self.train_params = train_params
|
| 574 |
+
self.scales = self.discriminator.scales
|
| 575 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 576 |
+
if torch.cuda.is_available():
|
| 577 |
+
self.pyramid = self.pyramid.cuda()
|
| 578 |
+
|
| 579 |
+
self.loss_weights = train_params['loss_weights']
|
| 580 |
+
|
| 581 |
+
def forward(self, x, generated):
|
| 582 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 583 |
+
pyramide_generated = self.pyramid(generated['prediction'].detach())
|
| 584 |
+
|
| 585 |
+
kp_driving = generated['kp_driving']
|
| 586 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 587 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 588 |
+
|
| 589 |
+
loss_values = {}
|
| 590 |
+
value_total = 0
|
| 591 |
+
for scale in self.scales:
|
| 592 |
+
key = 'prediction_map_%s' % scale
|
| 593 |
+
value = (1 - discriminator_maps_real[key]) ** 2 + discriminator_maps_generated[key] ** 2
|
| 594 |
+
value_total += self.loss_weights['discriminator_gan'] * value.mean()
|
| 595 |
+
loss_values['disc_gan'] = value_total
|
| 596 |
+
|
| 597 |
+
return loss_values
|
FONT/modules/model1.py
ADDED
|
@@ -0,0 +1,539 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from modules.util import AntiAliasInterpolation2d, make_coordinate_grid
|
| 5 |
+
from torchvision import models
|
| 6 |
+
import numpy as np
|
| 7 |
+
from torch.autograd import grad
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class Vgg19(torch.nn.Module):
|
| 11 |
+
"""
|
| 12 |
+
Vgg19 network for perceptual loss. See Sec 3.3.
|
| 13 |
+
"""
|
| 14 |
+
def __init__(self, requires_grad=False):
|
| 15 |
+
super(Vgg19, self).__init__()
|
| 16 |
+
vgg_pretrained_features = models.vgg19(pretrained=True).features
|
| 17 |
+
self.slice1 = torch.nn.Sequential()
|
| 18 |
+
self.slice2 = torch.nn.Sequential()
|
| 19 |
+
self.slice3 = torch.nn.Sequential()
|
| 20 |
+
self.slice4 = torch.nn.Sequential()
|
| 21 |
+
self.slice5 = torch.nn.Sequential()
|
| 22 |
+
for x in range(2):
|
| 23 |
+
self.slice1.add_module(str(x), vgg_pretrained_features[x])
|
| 24 |
+
for x in range(2, 7):
|
| 25 |
+
self.slice2.add_module(str(x), vgg_pretrained_features[x])
|
| 26 |
+
for x in range(7, 12):
|
| 27 |
+
self.slice3.add_module(str(x), vgg_pretrained_features[x])
|
| 28 |
+
for x in range(12, 21):
|
| 29 |
+
self.slice4.add_module(str(x), vgg_pretrained_features[x])
|
| 30 |
+
for x in range(21, 30):
|
| 31 |
+
self.slice5.add_module(str(x), vgg_pretrained_features[x])
|
| 32 |
+
|
| 33 |
+
self.mean = torch.nn.Parameter(data=torch.Tensor(np.array([0.485, 0.456, 0.406]).reshape((1, 3, 1, 1))),
|
| 34 |
+
requires_grad=False)
|
| 35 |
+
self.std = torch.nn.Parameter(data=torch.Tensor(np.array([0.229, 0.224, 0.225]).reshape((1, 3, 1, 1))),
|
| 36 |
+
requires_grad=False)
|
| 37 |
+
|
| 38 |
+
if not requires_grad:
|
| 39 |
+
for param in self.parameters():
|
| 40 |
+
param.requires_grad = False
|
| 41 |
+
|
| 42 |
+
def forward(self, X):
|
| 43 |
+
X = (X - self.mean) / self.std
|
| 44 |
+
h_relu1 = self.slice1(X)
|
| 45 |
+
h_relu2 = self.slice2(h_relu1)
|
| 46 |
+
h_relu3 = self.slice3(h_relu2)
|
| 47 |
+
h_relu4 = self.slice4(h_relu3)
|
| 48 |
+
h_relu5 = self.slice5(h_relu4)
|
| 49 |
+
out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]
|
| 50 |
+
return out
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class ImagePyramide(torch.nn.Module):
|
| 54 |
+
"""
|
| 55 |
+
Create image pyramide for computing pyramide perceptual loss. See Sec 3.3
|
| 56 |
+
"""
|
| 57 |
+
def __init__(self, scales, num_channels):
|
| 58 |
+
super(ImagePyramide, self).__init__()
|
| 59 |
+
downs = {}
|
| 60 |
+
for scale in scales:
|
| 61 |
+
downs[str(scale).replace('.', '-')] = AntiAliasInterpolation2d(num_channels, scale)
|
| 62 |
+
self.downs = nn.ModuleDict(downs)
|
| 63 |
+
|
| 64 |
+
def forward(self, x):
|
| 65 |
+
out_dict = {}
|
| 66 |
+
for scale, down_module in self.downs.items():
|
| 67 |
+
out_dict['prediction_' + str(scale).replace('-', '.')] = down_module(x)
|
| 68 |
+
return out_dict
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Transform:
|
| 72 |
+
"""
|
| 73 |
+
Random tps transformation for equivariance constraints. See Sec 3.3
|
| 74 |
+
"""
|
| 75 |
+
def __init__(self, bs, **kwargs):
|
| 76 |
+
noise = torch.normal(mean=0, std=kwargs['sigma_affine'] * torch.ones([bs, 2, 3]))
|
| 77 |
+
self.theta = noise + torch.eye(2, 3).view(1, 2, 3)
|
| 78 |
+
self.bs = bs
|
| 79 |
+
|
| 80 |
+
if ('sigma_tps' in kwargs) and ('points_tps' in kwargs):
|
| 81 |
+
self.tps = True
|
| 82 |
+
self.control_points = make_coordinate_grid((kwargs['points_tps'], kwargs['points_tps']), type=noise.type())
|
| 83 |
+
self.control_points = self.control_points.unsqueeze(0)
|
| 84 |
+
self.control_params = torch.normal(mean=0,
|
| 85 |
+
std=kwargs['sigma_tps'] * torch.ones([bs, 1, kwargs['points_tps'] ** 2]))
|
| 86 |
+
else:
|
| 87 |
+
self.tps = False
|
| 88 |
+
|
| 89 |
+
def transform_frame(self, frame):
|
| 90 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 91 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 92 |
+
grid = self.warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 93 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 94 |
+
|
| 95 |
+
def inverse_transform_frame(self, frame):
|
| 96 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 97 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 98 |
+
grid = self.inverse_warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 99 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 100 |
+
|
| 101 |
+
def warp_coordinates(self, coordinates):
|
| 102 |
+
theta = self.theta.type(coordinates.type())
|
| 103 |
+
theta = theta.unsqueeze(1)
|
| 104 |
+
transformed = torch.matmul(theta[:, :, :, :2], coordinates.unsqueeze(-1)) + theta[:, :, :, 2:]
|
| 105 |
+
transformed = transformed.squeeze(-1)
|
| 106 |
+
|
| 107 |
+
if self.tps:
|
| 108 |
+
control_points = self.control_points.type(coordinates.type())
|
| 109 |
+
control_params = self.control_params.type(coordinates.type())
|
| 110 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 111 |
+
distances = torch.abs(distances).sum(-1)
|
| 112 |
+
|
| 113 |
+
result = distances ** 2
|
| 114 |
+
result = result * torch.log(distances + 1e-6)
|
| 115 |
+
result = result * control_params
|
| 116 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 117 |
+
transformed = transformed + result
|
| 118 |
+
|
| 119 |
+
return transformed
|
| 120 |
+
|
| 121 |
+
def inverse_warp_coordinates(self, coordinates):
|
| 122 |
+
theta = self.theta.type(coordinates.type())
|
| 123 |
+
theta = theta.unsqueeze(1)
|
| 124 |
+
a = torch.FloatTensor([[[[0,0,1]]]]).repeat([self.bs,1,1,1]).cuda()
|
| 125 |
+
c = torch.cat((theta,a),2)
|
| 126 |
+
d = c.inverse()[:,:,:2,:]
|
| 127 |
+
d = d.type(coordinates.type())
|
| 128 |
+
transformed = torch.matmul(d[:, :, :, :2], coordinates.unsqueeze(-1)) + d[:, :, :, 2:]
|
| 129 |
+
transformed = transformed.squeeze(-1)
|
| 130 |
+
|
| 131 |
+
if self.tps:
|
| 132 |
+
control_points = self.control_points.type(coordinates.type())
|
| 133 |
+
control_params = self.control_params.type(coordinates.type())
|
| 134 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 135 |
+
distances = torch.abs(distances).sum(-1)
|
| 136 |
+
|
| 137 |
+
result = distances ** 2
|
| 138 |
+
result = result * torch.log(distances + 1e-6)
|
| 139 |
+
result = result * control_params
|
| 140 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 141 |
+
transformed = transformed + result
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
return transformed
|
| 145 |
+
|
| 146 |
+
def jacobian(self, coordinates):
|
| 147 |
+
coordinates.requires_grad=True
|
| 148 |
+
new_coordinates = self.warp_coordinates(coordinates)#[4,10,2]
|
| 149 |
+
grad_x = grad(new_coordinates[..., 0].sum(), coordinates, create_graph=True)
|
| 150 |
+
grad_y = grad(new_coordinates[..., 1].sum(), coordinates, create_graph=True)
|
| 151 |
+
jacobian = torch.cat([grad_x[0].unsqueeze(-2), grad_y[0].unsqueeze(-2)], dim=-2)
|
| 152 |
+
return jacobian
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def detach_kp(kp):
|
| 156 |
+
return {key: value.detach() for key, value in kp.items()}
|
| 157 |
+
|
| 158 |
+
class TrainFullModel(torch.nn.Module):
|
| 159 |
+
"""
|
| 160 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
def __init__(self, kp_extractor, emo_feature, kp_extractor_a, audio_feature, generator, discriminator, train_params, device_ids):
|
| 164 |
+
super(TrainFullModel, self).__init__()
|
| 165 |
+
self.kp_extractor = kp_extractor
|
| 166 |
+
self.kp_extractor_a = kp_extractor_a
|
| 167 |
+
# self.emo_detector = emo_detector
|
| 168 |
+
# self.content_encoder = content_encoder
|
| 169 |
+
# self.emotion_encoder = emotion_encoder
|
| 170 |
+
self.audio_feature = audio_feature
|
| 171 |
+
self.emo_feature = emo_feature
|
| 172 |
+
self.generator = generator
|
| 173 |
+
self.discriminator = discriminator
|
| 174 |
+
self.train_params = train_params
|
| 175 |
+
self.scales = train_params['scales']
|
| 176 |
+
self.disc_scales = self.discriminator.scales
|
| 177 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 178 |
+
if torch.cuda.is_available():
|
| 179 |
+
self.pyramid = self.pyramid.cuda()
|
| 180 |
+
|
| 181 |
+
self.loss_weights = train_params['loss_weights']
|
| 182 |
+
|
| 183 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 184 |
+
self.vgg = Vgg19()
|
| 185 |
+
if torch.cuda.is_available():
|
| 186 |
+
self.vgg = self.vgg.cuda()
|
| 187 |
+
|
| 188 |
+
# self.pca = torch.FloatTensor(np.load('/mnt/lustre/jixinya/Home/LRW/list/U_106.npy'))[:, :16].to(device_ids[0])
|
| 189 |
+
# self.mean = torch.FloatTensor(np.load('/mnt/lustre/jixinya/Home/LRW/list/mean_106.npy')).to(device_ids[0])
|
| 190 |
+
self.mse_loss_fn = nn.MSELoss().cuda()
|
| 191 |
+
self.CroEn_loss = nn.CrossEntropyLoss().cuda()
|
| 192 |
+
def forward(self, x):
|
| 193 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 194 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 195 |
+
kp_source = self.kp_extractor(x['example_image'])
|
| 196 |
+
# print(x['name'],len(x['name']))
|
| 197 |
+
kp_driving = []
|
| 198 |
+
kp_emo = []
|
| 199 |
+
for i in range(16):
|
| 200 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i]))
|
| 201 |
+
# kp_emo.append(self.emo_detector(x['driving'][:,i]))
|
| 202 |
+
# print('KP_driving ', file=open('/mnt/lustre/jixinya/Home/fomm_audio/log/LRW_test.txt', 'a'))
|
| 203 |
+
kp_driving_a = [] #x['example_image'],
|
| 204 |
+
deco_out = self.audio_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 205 |
+
# emo_out = self.emo_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 206 |
+
loss_values = {}
|
| 207 |
+
|
| 208 |
+
if self.loss_weights['emo'] != 0:
|
| 209 |
+
|
| 210 |
+
kp_driving_a = []
|
| 211 |
+
fakes = []
|
| 212 |
+
for i in range(16):
|
| 213 |
+
kp_driving_a.append(self.kp_extractor_a(deco_out[:,i]))#
|
| 214 |
+
value = self.kp_extractor_a(deco_out[:,i])['value']
|
| 215 |
+
jacobian = self.kp_extractor_a(deco_out[:,i])['jacobian']
|
| 216 |
+
if self.train_params['type'] == 'linear_4' :
|
| 217 |
+
out, fake = self.emo_feature(x['transformed_driving'][:,i],value,jacobian)
|
| 218 |
+
kp_emo.append(out)
|
| 219 |
+
fakes.append(fake)
|
| 220 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 221 |
+
elif self.train_params['type'] == 'linear_10':
|
| 222 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 223 |
+
|
| 224 |
+
out, fake = self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian)
|
| 225 |
+
kp_emo.append(out)
|
| 226 |
+
fakes.append(fake)
|
| 227 |
+
elif self.train_params['type'] == 'linear_4_new':
|
| 228 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 229 |
+
|
| 230 |
+
out, fake = self.emo_feature.linear_4(x['transformed_driving'][:,i],value,jacobian)
|
| 231 |
+
kp_emo.append(out)
|
| 232 |
+
fakes.append(fake)
|
| 233 |
+
elif self.train_params['type'] == 'linear_np_4':
|
| 234 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 235 |
+
|
| 236 |
+
out, fake = self.emo_feature.linear_np_4(x['transformed_driving'][:,i],value,jacobian)
|
| 237 |
+
kp_emo.append(out)
|
| 238 |
+
fakes.append(fake)
|
| 239 |
+
elif self.train_params['type'] == 'linear_np_10':
|
| 240 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 241 |
+
|
| 242 |
+
out, fake = self.emo_feature.linear_np_10(x['transformed_driving'][:,i],value,jacobian)
|
| 243 |
+
kp_emo.append(out)
|
| 244 |
+
fakes.append(fake)
|
| 245 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 246 |
+
# print('Kp_audio_driving ', file=open('/mnt/lustre/jixinya/Home/fomm_audio/log/LRW_test.txt', 'a'))
|
| 247 |
+
loss_value = 0
|
| 248 |
+
# loss_heatmap = 0
|
| 249 |
+
loss_jacobian = 0
|
| 250 |
+
loss_perceptual = 0
|
| 251 |
+
loss_classify = 0
|
| 252 |
+
kp_all = kp_driving_a
|
| 253 |
+
if self.train_params['smooth'] == True:
|
| 254 |
+
value_all = torch.randn(len(kp_driving),out['value'].shape[0],out['value'].shape[1],out['value'].shape[2]).cuda()
|
| 255 |
+
jacobian_all = torch.randn(len(kp_driving),out['jacobian'].shape[0],out['jacobian'].shape[1],2,2).cuda()
|
| 256 |
+
print(len(kp_driving))
|
| 257 |
+
for i in range(len(kp_driving)):
|
| 258 |
+
# if x['name'][i] == 'LRW':
|
| 259 |
+
# loss_jacobian += (torch.abs(kp_driving[i]['jacobian'] - kp_driving_a[i]['jacobian']).mean())*self.loss_weights['emo']
|
| 260 |
+
|
| 261 |
+
# loss_value += (torch.abs(kp_driving[i]['value'].detach() - kp_driving_a[i]['value']).mean())*self.loss_weights['emo']
|
| 262 |
+
# loss_classify += self.mse_loss_fn(deco_out,deco_out)
|
| 263 |
+
if self.train_params['type'] == 'linear_4' or self.train_params['type'] == 'linear_4_new' or self.train_params['type'] == 'linear_np_4':
|
| 264 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,1] - kp_driving_a[i]['jacobian'][:,1] -kp_emo[i]['jacobian'][:,0]).mean())*self.loss_weights['emo']
|
| 265 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,4] - kp_driving_a[i]['jacobian'][:,4] -kp_emo[i]['jacobian'][:,1]).mean())*self.loss_weights['emo']
|
| 266 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,6] - kp_driving_a[i]['jacobian'][:,6] -kp_emo[i]['jacobian'][:,2]).mean())*self.loss_weights['emo']
|
| 267 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,8] - kp_driving_a[i]['jacobian'][:,8] -kp_emo[i]['jacobian'][:,3]).mean())*self.loss_weights['emo']
|
| 268 |
+
|
| 269 |
+
loss_classify += self.CroEn_loss(fakes[i],x['emotion'])
|
| 270 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,1] .detach() - kp_driving_a[i]['value'][:,1] - kp_emo[i]['value'][:,0] ).mean())*self.loss_weights['emo']
|
| 271 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,4] .detach() - kp_driving_a[i]['value'][:,4] - kp_emo[i]['value'][:,1] ).mean())*self.loss_weights['emo']
|
| 272 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,6] .detach() - kp_driving_a[i]['value'][:,6] - kp_emo[i]['value'][:,2] ).mean())*self.loss_weights['emo']
|
| 273 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,8] .detach() - kp_driving_a[i]['value'][:,8] - kp_emo[i]['value'][:,3] ).mean())*self.loss_weights['emo']
|
| 274 |
+
kp_all[i]['jacobian'][:,1] = kp_emo[i]['jacobian'][:,0] + kp_driving_a[i]['jacobian'][:,1]
|
| 275 |
+
kp_all[i]['jacobian'][:,4] = kp_emo[i]['jacobian'][:,1] + kp_driving_a[i]['jacobian'][:,4]
|
| 276 |
+
kp_all[i]['jacobian'][:,6] = kp_emo[i]['jacobian'][:,2] + kp_driving_a[i]['jacobian'][:,6]
|
| 277 |
+
kp_all[i]['jacobian'][:,8] = kp_emo[i]['jacobian'][:,3] + kp_driving_a[i]['jacobian'][:,8]
|
| 278 |
+
kp_all[i]['value'][:,1] = kp_emo[i]['value'][:,0] + kp_driving_a[i]['value'][:,1]
|
| 279 |
+
kp_all[i]['value'][:,4] = kp_emo[i]['value'][:,1] + kp_driving_a[i]['value'][:,4]
|
| 280 |
+
kp_all[i]['value'][:,6] = kp_emo[i]['value'][:,2] + kp_driving_a[i]['value'][:,6]
|
| 281 |
+
kp_all[i]['value'][:,8] = kp_emo[i]['value'][:,3] + kp_driving_a[i]['value'][:,8]
|
| 282 |
+
elif self.train_params['type'] == 'linear_10' or self.train_params['type'] == 'linear_np_10':
|
| 283 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'] - kp_driving_a[i]['jacobian'] -kp_emo[i]['jacobian']).mean())*self.loss_weights['emo']
|
| 284 |
+
|
| 285 |
+
loss_classify += self.CroEn_loss(fakes[i],x['emotion'])
|
| 286 |
+
loss_value += (torch.abs(kp_driving[i]['value'].detach() - kp_driving_a[i]['value'] - kp_emo[i]['value'] ).mean())*self.loss_weights['emo']
|
| 287 |
+
if self.train_params['smooth'] == True:
|
| 288 |
+
value_all[i]=kp_emo[i]['value']
|
| 289 |
+
jacobian_all[i] = kp_emo[i]['jacobian']
|
| 290 |
+
|
| 291 |
+
# kp_all[i]['value'] = kp_emo[i]['value'] + kp_driving_a[i]['value']
|
| 292 |
+
|
| 293 |
+
loss_values['loss_value'] = loss_value/len(kp_driving)
|
| 294 |
+
# loss_values['loss_heatmap'] = loss_heatmap/len(kp_driving)
|
| 295 |
+
loss_values['loss_jacobian'] = loss_jacobian/len(kp_driving)
|
| 296 |
+
if self.train_params['classify'] == True:
|
| 297 |
+
loss_values['loss_classify'] = loss_classify/len(kp_driving)
|
| 298 |
+
else:
|
| 299 |
+
loss_values['loss_classify'] = self.mse_loss_fn(deco_out,deco_out)
|
| 300 |
+
if self.train_params['smooth'] == True:
|
| 301 |
+
loss_smooth = 0
|
| 302 |
+
loss_smooth += (torch.abs(value_all[2:,:,:,:] + value_all[:-2,:,:,:].detach() -2*value_all[1:-1,:,:,:].detach()).mean())*self.loss_weights['emo'] *100
|
| 303 |
+
loss_smooth += (torch.abs(jacobian_all[2:,:,:,:] + jacobian_all[:-2,:,:,:].detach() -2*jacobian_all[1:-1,:,:,:].detach()).mean())*self.loss_weights['emo'] *100
|
| 304 |
+
loss_values['loss_smooth'] = loss_smooth/len(kp_driving)
|
| 305 |
+
else:
|
| 306 |
+
loss_values['loss_smooth'] = self.mse_loss_fn(deco_out,deco_out)
|
| 307 |
+
if self.train_params['generator'] == 'not':
|
| 308 |
+
loss_values['perceptual'] = self.mse_loss_fn(deco_out,deco_out)
|
| 309 |
+
for i in range(1): #0,len(kp_driving),4
|
| 310 |
+
|
| 311 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_all[i])
|
| 312 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_all})
|
| 313 |
+
elif self.train_params['generator'] == 'visual':
|
| 314 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 315 |
+
|
| 316 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving[i])
|
| 317 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 318 |
+
|
| 319 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 320 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 321 |
+
|
| 322 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 323 |
+
value_total = 0
|
| 324 |
+
for scale in self.scales:
|
| 325 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 326 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 327 |
+
|
| 328 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 329 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 330 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 331 |
+
loss_perceptual += value_total
|
| 332 |
+
|
| 333 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 334 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 335 |
+
elif self.train_params['generator'] == 'audio':
|
| 336 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 337 |
+
|
| 338 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_all[i])
|
| 339 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_all})
|
| 340 |
+
|
| 341 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 342 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 343 |
+
|
| 344 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 345 |
+
value_total = 0
|
| 346 |
+
for scale in self.scales:
|
| 347 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 348 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 349 |
+
|
| 350 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 351 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 352 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 353 |
+
loss_perceptual += value_total
|
| 354 |
+
|
| 355 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 356 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 357 |
+
else:
|
| 358 |
+
print('wrong train_params: ', self.train_params['generator'])
|
| 359 |
+
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
return loss_values,generated
|
| 363 |
+
|
| 364 |
+
class GeneratorFullModel(torch.nn.Module):
|
| 365 |
+
"""
|
| 366 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 367 |
+
"""
|
| 368 |
+
|
| 369 |
+
def __init__(self, kp_extractor, kp_extractor_a, audio_feature, generator, discriminator, train_params):
|
| 370 |
+
super(GeneratorFullModel, self).__init__()
|
| 371 |
+
self.kp_extractor = kp_extractor
|
| 372 |
+
self.kp_extractor_a = kp_extractor_a
|
| 373 |
+
# self.content_encoder = content_encoder
|
| 374 |
+
# self.emotion_encoder = emotion_encoder
|
| 375 |
+
self.audio_feature = audio_feature
|
| 376 |
+
self.generator = generator
|
| 377 |
+
self.discriminator = discriminator
|
| 378 |
+
self.train_params = train_params
|
| 379 |
+
self.scales = train_params['scales']
|
| 380 |
+
self.disc_scales = self.discriminator.scales
|
| 381 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 382 |
+
if torch.cuda.is_available():
|
| 383 |
+
self.pyramid = self.pyramid.cuda()
|
| 384 |
+
|
| 385 |
+
self.loss_weights = train_params['loss_weights']
|
| 386 |
+
|
| 387 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 388 |
+
self.vgg = Vgg19()
|
| 389 |
+
if torch.cuda.is_available():
|
| 390 |
+
self.vgg = self.vgg.cuda()
|
| 391 |
+
|
| 392 |
+
self.pca = torch.FloatTensor(np.load('.../LRW/list/U_106.npy'))[:, :16].cuda()
|
| 393 |
+
self.mean = torch.FloatTensor(np.load('.../LRW/list/mean_106.npy')).cuda()
|
| 394 |
+
|
| 395 |
+
def forward(self, x):
|
| 396 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 397 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 398 |
+
# kp_source = self.kp_extractor(x['source'])
|
| 399 |
+
# kp_source_a = self.kp_extractor_a(x['source'], x['source_cube'], source_a_f)
|
| 400 |
+
# driving_a_f = self.audio_feature(self.content_encoder(x['driving_audio'].unsqueeze(1)), self.emotion_encoder(x['driving_audio'].unsqueeze(1)))
|
| 401 |
+
# driving_a_f = self.audio_feature(x['driving_audio'])
|
| 402 |
+
# kp_driving = self.kp_extractor(x['driving'])
|
| 403 |
+
# kp_driving_a = self.kp_extractor_a(x['driving'], x['driving_cube'], driving_a_f)
|
| 404 |
+
|
| 405 |
+
kp_driving = []
|
| 406 |
+
for i in range(16):
|
| 407 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i],x['driving_landmark'][:,i],self.loss_weights['equivariance_value']))
|
| 408 |
+
|
| 409 |
+
kp_driving_a = []
|
| 410 |
+
fc_out, deco_out = self.audio_feature(x['example_landmark'], x['driving_audio'], x['driving_pose'])
|
| 411 |
+
fake_lmark=fc_out + x['example_landmark'].expand_as(fc_out)
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
fake_lmark = torch.mm( fake_lmark, self.pca.t() )
|
| 415 |
+
fake_lmark = fake_lmark + self.mean.expand_as(fake_lmark)
|
| 416 |
+
|
| 417 |
+
|
| 418 |
+
fake_lmark = fake_lmark.unsqueeze(0)
|
| 419 |
+
|
| 420 |
+
# for i in range(16):
|
| 421 |
+
# kp_driving_a.append()
|
| 422 |
+
|
| 423 |
+
# generated = self.generator(x['source'], kp_source=kp_source, kp_driving=kp_driving)
|
| 424 |
+
# generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 425 |
+
|
| 426 |
+
loss_values = {}
|
| 427 |
+
|
| 428 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 429 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 430 |
+
|
| 431 |
+
if self.loss_weights['audio'] != 0:
|
| 432 |
+
value = torch.abs(kp_source['jacobian'].detach() - kp_source_a['jacobian'].detach()).mean() + torch.abs(kp_driving['jacobian'].detach() - kp_driving_a['jacobian']).mean()
|
| 433 |
+
value = value/2
|
| 434 |
+
loss_values['jacobian'] = value*self.loss_weights['audio']
|
| 435 |
+
value = torch.abs(kp_source['heatmap'].detach() - kp_source_a['heatmap'].detach()).mean() + torch.abs(kp_driving['heatmap'].detach() - kp_driving_a['heatmap']).mean()
|
| 436 |
+
value = value/2
|
| 437 |
+
loss_values['heatmap'] = value*self.loss_weights['audio']
|
| 438 |
+
value = torch.abs(kp_source['value'].detach() - kp_source_a['value'].detach()).mean() + torch.abs(kp_driving['value'].detach() - kp_driving_a['value']).mean()
|
| 439 |
+
value = value/2
|
| 440 |
+
loss_values['value'] = value*self.loss_weights['audio']
|
| 441 |
+
|
| 442 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 443 |
+
value_total = 0
|
| 444 |
+
for scale in self.scales:
|
| 445 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 446 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 447 |
+
|
| 448 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 449 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 450 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 451 |
+
loss_values['perceptual'] = value_total
|
| 452 |
+
|
| 453 |
+
if self.loss_weights['generator_gan'] != 0:
|
| 454 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 455 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 456 |
+
value_total = 0
|
| 457 |
+
for scale in self.disc_scales:
|
| 458 |
+
key = 'prediction_map_%s' % scale
|
| 459 |
+
value = ((1 - discriminator_maps_generated[key]) ** 2).mean()
|
| 460 |
+
value_total += self.loss_weights['generator_gan'] * value
|
| 461 |
+
loss_values['gen_gan'] = value_total
|
| 462 |
+
|
| 463 |
+
if sum(self.loss_weights['feature_matching']) != 0:
|
| 464 |
+
value_total = 0
|
| 465 |
+
for scale in self.disc_scales:
|
| 466 |
+
key = 'feature_maps_%s' % scale
|
| 467 |
+
for i, (a, b) in enumerate(zip(discriminator_maps_real[key], discriminator_maps_generated[key])):
|
| 468 |
+
if self.loss_weights['feature_matching'][i] == 0:
|
| 469 |
+
continue
|
| 470 |
+
value = torch.abs(a - b).mean()
|
| 471 |
+
value_total += self.loss_weights['feature_matching'][i] * value
|
| 472 |
+
loss_values['feature_matching'] = value_total
|
| 473 |
+
|
| 474 |
+
if (self.loss_weights['equivariance_value'] + self.loss_weights['equivariance_jacobian']) != 0:
|
| 475 |
+
transform = Transform(x['driving'].shape[0], **self.train_params['transform_params'])
|
| 476 |
+
transformed_frame = transform.transform_frame(x['driving'])
|
| 477 |
+
transformed_landmark = transform.inverse_warp_coordinates(x['driving_landmark'])
|
| 478 |
+
transformed_kp = self.kp_extractor(transformed_frame)
|
| 479 |
+
|
| 480 |
+
generated['transformed_frame'] = transformed_frame
|
| 481 |
+
generated['transformed_kp'] = transformed_kp
|
| 482 |
+
|
| 483 |
+
## Value loss part
|
| 484 |
+
if self.loss_weights['equivariance_value'] != 0:
|
| 485 |
+
value = torch.abs(kp_driving['value'] - transform.warp_coordinates(transformed_kp['value'])).mean()
|
| 486 |
+
loss_values['equivariance_value'] = self.loss_weights['equivariance_value'] * value
|
| 487 |
+
|
| 488 |
+
## jacobian loss part
|
| 489 |
+
if self.loss_weights['equivariance_jacobian'] != 0:
|
| 490 |
+
jacobian_transformed = torch.matmul(transform.jacobian(transformed_kp['value']),
|
| 491 |
+
transformed_kp['jacobian'])
|
| 492 |
+
|
| 493 |
+
normed_driving = torch.inverse(kp_driving['jacobian'])
|
| 494 |
+
normed_transformed = jacobian_transformed
|
| 495 |
+
value = torch.matmul(normed_driving, normed_transformed)
|
| 496 |
+
|
| 497 |
+
eye = torch.eye(2).view(1, 1, 2, 2).type(value.type())
|
| 498 |
+
|
| 499 |
+
value = torch.abs(eye - value).mean()
|
| 500 |
+
loss_values['equivariance_jacobian'] = self.loss_weights['equivariance_jacobian'] * value
|
| 501 |
+
|
| 502 |
+
return loss_values, generated
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
class DiscriminatorFullModel(torch.nn.Module):
|
| 506 |
+
"""
|
| 507 |
+
Merge all discriminator related updates into single model for better multi-gpu usage
|
| 508 |
+
"""
|
| 509 |
+
|
| 510 |
+
def __init__(self, kp_extractor, generator, discriminator, train_params):
|
| 511 |
+
super(DiscriminatorFullModel, self).__init__()
|
| 512 |
+
self.kp_extractor = kp_extractor
|
| 513 |
+
self.generator = generator
|
| 514 |
+
self.discriminator = discriminator
|
| 515 |
+
self.train_params = train_params
|
| 516 |
+
self.scales = self.discriminator.scales
|
| 517 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 518 |
+
if torch.cuda.is_available():
|
| 519 |
+
self.pyramid = self.pyramid.cuda()
|
| 520 |
+
|
| 521 |
+
self.loss_weights = train_params['loss_weights']
|
| 522 |
+
|
| 523 |
+
def forward(self, x, generated):
|
| 524 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 525 |
+
pyramide_generated = self.pyramid(generated['prediction'].detach())
|
| 526 |
+
|
| 527 |
+
kp_driving = generated['kp_driving']
|
| 528 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 529 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 530 |
+
|
| 531 |
+
loss_values = {}
|
| 532 |
+
value_total = 0
|
| 533 |
+
for scale in self.scales:
|
| 534 |
+
key = 'prediction_map_%s' % scale
|
| 535 |
+
value = (1 - discriminator_maps_real[key]) ** 2 + discriminator_maps_generated[key] ** 2
|
| 536 |
+
value_total += self.loss_weights['discriminator_gan'] * value.mean()
|
| 537 |
+
loss_values['disc_gan'] = value_total
|
| 538 |
+
|
| 539 |
+
return loss_values
|
FONT/modules/model_delta_map.py
ADDED
|
@@ -0,0 +1,500 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from modules.util import AntiAliasInterpolation2d, make_coordinate_grid
|
| 5 |
+
from torchvision import models
|
| 6 |
+
import numpy as np
|
| 7 |
+
from torch.autograd import grad
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class Vgg19(torch.nn.Module):
|
| 11 |
+
"""
|
| 12 |
+
Vgg19 network for perceptual loss. See Sec 3.3.
|
| 13 |
+
"""
|
| 14 |
+
def __init__(self, requires_grad=False):
|
| 15 |
+
super(Vgg19, self).__init__()
|
| 16 |
+
vgg_pretrained_features = models.vgg19(pretrained=True).features
|
| 17 |
+
self.slice1 = torch.nn.Sequential()
|
| 18 |
+
self.slice2 = torch.nn.Sequential()
|
| 19 |
+
self.slice3 = torch.nn.Sequential()
|
| 20 |
+
self.slice4 = torch.nn.Sequential()
|
| 21 |
+
self.slice5 = torch.nn.Sequential()
|
| 22 |
+
for x in range(2):
|
| 23 |
+
self.slice1.add_module(str(x), vgg_pretrained_features[x])
|
| 24 |
+
for x in range(2, 7):
|
| 25 |
+
self.slice2.add_module(str(x), vgg_pretrained_features[x])
|
| 26 |
+
for x in range(7, 12):
|
| 27 |
+
self.slice3.add_module(str(x), vgg_pretrained_features[x])
|
| 28 |
+
for x in range(12, 21):
|
| 29 |
+
self.slice4.add_module(str(x), vgg_pretrained_features[x])
|
| 30 |
+
for x in range(21, 30):
|
| 31 |
+
self.slice5.add_module(str(x), vgg_pretrained_features[x])
|
| 32 |
+
|
| 33 |
+
self.mean = torch.nn.Parameter(data=torch.Tensor(np.array([0.485, 0.456, 0.406]).reshape((1, 3, 1, 1))),
|
| 34 |
+
requires_grad=False)
|
| 35 |
+
self.std = torch.nn.Parameter(data=torch.Tensor(np.array([0.229, 0.224, 0.225]).reshape((1, 3, 1, 1))),
|
| 36 |
+
requires_grad=False)
|
| 37 |
+
|
| 38 |
+
if not requires_grad:
|
| 39 |
+
for param in self.parameters():
|
| 40 |
+
param.requires_grad = False
|
| 41 |
+
|
| 42 |
+
def forward(self, X):
|
| 43 |
+
X = (X - self.mean) / self.std
|
| 44 |
+
h_relu1 = self.slice1(X)
|
| 45 |
+
h_relu2 = self.slice2(h_relu1)
|
| 46 |
+
h_relu3 = self.slice3(h_relu2)
|
| 47 |
+
h_relu4 = self.slice4(h_relu3)
|
| 48 |
+
h_relu5 = self.slice5(h_relu4)
|
| 49 |
+
out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]
|
| 50 |
+
return out
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class ImagePyramide(torch.nn.Module):
|
| 54 |
+
"""
|
| 55 |
+
Create image pyramide for computing pyramide perceptual loss. See Sec 3.3
|
| 56 |
+
"""
|
| 57 |
+
def __init__(self, scales, num_channels):
|
| 58 |
+
super(ImagePyramide, self).__init__()
|
| 59 |
+
downs = {}
|
| 60 |
+
for scale in scales:
|
| 61 |
+
downs[str(scale).replace('.', '-')] = AntiAliasInterpolation2d(num_channels, scale)
|
| 62 |
+
self.downs = nn.ModuleDict(downs)
|
| 63 |
+
|
| 64 |
+
def forward(self, x):
|
| 65 |
+
out_dict = {}
|
| 66 |
+
for scale, down_module in self.downs.items():
|
| 67 |
+
out_dict['prediction_' + str(scale).replace('-', '.')] = down_module(x)
|
| 68 |
+
return out_dict
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Transform:
|
| 72 |
+
"""
|
| 73 |
+
Random tps transformation for equivariance constraints. See Sec 3.3
|
| 74 |
+
"""
|
| 75 |
+
def __init__(self, bs, **kwargs):
|
| 76 |
+
noise = torch.normal(mean=0, std=kwargs['sigma_affine'] * torch.ones([bs, 2, 3]))
|
| 77 |
+
self.theta = noise + torch.eye(2, 3).view(1, 2, 3)
|
| 78 |
+
self.bs = bs
|
| 79 |
+
|
| 80 |
+
if ('sigma_tps' in kwargs) and ('points_tps' in kwargs):
|
| 81 |
+
self.tps = True
|
| 82 |
+
self.control_points = make_coordinate_grid((kwargs['points_tps'], kwargs['points_tps']), type=noise.type())
|
| 83 |
+
self.control_points = self.control_points.unsqueeze(0)
|
| 84 |
+
self.control_params = torch.normal(mean=0,
|
| 85 |
+
std=kwargs['sigma_tps'] * torch.ones([bs, 1, kwargs['points_tps'] ** 2]))
|
| 86 |
+
else:
|
| 87 |
+
self.tps = False
|
| 88 |
+
|
| 89 |
+
def transform_frame(self, frame):
|
| 90 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 91 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 92 |
+
grid = self.warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 93 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 94 |
+
|
| 95 |
+
def inverse_transform_frame(self, frame):
|
| 96 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 97 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 98 |
+
grid = self.inverse_warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 99 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 100 |
+
|
| 101 |
+
def warp_coordinates(self, coordinates):
|
| 102 |
+
theta = self.theta.type(coordinates.type())
|
| 103 |
+
theta = theta.unsqueeze(1)
|
| 104 |
+
transformed = torch.matmul(theta[:, :, :, :2], coordinates.unsqueeze(-1)) + theta[:, :, :, 2:]
|
| 105 |
+
transformed = transformed.squeeze(-1)
|
| 106 |
+
|
| 107 |
+
if self.tps:
|
| 108 |
+
control_points = self.control_points.type(coordinates.type())
|
| 109 |
+
control_params = self.control_params.type(coordinates.type())
|
| 110 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 111 |
+
distances = torch.abs(distances).sum(-1)
|
| 112 |
+
|
| 113 |
+
result = distances ** 2
|
| 114 |
+
result = result * torch.log(distances + 1e-6)
|
| 115 |
+
result = result * control_params
|
| 116 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 117 |
+
transformed = transformed + result
|
| 118 |
+
|
| 119 |
+
return transformed
|
| 120 |
+
|
| 121 |
+
def inverse_warp_coordinates(self, coordinates):
|
| 122 |
+
theta = self.theta.type(coordinates.type())
|
| 123 |
+
theta = theta.unsqueeze(1)
|
| 124 |
+
a = torch.FloatTensor([[[[0,0,1]]]]).repeat([self.bs,1,1,1]).cuda()
|
| 125 |
+
c = torch.cat((theta,a),2)
|
| 126 |
+
d = c.inverse()[:,:,:2,:]
|
| 127 |
+
d = d.type(coordinates.type())
|
| 128 |
+
transformed = torch.matmul(d[:, :, :, :2], coordinates.unsqueeze(-1)) + d[:, :, :, 2:]
|
| 129 |
+
transformed = transformed.squeeze(-1)
|
| 130 |
+
|
| 131 |
+
if self.tps:
|
| 132 |
+
control_points = self.control_points.type(coordinates.type())
|
| 133 |
+
control_params = self.control_params.type(coordinates.type())
|
| 134 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 135 |
+
distances = torch.abs(distances).sum(-1)
|
| 136 |
+
|
| 137 |
+
result = distances ** 2
|
| 138 |
+
result = result * torch.log(distances + 1e-6)
|
| 139 |
+
result = result * control_params
|
| 140 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 141 |
+
transformed = transformed + result
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
return transformed
|
| 145 |
+
|
| 146 |
+
def jacobian(self, coordinates):
|
| 147 |
+
coordinates.requires_grad=True
|
| 148 |
+
new_coordinates = self.warp_coordinates(coordinates)#[4,10,2]
|
| 149 |
+
grad_x = grad(new_coordinates[..., 0].sum(), coordinates, create_graph=True)
|
| 150 |
+
grad_y = grad(new_coordinates[..., 1].sum(), coordinates, create_graph=True)
|
| 151 |
+
jacobian = torch.cat([grad_x[0].unsqueeze(-2), grad_y[0].unsqueeze(-2)], dim=-2)
|
| 152 |
+
return jacobian
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def detach_kp(kp):
|
| 156 |
+
return {key: value.detach() for key, value in kp.items()}
|
| 157 |
+
|
| 158 |
+
class TrainFullModel(torch.nn.Module):
|
| 159 |
+
"""
|
| 160 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
def __init__(self, kp_extractor, emo_feature, kp_extractor_a, audio_feature, generator, discriminator, train_params, device_ids):
|
| 164 |
+
super(TrainFullModel, self).__init__()
|
| 165 |
+
self.kp_extractor = kp_extractor
|
| 166 |
+
self.kp_extractor_a = kp_extractor_a
|
| 167 |
+
# self.emo_detector = emo_detector
|
| 168 |
+
# self.content_encoder = content_encoder
|
| 169 |
+
# self.emotion_encoder = emotion_encoder
|
| 170 |
+
self.audio_feature = audio_feature
|
| 171 |
+
self.emo_feature = emo_feature
|
| 172 |
+
self.generator = generator
|
| 173 |
+
self.discriminator = discriminator
|
| 174 |
+
self.train_params = train_params
|
| 175 |
+
self.scales = train_params['scales']
|
| 176 |
+
self.disc_scales = self.discriminator.scales
|
| 177 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 178 |
+
if torch.cuda.is_available():
|
| 179 |
+
self.pyramid = self.pyramid.cuda()
|
| 180 |
+
|
| 181 |
+
self.loss_weights = train_params['loss_weights']
|
| 182 |
+
|
| 183 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 184 |
+
self.vgg = Vgg19()
|
| 185 |
+
if torch.cuda.is_available():
|
| 186 |
+
self.vgg = self.vgg.cuda()
|
| 187 |
+
|
| 188 |
+
# self.pca = torch.FloatTensor(np.load('/mnt/lustre/jixinya/Home/LRW/list/U_106.npy'))[:, :16].to(device_ids[0])
|
| 189 |
+
# self.mean = torch.FloatTensor(np.load('/mnt/lustre/jixinya/Home/LRW/list/mean_106.npy')).to(device_ids[0])
|
| 190 |
+
self.mse_loss_fn = nn.MSELoss().cuda()
|
| 191 |
+
self.CroEn_loss = nn.CrossEntropyLoss().cuda()
|
| 192 |
+
def forward(self, x):
|
| 193 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 194 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 195 |
+
kp_source = self.kp_extractor(x['example_image'])
|
| 196 |
+
|
| 197 |
+
kp_driving = []
|
| 198 |
+
kp_emo = []
|
| 199 |
+
for i in range(16):
|
| 200 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i]))
|
| 201 |
+
# kp_emo.append(self.emo_detector(x['driving'][:,i]))
|
| 202 |
+
# print('KP_driving ', file=open('/mnt/lustre/jixinya/Home/fomm_audio/log/LRW_test.txt', 'a'))
|
| 203 |
+
kp_driving_a = [] #x['example_image'],
|
| 204 |
+
deco_out = self.audio_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 205 |
+
# emo_out = self.emo_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 206 |
+
loss_values = {}
|
| 207 |
+
|
| 208 |
+
if self.loss_weights['emo'] != 0:
|
| 209 |
+
|
| 210 |
+
kp_driving_a = []
|
| 211 |
+
fakes = []
|
| 212 |
+
for i in range(16):
|
| 213 |
+
kp_driving_a.append(self.kp_extractor_a(deco_out[:,i]))#
|
| 214 |
+
value = self.kp_extractor_a(deco_out[:,i])['value']
|
| 215 |
+
jacobian = self.kp_extractor_a(deco_out[:,i])['jacobian']
|
| 216 |
+
if self.train_params['type'] == 'map_4':
|
| 217 |
+
out, fake = self.emo_feature.map_4(x['transformed_driving'][:,i],value,jacobian)
|
| 218 |
+
kp_emo.append(out)
|
| 219 |
+
fakes.append(fake)
|
| 220 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 221 |
+
elif self.train_params['type'] == 'map_10':
|
| 222 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 223 |
+
|
| 224 |
+
out, fake = self.emo_feature(x['transformed_driving'][:,i],value,jacobian)
|
| 225 |
+
kp_emo.append(out)
|
| 226 |
+
fakes.append(fake)
|
| 227 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 228 |
+
# print('Kp_audio_driving ', file=open('/mnt/lustre/jixinya/Home/fomm_audio/log/LRW_test.txt', 'a'))
|
| 229 |
+
loss_value = 0
|
| 230 |
+
# loss_heatmap = 0
|
| 231 |
+
loss_jacobian = 0
|
| 232 |
+
loss_perceptual = 0
|
| 233 |
+
loss_classify = 0
|
| 234 |
+
kp_all = kp_driving_a
|
| 235 |
+
for i in range(len(kp_driving)):
|
| 236 |
+
if self.train_params['type'] == 'map_4':
|
| 237 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,1] - kp_driving_a[i]['jacobian'][:,1] -kp_emo[i]['jacobian'][:,0]).mean())*self.loss_weights['emo']
|
| 238 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,4] - kp_driving_a[i]['jacobian'][:,4] -kp_emo[i]['jacobian'][:,1]).mean())*self.loss_weights['emo']
|
| 239 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,6] - kp_driving_a[i]['jacobian'][:,6] -kp_emo[i]['jacobian'][:,2]).mean())*self.loss_weights['emo']
|
| 240 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'][:,8] - kp_driving_a[i]['jacobian'][:,8] -kp_emo[i]['jacobian'][:,3]).mean())*self.loss_weights['emo']
|
| 241 |
+
|
| 242 |
+
loss_classify += self.CroEn_loss(fakes[i],x['emotion'])
|
| 243 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,1] .detach() - kp_driving_a[i]['value'][:,1] - kp_emo[i]['value'][:,0] ).mean())*self.loss_weights['emo']
|
| 244 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,4] .detach() - kp_driving_a[i]['value'][:,4] - kp_emo[i]['value'][:,1] ).mean())*self.loss_weights['emo']
|
| 245 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,6] .detach() - kp_driving_a[i]['value'][:,6] - kp_emo[i]['value'][:,2] ).mean())*self.loss_weights['emo']
|
| 246 |
+
loss_value += (torch.abs(kp_driving[i]['value'][:,8] .detach() - kp_driving_a[i]['value'][:,8] - kp_emo[i]['value'][:,3] ).mean())*self.loss_weights['emo']
|
| 247 |
+
kp_all[i]['jacobian'][:,1] = kp_emo[i]['jacobian'][:,0] + kp_driving_a[i]['jacobian'][:,1]
|
| 248 |
+
kp_all[i]['jacobian'][:,4] = kp_emo[i]['jacobian'][:,1] + kp_driving_a[i]['jacobian'][:,4]
|
| 249 |
+
kp_all[i]['jacobian'][:,6] = kp_emo[i]['jacobian'][:,2] + kp_driving_a[i]['jacobian'][:,6]
|
| 250 |
+
kp_all[i]['jacobian'][:,8] = kp_emo[i]['jacobian'][:,3] + kp_driving_a[i]['jacobian'][:,8]
|
| 251 |
+
kp_all[i]['value'][:,1] = kp_emo[i]['value'][:,0] + kp_driving_a[i]['value'][:,1]
|
| 252 |
+
kp_all[i]['value'][:,4] = kp_emo[i]['value'][:,1] + kp_driving_a[i]['value'][:,4]
|
| 253 |
+
kp_all[i]['value'][:,6] = kp_emo[i]['value'][:,2] + kp_driving_a[i]['value'][:,6]
|
| 254 |
+
kp_all[i]['value'][:,8] = kp_emo[i]['value'][:,3] + kp_driving_a[i]['value'][:,8]
|
| 255 |
+
elif self.train_params['type'] == 'map_10':
|
| 256 |
+
loss_jacobian += (torch.abs(kp_driving[i]['jacobian'] - kp_driving_a[i]['jacobian'] -kp_emo[i]['jacobian']).mean())*self.loss_weights['emo']
|
| 257 |
+
|
| 258 |
+
loss_classify += self.CroEn_loss(fakes[i],x['emotion'])
|
| 259 |
+
loss_value += (torch.abs(kp_driving[i]['value'].detach() - kp_driving_a[i]['value'] - kp_emo[i]['value'] ).mean())*self.loss_weights['emo']
|
| 260 |
+
|
| 261 |
+
# kp_all[i]['value'] = kp_emo[i]['value'] + kp_driving_a[i]['value']
|
| 262 |
+
|
| 263 |
+
loss_values['loss_value'] = loss_value/len(kp_driving)
|
| 264 |
+
# loss_values['loss_heatmap'] = loss_heatmap/len(kp_driving)
|
| 265 |
+
loss_values['loss_jacobian'] = loss_jacobian/len(kp_driving)
|
| 266 |
+
loss_values['loss_classify'] = loss_classify/len(kp_driving)
|
| 267 |
+
|
| 268 |
+
if self.train_params['generator'] == 'not':
|
| 269 |
+
loss_values['perceptual'] = self.mse_loss_fn(deco_out,deco_out)
|
| 270 |
+
for i in range(1): #0,len(kp_driving),4
|
| 271 |
+
|
| 272 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_all[i])
|
| 273 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_all})
|
| 274 |
+
elif self.train_params['generator'] == 'visual':
|
| 275 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 276 |
+
|
| 277 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving[i])
|
| 278 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 279 |
+
|
| 280 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 281 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 282 |
+
|
| 283 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 284 |
+
value_total = 0
|
| 285 |
+
for scale in self.scales:
|
| 286 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 287 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 288 |
+
|
| 289 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 290 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 291 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 292 |
+
loss_perceptual += value_total
|
| 293 |
+
|
| 294 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 295 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 296 |
+
elif self.train_params['generator'] == 'audio':
|
| 297 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 298 |
+
|
| 299 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving_a[i])
|
| 300 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving_a})
|
| 301 |
+
|
| 302 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 303 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 304 |
+
|
| 305 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 306 |
+
value_total = 0
|
| 307 |
+
for scale in self.scales:
|
| 308 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 309 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 310 |
+
|
| 311 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 312 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 313 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 314 |
+
loss_perceptual += value_total
|
| 315 |
+
|
| 316 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 317 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 318 |
+
else:
|
| 319 |
+
print('wrong train_params: ', self.train_params['generator'])
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
return loss_values,generated
|
| 324 |
+
|
| 325 |
+
class GeneratorFullModel(torch.nn.Module):
|
| 326 |
+
"""
|
| 327 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 328 |
+
"""
|
| 329 |
+
|
| 330 |
+
def __init__(self, kp_extractor, kp_extractor_a, audio_feature, generator, discriminator, train_params):
|
| 331 |
+
super(GeneratorFullModel, self).__init__()
|
| 332 |
+
self.kp_extractor = kp_extractor
|
| 333 |
+
self.kp_extractor_a = kp_extractor_a
|
| 334 |
+
# self.content_encoder = content_encoder
|
| 335 |
+
# self.emotion_encoder = emotion_encoder
|
| 336 |
+
self.audio_feature = audio_feature
|
| 337 |
+
self.generator = generator
|
| 338 |
+
self.discriminator = discriminator
|
| 339 |
+
self.train_params = train_params
|
| 340 |
+
self.scales = train_params['scales']
|
| 341 |
+
self.disc_scales = self.discriminator.scales
|
| 342 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 343 |
+
if torch.cuda.is_available():
|
| 344 |
+
self.pyramid = self.pyramid.cuda()
|
| 345 |
+
|
| 346 |
+
self.loss_weights = train_params['loss_weights']
|
| 347 |
+
|
| 348 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 349 |
+
self.vgg = Vgg19()
|
| 350 |
+
if torch.cuda.is_available():
|
| 351 |
+
self.vgg = self.vgg.cuda()
|
| 352 |
+
|
| 353 |
+
self.pca = torch.FloatTensor(np.load('.../LRW/list/U_106.npy'))[:, :16].cuda()
|
| 354 |
+
self.mean = torch.FloatTensor(np.load('.../LRW/list/mean_106.npy')).cuda()
|
| 355 |
+
|
| 356 |
+
def forward(self, x):
|
| 357 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 358 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 359 |
+
# kp_source = self.kp_extractor(x['source'])
|
| 360 |
+
# kp_source_a = self.kp_extractor_a(x['source'], x['source_cube'], source_a_f)
|
| 361 |
+
# driving_a_f = self.audio_feature(self.content_encoder(x['driving_audio'].unsqueeze(1)), self.emotion_encoder(x['driving_audio'].unsqueeze(1)))
|
| 362 |
+
# driving_a_f = self.audio_feature(x['driving_audio'])
|
| 363 |
+
# kp_driving = self.kp_extractor(x['driving'])
|
| 364 |
+
# kp_driving_a = self.kp_extractor_a(x['driving'], x['driving_cube'], driving_a_f)
|
| 365 |
+
|
| 366 |
+
kp_driving = []
|
| 367 |
+
for i in range(16):
|
| 368 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i],x['driving_landmark'][:,i],self.loss_weights['equivariance_value']))
|
| 369 |
+
|
| 370 |
+
kp_driving_a = []
|
| 371 |
+
fc_out, deco_out = self.audio_feature(x['example_landmark'], x['driving_audio'], x['driving_pose'])
|
| 372 |
+
fake_lmark=fc_out + x['example_landmark'].expand_as(fc_out)
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
fake_lmark = torch.mm( fake_lmark, self.pca.t() )
|
| 376 |
+
fake_lmark = fake_lmark + self.mean.expand_as(fake_lmark)
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
fake_lmark = fake_lmark.unsqueeze(0)
|
| 380 |
+
|
| 381 |
+
# for i in range(16):
|
| 382 |
+
# kp_driving_a.append()
|
| 383 |
+
|
| 384 |
+
# generated = self.generator(x['source'], kp_source=kp_source, kp_driving=kp_driving)
|
| 385 |
+
# generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 386 |
+
|
| 387 |
+
loss_values = {}
|
| 388 |
+
|
| 389 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 390 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 391 |
+
|
| 392 |
+
if self.loss_weights['audio'] != 0:
|
| 393 |
+
value = torch.abs(kp_source['jacobian'].detach() - kp_source_a['jacobian'].detach()).mean() + torch.abs(kp_driving['jacobian'].detach() - kp_driving_a['jacobian']).mean()
|
| 394 |
+
value = value/2
|
| 395 |
+
loss_values['jacobian'] = value*self.loss_weights['audio']
|
| 396 |
+
value = torch.abs(kp_source['heatmap'].detach() - kp_source_a['heatmap'].detach()).mean() + torch.abs(kp_driving['heatmap'].detach() - kp_driving_a['heatmap']).mean()
|
| 397 |
+
value = value/2
|
| 398 |
+
loss_values['heatmap'] = value*self.loss_weights['audio']
|
| 399 |
+
value = torch.abs(kp_source['value'].detach() - kp_source_a['value'].detach()).mean() + torch.abs(kp_driving['value'].detach() - kp_driving_a['value']).mean()
|
| 400 |
+
value = value/2
|
| 401 |
+
loss_values['value'] = value*self.loss_weights['audio']
|
| 402 |
+
|
| 403 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 404 |
+
value_total = 0
|
| 405 |
+
for scale in self.scales:
|
| 406 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 407 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 408 |
+
|
| 409 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 410 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 411 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 412 |
+
loss_values['perceptual'] = value_total
|
| 413 |
+
|
| 414 |
+
if self.loss_weights['generator_gan'] != 0:
|
| 415 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 416 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 417 |
+
value_total = 0
|
| 418 |
+
for scale in self.disc_scales:
|
| 419 |
+
key = 'prediction_map_%s' % scale
|
| 420 |
+
value = ((1 - discriminator_maps_generated[key]) ** 2).mean()
|
| 421 |
+
value_total += self.loss_weights['generator_gan'] * value
|
| 422 |
+
loss_values['gen_gan'] = value_total
|
| 423 |
+
|
| 424 |
+
if sum(self.loss_weights['feature_matching']) != 0:
|
| 425 |
+
value_total = 0
|
| 426 |
+
for scale in self.disc_scales:
|
| 427 |
+
key = 'feature_maps_%s' % scale
|
| 428 |
+
for i, (a, b) in enumerate(zip(discriminator_maps_real[key], discriminator_maps_generated[key])):
|
| 429 |
+
if self.loss_weights['feature_matching'][i] == 0:
|
| 430 |
+
continue
|
| 431 |
+
value = torch.abs(a - b).mean()
|
| 432 |
+
value_total += self.loss_weights['feature_matching'][i] * value
|
| 433 |
+
loss_values['feature_matching'] = value_total
|
| 434 |
+
|
| 435 |
+
if (self.loss_weights['equivariance_value'] + self.loss_weights['equivariance_jacobian']) != 0:
|
| 436 |
+
transform = Transform(x['driving'].shape[0], **self.train_params['transform_params'])
|
| 437 |
+
transformed_frame = transform.transform_frame(x['driving'])
|
| 438 |
+
transformed_landmark = transform.inverse_warp_coordinates(x['driving_landmark'])
|
| 439 |
+
transformed_kp = self.kp_extractor(transformed_frame)
|
| 440 |
+
|
| 441 |
+
generated['transformed_frame'] = transformed_frame
|
| 442 |
+
generated['transformed_kp'] = transformed_kp
|
| 443 |
+
|
| 444 |
+
## Value loss part
|
| 445 |
+
if self.loss_weights['equivariance_value'] != 0:
|
| 446 |
+
value = torch.abs(kp_driving['value'] - transform.warp_coordinates(transformed_kp['value'])).mean()
|
| 447 |
+
loss_values['equivariance_value'] = self.loss_weights['equivariance_value'] * value
|
| 448 |
+
|
| 449 |
+
## jacobian loss part
|
| 450 |
+
if self.loss_weights['equivariance_jacobian'] != 0:
|
| 451 |
+
jacobian_transformed = torch.matmul(transform.jacobian(transformed_kp['value']),
|
| 452 |
+
transformed_kp['jacobian'])
|
| 453 |
+
|
| 454 |
+
normed_driving = torch.inverse(kp_driving['jacobian'])
|
| 455 |
+
normed_transformed = jacobian_transformed
|
| 456 |
+
value = torch.matmul(normed_driving, normed_transformed)
|
| 457 |
+
|
| 458 |
+
eye = torch.eye(2).view(1, 1, 2, 2).type(value.type())
|
| 459 |
+
|
| 460 |
+
value = torch.abs(eye - value).mean()
|
| 461 |
+
loss_values['equivariance_jacobian'] = self.loss_weights['equivariance_jacobian'] * value
|
| 462 |
+
|
| 463 |
+
return loss_values, generated
|
| 464 |
+
|
| 465 |
+
|
| 466 |
+
class DiscriminatorFullModel(torch.nn.Module):
|
| 467 |
+
"""
|
| 468 |
+
Merge all discriminator related updates into single model for better multi-gpu usage
|
| 469 |
+
"""
|
| 470 |
+
|
| 471 |
+
def __init__(self, kp_extractor, generator, discriminator, train_params):
|
| 472 |
+
super(DiscriminatorFullModel, self).__init__()
|
| 473 |
+
self.kp_extractor = kp_extractor
|
| 474 |
+
self.generator = generator
|
| 475 |
+
self.discriminator = discriminator
|
| 476 |
+
self.train_params = train_params
|
| 477 |
+
self.scales = self.discriminator.scales
|
| 478 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 479 |
+
if torch.cuda.is_available():
|
| 480 |
+
self.pyramid = self.pyramid.cuda()
|
| 481 |
+
|
| 482 |
+
self.loss_weights = train_params['loss_weights']
|
| 483 |
+
|
| 484 |
+
def forward(self, x, generated):
|
| 485 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 486 |
+
pyramide_generated = self.pyramid(generated['prediction'].detach())
|
| 487 |
+
|
| 488 |
+
kp_driving = generated['kp_driving']
|
| 489 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 490 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 491 |
+
|
| 492 |
+
loss_values = {}
|
| 493 |
+
value_total = 0
|
| 494 |
+
for scale in self.scales:
|
| 495 |
+
key = 'prediction_map_%s' % scale
|
| 496 |
+
value = (1 - discriminator_maps_real[key]) ** 2 + discriminator_maps_generated[key] ** 2
|
| 497 |
+
value_total += self.loss_weights['discriminator_gan'] * value.mean()
|
| 498 |
+
loss_values['disc_gan'] = value_total
|
| 499 |
+
|
| 500 |
+
return loss_values
|
FONT/modules/model_gen.py
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
from modules.util import AntiAliasInterpolation2d, make_coordinate_grid
|
| 5 |
+
from torchvision import models
|
| 6 |
+
import numpy as np
|
| 7 |
+
from torch.autograd import grad
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class Vgg19(torch.nn.Module):
|
| 11 |
+
"""
|
| 12 |
+
Vgg19 network for perceptual loss. See Sec 3.3.
|
| 13 |
+
"""
|
| 14 |
+
def __init__(self, requires_grad=False):
|
| 15 |
+
super(Vgg19, self).__init__()
|
| 16 |
+
vgg_pretrained_features = models.vgg19(pretrained=True).features
|
| 17 |
+
self.slice1 = torch.nn.Sequential()
|
| 18 |
+
self.slice2 = torch.nn.Sequential()
|
| 19 |
+
self.slice3 = torch.nn.Sequential()
|
| 20 |
+
self.slice4 = torch.nn.Sequential()
|
| 21 |
+
self.slice5 = torch.nn.Sequential()
|
| 22 |
+
for x in range(2):
|
| 23 |
+
self.slice1.add_module(str(x), vgg_pretrained_features[x])
|
| 24 |
+
for x in range(2, 7):
|
| 25 |
+
self.slice2.add_module(str(x), vgg_pretrained_features[x])
|
| 26 |
+
for x in range(7, 12):
|
| 27 |
+
self.slice3.add_module(str(x), vgg_pretrained_features[x])
|
| 28 |
+
for x in range(12, 21):
|
| 29 |
+
self.slice4.add_module(str(x), vgg_pretrained_features[x])
|
| 30 |
+
for x in range(21, 30):
|
| 31 |
+
self.slice5.add_module(str(x), vgg_pretrained_features[x])
|
| 32 |
+
|
| 33 |
+
self.mean = torch.nn.Parameter(data=torch.Tensor(np.array([0.485, 0.456, 0.406]).reshape((1, 3, 1, 1))),
|
| 34 |
+
requires_grad=False)
|
| 35 |
+
self.std = torch.nn.Parameter(data=torch.Tensor(np.array([0.229, 0.224, 0.225]).reshape((1, 3, 1, 1))),
|
| 36 |
+
requires_grad=False)
|
| 37 |
+
|
| 38 |
+
if not requires_grad:
|
| 39 |
+
for param in self.parameters():
|
| 40 |
+
param.requires_grad = False
|
| 41 |
+
|
| 42 |
+
def forward(self, X):
|
| 43 |
+
X = (X - self.mean) / self.std
|
| 44 |
+
h_relu1 = self.slice1(X)
|
| 45 |
+
h_relu2 = self.slice2(h_relu1)
|
| 46 |
+
h_relu3 = self.slice3(h_relu2)
|
| 47 |
+
h_relu4 = self.slice4(h_relu3)
|
| 48 |
+
h_relu5 = self.slice5(h_relu4)
|
| 49 |
+
out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]
|
| 50 |
+
return out
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class ImagePyramide(torch.nn.Module):
|
| 54 |
+
"""
|
| 55 |
+
Create image pyramide for computing pyramide perceptual loss. See Sec 3.3
|
| 56 |
+
"""
|
| 57 |
+
def __init__(self, scales, num_channels):
|
| 58 |
+
super(ImagePyramide, self).__init__()
|
| 59 |
+
downs = {}
|
| 60 |
+
for scale in scales:
|
| 61 |
+
downs[str(scale).replace('.', '-')] = AntiAliasInterpolation2d(num_channels, scale)
|
| 62 |
+
self.downs = nn.ModuleDict(downs)
|
| 63 |
+
|
| 64 |
+
def forward(self, x):
|
| 65 |
+
out_dict = {}
|
| 66 |
+
for scale, down_module in self.downs.items():
|
| 67 |
+
out_dict['prediction_' + str(scale).replace('-', '.')] = down_module(x)
|
| 68 |
+
return out_dict
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Transform:
|
| 72 |
+
"""
|
| 73 |
+
Random tps transformation for equivariance constraints. See Sec 3.3
|
| 74 |
+
"""
|
| 75 |
+
def __init__(self, bs, **kwargs):
|
| 76 |
+
noise = torch.normal(mean=0, std=kwargs['sigma_affine'] * torch.ones([bs, 2, 3]))
|
| 77 |
+
self.theta = noise + torch.eye(2, 3).view(1, 2, 3)
|
| 78 |
+
self.bs = bs
|
| 79 |
+
|
| 80 |
+
if ('sigma_tps' in kwargs) and ('points_tps' in kwargs):
|
| 81 |
+
self.tps = True
|
| 82 |
+
self.control_points = make_coordinate_grid((kwargs['points_tps'], kwargs['points_tps']), type=noise.type())
|
| 83 |
+
self.control_points = self.control_points.unsqueeze(0)
|
| 84 |
+
self.control_params = torch.normal(mean=0,
|
| 85 |
+
std=kwargs['sigma_tps'] * torch.ones([bs, 1, kwargs['points_tps'] ** 2]))
|
| 86 |
+
else:
|
| 87 |
+
self.tps = False
|
| 88 |
+
|
| 89 |
+
def transform_frame(self, frame):
|
| 90 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 91 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 92 |
+
grid = self.warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 93 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 94 |
+
|
| 95 |
+
def inverse_transform_frame(self, frame):
|
| 96 |
+
grid = make_coordinate_grid(frame.shape[2:], type=frame.type()).unsqueeze(0) #[1,256,256,2]
|
| 97 |
+
grid = grid.view(1, frame.shape[2] * frame.shape[3], 2)
|
| 98 |
+
grid = self.inverse_warp_coordinates(grid).view(self.bs, frame.shape[2], frame.shape[3], 2)
|
| 99 |
+
return F.grid_sample(frame, grid, padding_mode="reflection")
|
| 100 |
+
|
| 101 |
+
def warp_coordinates(self, coordinates):
|
| 102 |
+
theta = self.theta.type(coordinates.type())
|
| 103 |
+
theta = theta.unsqueeze(1)
|
| 104 |
+
transformed = torch.matmul(theta[:, :, :, :2], coordinates.unsqueeze(-1)) + theta[:, :, :, 2:]
|
| 105 |
+
transformed = transformed.squeeze(-1)
|
| 106 |
+
|
| 107 |
+
if self.tps:
|
| 108 |
+
control_points = self.control_points.type(coordinates.type())
|
| 109 |
+
control_params = self.control_params.type(coordinates.type())
|
| 110 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 111 |
+
distances = torch.abs(distances).sum(-1)
|
| 112 |
+
|
| 113 |
+
result = distances ** 2
|
| 114 |
+
result = result * torch.log(distances + 1e-6)
|
| 115 |
+
result = result * control_params
|
| 116 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 117 |
+
transformed = transformed + result
|
| 118 |
+
|
| 119 |
+
return transformed
|
| 120 |
+
|
| 121 |
+
def inverse_warp_coordinates(self, coordinates):
|
| 122 |
+
theta = self.theta.type(coordinates.type())
|
| 123 |
+
theta = theta.unsqueeze(1)
|
| 124 |
+
a = torch.FloatTensor([[[[0,0,1]]]]).repeat([self.bs,1,1,1]).cuda()
|
| 125 |
+
c = torch.cat((theta,a),2)
|
| 126 |
+
d = c.inverse()[:,:,:2,:]
|
| 127 |
+
d = d.type(coordinates.type())
|
| 128 |
+
transformed = torch.matmul(d[:, :, :, :2], coordinates.unsqueeze(-1)) + d[:, :, :, 2:]
|
| 129 |
+
transformed = transformed.squeeze(-1)
|
| 130 |
+
|
| 131 |
+
if self.tps:
|
| 132 |
+
control_points = self.control_points.type(coordinates.type())
|
| 133 |
+
control_params = self.control_params.type(coordinates.type())
|
| 134 |
+
distances = coordinates.view(coordinates.shape[0], -1, 1, 2) - control_points.view(1, 1, -1, 2)
|
| 135 |
+
distances = torch.abs(distances).sum(-1)
|
| 136 |
+
|
| 137 |
+
result = distances ** 2
|
| 138 |
+
result = result * torch.log(distances + 1e-6)
|
| 139 |
+
result = result * control_params
|
| 140 |
+
result = result.sum(dim=2).view(self.bs, coordinates.shape[1], 1)
|
| 141 |
+
transformed = transformed + result
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
return transformed
|
| 145 |
+
|
| 146 |
+
def jacobian(self, coordinates):
|
| 147 |
+
coordinates.requires_grad=True
|
| 148 |
+
new_coordinates = self.warp_coordinates(coordinates)#[4,10,2]
|
| 149 |
+
grad_x = grad(new_coordinates[..., 0].sum(), coordinates, create_graph=True)
|
| 150 |
+
grad_y = grad(new_coordinates[..., 1].sum(), coordinates, create_graph=True)
|
| 151 |
+
jacobian = torch.cat([grad_x[0].unsqueeze(-2), grad_y[0].unsqueeze(-2)], dim=-2)
|
| 152 |
+
return jacobian
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def detach_kp(kp):
|
| 156 |
+
return {key: value.detach() for key, value in kp.items()}
|
| 157 |
+
|
| 158 |
+
class TrainFullModel(torch.nn.Module):
|
| 159 |
+
"""
|
| 160 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
def __init__(self, kp_extractor, emo_feature, kp_extractor_a, audio_feature, generator, discriminator, train_params, device_ids):
|
| 164 |
+
super(TrainFullModel, self).__init__()
|
| 165 |
+
self.kp_extractor = kp_extractor
|
| 166 |
+
self.kp_extractor_a = kp_extractor_a
|
| 167 |
+
# self.emo_detector = emo_detector
|
| 168 |
+
# self.content_encoder = content_encoder
|
| 169 |
+
# self.emotion_encoder = emotion_encoder
|
| 170 |
+
self.audio_feature = audio_feature
|
| 171 |
+
self.emo_feature = emo_feature
|
| 172 |
+
self.generator = generator
|
| 173 |
+
self.discriminator = discriminator
|
| 174 |
+
self.train_params = train_params
|
| 175 |
+
self.scales = train_params['scales']
|
| 176 |
+
self.disc_scales = self.discriminator.scales
|
| 177 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 178 |
+
if torch.cuda.is_available():
|
| 179 |
+
self.pyramid = self.pyramid.cuda()
|
| 180 |
+
|
| 181 |
+
self.loss_weights = train_params['loss_weights']
|
| 182 |
+
|
| 183 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 184 |
+
self.vgg = Vgg19()
|
| 185 |
+
if torch.cuda.is_available():
|
| 186 |
+
self.vgg = self.vgg.cuda()
|
| 187 |
+
|
| 188 |
+
# self.pca = torch.FloatTensor(np.load('/mnt/lustre/jixinya/Home/LRW/list/U_106.npy'))[:, :16].to(device_ids[0])
|
| 189 |
+
# self.mean = torch.FloatTensor(np.load('/mnt/lustre/jixinya/Home/LRW/list/mean_106.npy')).to(device_ids[0])
|
| 190 |
+
self.mse_loss_fn = nn.MSELoss().cuda()
|
| 191 |
+
self.CroEn_loss = nn.CrossEntropyLoss().cuda()
|
| 192 |
+
def forward(self, x):
|
| 193 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 194 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 195 |
+
kp_source = self.kp_extractor(x['example_image'])
|
| 196 |
+
# print(x['name'],len(x['name']))
|
| 197 |
+
kp_driving = []
|
| 198 |
+
kp_emo = []
|
| 199 |
+
for i in range(16):
|
| 200 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i]))
|
| 201 |
+
# kp_emo.append(self.emo_detector(x['driving'][:,i]))
|
| 202 |
+
# print('KP_driving ', file=open('/mnt/lustre/jixinya/Home/fomm_audio/log/LRW_test.txt', 'a'))
|
| 203 |
+
kp_driving_a = [] #x['example_image'],
|
| 204 |
+
deco_out = self.audio_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 205 |
+
# emo_out = self.emo_feature(x['example_image'], x['driving_audio'], x['driving_pose'], self.train_params['jaco_net'])
|
| 206 |
+
loss_values = {}
|
| 207 |
+
|
| 208 |
+
if self.loss_weights['emo'] != 0:
|
| 209 |
+
|
| 210 |
+
kp_driving_a = []
|
| 211 |
+
fakes = []
|
| 212 |
+
for i in range(16):
|
| 213 |
+
kp_driving_a.append(self.kp_extractor_a(deco_out[:,i]))#
|
| 214 |
+
value = self.kp_extractor_a(deco_out[:,i])['value']
|
| 215 |
+
jacobian = self.kp_extractor_a(deco_out[:,i])['jacobian']
|
| 216 |
+
if self.train_params['type'] == 'linear_4' and x['name'][0] == 0:
|
| 217 |
+
out, fake = self.emo_feature(x['transformed_driving'][:,i],value,jacobian)
|
| 218 |
+
kp_emo.append(out)
|
| 219 |
+
fakes.append(fake)
|
| 220 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 221 |
+
elif self.train_params['type'] == 'linear_10' and x['name'][0] == 0:
|
| 222 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 223 |
+
|
| 224 |
+
out, fake = self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian)
|
| 225 |
+
kp_emo.append(out)
|
| 226 |
+
fakes.append(fake)
|
| 227 |
+
elif self.train_params['type'] == 'linear_4_new' and x['name'][0] == 0:
|
| 228 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 229 |
+
|
| 230 |
+
out, fake = self.emo_feature.linear_4(x['transformed_driving'][:,i],value,jacobian)
|
| 231 |
+
kp_emo.append(out)
|
| 232 |
+
fakes.append(fake)
|
| 233 |
+
elif self.train_params['type'] == 'linear_np_4':
|
| 234 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 235 |
+
|
| 236 |
+
out, fake = self.emo_feature.linear_np_4(x['transformed_driving'][:,i],value,jacobian)
|
| 237 |
+
kp_emo.append(out)
|
| 238 |
+
fakes.append(fake)
|
| 239 |
+
elif self.train_params['type'] == 'linear_np_10':
|
| 240 |
+
# kp_emo.append(self.emo_feature.linear_10(x['transformed_driving'][:,i],value,jacobian))
|
| 241 |
+
|
| 242 |
+
out, fake = self.emo_feature.linear_np_10(x['transformed_driving'][:,i],value,jacobian)
|
| 243 |
+
kp_emo.append(out)
|
| 244 |
+
fakes.append(fake)
|
| 245 |
+
# kp_emo.append(self.emo_feature(x['transformed_driving'][:,i],value,jacobian))
|
| 246 |
+
# print('Kp_audio_driving ', file=open('/mnt/lustre/jixinya/Home/fomm_audio/log/LRW_test.txt', 'a'))
|
| 247 |
+
|
| 248 |
+
loss_perceptual = 0
|
| 249 |
+
|
| 250 |
+
kp_all = kp_driving_a
|
| 251 |
+
if self.train_params['smooth'] == True:
|
| 252 |
+
value_all = torch.randn(len(kp_driving),out['value'].shape[0],out['value'].shape[1],out['value'].shape[2]).cuda()
|
| 253 |
+
jacobian_all = torch.randn(len(kp_driving),out['jacobian'].shape[0],out['jacobian'].shape[1],2,2).cuda()
|
| 254 |
+
print(len(kp_driving))
|
| 255 |
+
for i in range(len(kp_driving)):
|
| 256 |
+
# if x['name'][i] == 'LRW':
|
| 257 |
+
# loss_jacobian += (torch.abs(kp_driving[i]['jacobian'] - kp_driving_a[i]['jacobian']).mean())*self.loss_weights['emo']
|
| 258 |
+
|
| 259 |
+
# loss_value += (torch.abs(kp_driving[i]['value'].detach() - kp_driving_a[i]['value']).mean())*self.loss_weights['emo']
|
| 260 |
+
# loss_classify += self.mse_loss_fn(deco_out,deco_out)
|
| 261 |
+
if self.train_params['type'] == 'linear_4' and x['name'][0] == 0:
|
| 262 |
+
|
| 263 |
+
kp_all[i]['jacobian'][:,1] = kp_emo[i]['jacobian'][:,0] + kp_driving_a[i]['jacobian'][:,1]
|
| 264 |
+
kp_all[i]['jacobian'][:,4] = kp_emo[i]['jacobian'][:,1] + kp_driving_a[i]['jacobian'][:,4]
|
| 265 |
+
kp_all[i]['jacobian'][:,6] = kp_emo[i]['jacobian'][:,2] + kp_driving_a[i]['jacobian'][:,6]
|
| 266 |
+
kp_all[i]['jacobian'][:,8] = kp_emo[i]['jacobian'][:,3] + kp_driving_a[i]['jacobian'][:,8]
|
| 267 |
+
kp_all[i]['value'][:,1] = kp_emo[i]['value'][:,0] + kp_driving_a[i]['value'][:,1]
|
| 268 |
+
kp_all[i]['value'][:,4] = kp_emo[i]['value'][:,1] + kp_driving_a[i]['value'][:,4]
|
| 269 |
+
kp_all[i]['value'][:,6] = kp_emo[i]['value'][:,2] + kp_driving_a[i]['value'][:,6]
|
| 270 |
+
kp_all[i]['value'][:,8] = kp_emo[i]['value'][:,3] + kp_driving_a[i]['value'][:,8]
|
| 271 |
+
|
| 272 |
+
# kp_all[i]['value'] = kp_emo[i]['value'] + kp_driving_a[i]['value']
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
if self.train_params['smooth'] == True:
|
| 276 |
+
loss_smooth = 0
|
| 277 |
+
loss_smooth += (torch.abs(value_all[2:,:,:,:] + value_all[:-2,:,:,:].detach() -2*value_all[1:-1,:,:,:].detach()).mean())*self.loss_weights['emo'] *100
|
| 278 |
+
loss_smooth += (torch.abs(jacobian_all[2:,:,:,:] + jacobian_all[:-2,:,:,:].detach() -2*jacobian_all[1:-1,:,:,:].detach()).mean())*self.loss_weights['emo'] *100
|
| 279 |
+
loss_values['loss_smooth'] = loss_smooth/len(kp_driving)
|
| 280 |
+
else:
|
| 281 |
+
loss_values['loss_smooth'] = self.mse_loss_fn(deco_out,deco_out)
|
| 282 |
+
if self.train_params['generator'] == 'not':
|
| 283 |
+
loss_values['perceptual'] = self.mse_loss_fn(deco_out,deco_out)
|
| 284 |
+
for i in range(1): #0,len(kp_driving),4
|
| 285 |
+
|
| 286 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_all[i])
|
| 287 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_all})
|
| 288 |
+
elif self.train_params['generator'] == 'visual':
|
| 289 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 290 |
+
|
| 291 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_driving[i])
|
| 292 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 293 |
+
|
| 294 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 295 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 296 |
+
|
| 297 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 298 |
+
value_total = 0
|
| 299 |
+
for scale in self.scales:
|
| 300 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 301 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 302 |
+
|
| 303 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 304 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 305 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 306 |
+
loss_perceptual += value_total
|
| 307 |
+
|
| 308 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 309 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 310 |
+
elif self.train_params['generator'] == 'audio':
|
| 311 |
+
for i in range(0,len(kp_driving),4): #0,len(kp_driving),4
|
| 312 |
+
|
| 313 |
+
generated = self.generator(x['example_image'], kp_source=kp_source, kp_driving=kp_all[i])
|
| 314 |
+
generated.update({'kp_source': kp_source, 'kp_driving': kp_all})
|
| 315 |
+
|
| 316 |
+
pyramide_real = self.pyramid(x['driving'][:,i])
|
| 317 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 318 |
+
# loss_mse = nn.MSELoss(generated['prediction'],x['driving'][:,i])
|
| 319 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 320 |
+
value_total = 0
|
| 321 |
+
for scale in self.scales:
|
| 322 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 323 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 324 |
+
|
| 325 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 326 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 327 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 328 |
+
loss_perceptual += value_total
|
| 329 |
+
|
| 330 |
+
length = int((len(kp_driving)-1)/4)+1
|
| 331 |
+
loss_values['perceptual'] = loss_perceptual/length
|
| 332 |
+
# loss_values['mse'] = loss_mse/length
|
| 333 |
+
|
| 334 |
+
else:
|
| 335 |
+
print('wrong train_params: ', self.train_params['generator'])
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
return loss_values,generated
|
| 340 |
+
|
| 341 |
+
class GeneratorFullModel(torch.nn.Module):
|
| 342 |
+
"""
|
| 343 |
+
Merge all generator related updates into single model for better multi-gpu usage
|
| 344 |
+
"""
|
| 345 |
+
|
| 346 |
+
def __init__(self, kp_extractor, kp_extractor_a, audio_feature, generator, discriminator, train_params):
|
| 347 |
+
super(GeneratorFullModel, self).__init__()
|
| 348 |
+
self.kp_extractor = kp_extractor
|
| 349 |
+
self.kp_extractor_a = kp_extractor_a
|
| 350 |
+
# self.content_encoder = content_encoder
|
| 351 |
+
# self.emotion_encoder = emotion_encoder
|
| 352 |
+
self.audio_feature = audio_feature
|
| 353 |
+
self.generator = generator
|
| 354 |
+
self.discriminator = discriminator
|
| 355 |
+
self.train_params = train_params
|
| 356 |
+
self.scales = train_params['scales']
|
| 357 |
+
self.disc_scales = self.discriminator.scales
|
| 358 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 359 |
+
if torch.cuda.is_available():
|
| 360 |
+
self.pyramid = self.pyramid.cuda()
|
| 361 |
+
|
| 362 |
+
self.loss_weights = train_params['loss_weights']
|
| 363 |
+
|
| 364 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 365 |
+
self.vgg = Vgg19()
|
| 366 |
+
if torch.cuda.is_available():
|
| 367 |
+
self.vgg = self.vgg.cuda()
|
| 368 |
+
|
| 369 |
+
self.pca = torch.FloatTensor(np.load('.../LRW/list/U_106.npy'))[:, :16].cuda()
|
| 370 |
+
self.mean = torch.FloatTensor(np.load('.../LRW/list/mean_106.npy')).cuda()
|
| 371 |
+
|
| 372 |
+
def forward(self, x):
|
| 373 |
+
# source_a_f = self.audio_feature(x['source_audio'],x['source_lm'],x[])
|
| 374 |
+
# source_a_f = self.audio_feature(self.content_encoder(x['source_audio'].unsqueeze(1)), self.emotion_encoder(x['source_audio'].unsqueeze(1)))
|
| 375 |
+
# kp_source = self.kp_extractor(x['source'])
|
| 376 |
+
# kp_source_a = self.kp_extractor_a(x['source'], x['source_cube'], source_a_f)
|
| 377 |
+
# driving_a_f = self.audio_feature(self.content_encoder(x['driving_audio'].unsqueeze(1)), self.emotion_encoder(x['driving_audio'].unsqueeze(1)))
|
| 378 |
+
# driving_a_f = self.audio_feature(x['driving_audio'])
|
| 379 |
+
# kp_driving = self.kp_extractor(x['driving'])
|
| 380 |
+
# kp_driving_a = self.kp_extractor_a(x['driving'], x['driving_cube'], driving_a_f)
|
| 381 |
+
|
| 382 |
+
kp_driving = []
|
| 383 |
+
for i in range(16):
|
| 384 |
+
kp_driving.append(self.kp_extractor(x['driving'][:,i],x['driving_landmark'][:,i],self.loss_weights['equivariance_value']))
|
| 385 |
+
|
| 386 |
+
kp_driving_a = []
|
| 387 |
+
fc_out, deco_out = self.audio_feature(x['example_landmark'], x['driving_audio'], x['driving_pose'])
|
| 388 |
+
fake_lmark=fc_out + x['example_landmark'].expand_as(fc_out)
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
fake_lmark = torch.mm( fake_lmark, self.pca.t() )
|
| 392 |
+
fake_lmark = fake_lmark + self.mean.expand_as(fake_lmark)
|
| 393 |
+
|
| 394 |
+
|
| 395 |
+
fake_lmark = fake_lmark.unsqueeze(0)
|
| 396 |
+
|
| 397 |
+
# for i in range(16):
|
| 398 |
+
# kp_driving_a.append()
|
| 399 |
+
|
| 400 |
+
# generated = self.generator(x['source'], kp_source=kp_source, kp_driving=kp_driving)
|
| 401 |
+
# generated.update({'kp_source': kp_source, 'kp_driving': kp_driving})
|
| 402 |
+
|
| 403 |
+
loss_values = {}
|
| 404 |
+
|
| 405 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 406 |
+
pyramide_generated = self.pyramid(generated['prediction'])
|
| 407 |
+
|
| 408 |
+
if self.loss_weights['audio'] != 0:
|
| 409 |
+
value = torch.abs(kp_source['jacobian'].detach() - kp_source_a['jacobian'].detach()).mean() + torch.abs(kp_driving['jacobian'].detach() - kp_driving_a['jacobian']).mean()
|
| 410 |
+
value = value/2
|
| 411 |
+
loss_values['jacobian'] = value*self.loss_weights['audio']
|
| 412 |
+
value = torch.abs(kp_source['heatmap'].detach() - kp_source_a['heatmap'].detach()).mean() + torch.abs(kp_driving['heatmap'].detach() - kp_driving_a['heatmap']).mean()
|
| 413 |
+
value = value/2
|
| 414 |
+
loss_values['heatmap'] = value*self.loss_weights['audio']
|
| 415 |
+
value = torch.abs(kp_source['value'].detach() - kp_source_a['value'].detach()).mean() + torch.abs(kp_driving['value'].detach() - kp_driving_a['value']).mean()
|
| 416 |
+
value = value/2
|
| 417 |
+
loss_values['value'] = value*self.loss_weights['audio']
|
| 418 |
+
|
| 419 |
+
if sum(self.loss_weights['perceptual']) != 0:
|
| 420 |
+
value_total = 0
|
| 421 |
+
for scale in self.scales:
|
| 422 |
+
x_vgg = self.vgg(pyramide_generated['prediction_' + str(scale)])
|
| 423 |
+
y_vgg = self.vgg(pyramide_real['prediction_' + str(scale)])
|
| 424 |
+
|
| 425 |
+
for i, weight in enumerate(self.loss_weights['perceptual']):
|
| 426 |
+
value = torch.abs(x_vgg[i] - y_vgg[i].detach()).mean()
|
| 427 |
+
value_total += self.loss_weights['perceptual'][i] * value
|
| 428 |
+
loss_values['perceptual'] = value_total
|
| 429 |
+
|
| 430 |
+
if self.loss_weights['generator_gan'] != 0:
|
| 431 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 432 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 433 |
+
value_total = 0
|
| 434 |
+
for scale in self.disc_scales:
|
| 435 |
+
key = 'prediction_map_%s' % scale
|
| 436 |
+
value = ((1 - discriminator_maps_generated[key]) ** 2).mean()
|
| 437 |
+
value_total += self.loss_weights['generator_gan'] * value
|
| 438 |
+
loss_values['gen_gan'] = value_total
|
| 439 |
+
|
| 440 |
+
if sum(self.loss_weights['feature_matching']) != 0:
|
| 441 |
+
value_total = 0
|
| 442 |
+
for scale in self.disc_scales:
|
| 443 |
+
key = 'feature_maps_%s' % scale
|
| 444 |
+
for i, (a, b) in enumerate(zip(discriminator_maps_real[key], discriminator_maps_generated[key])):
|
| 445 |
+
if self.loss_weights['feature_matching'][i] == 0:
|
| 446 |
+
continue
|
| 447 |
+
value = torch.abs(a - b).mean()
|
| 448 |
+
value_total += self.loss_weights['feature_matching'][i] * value
|
| 449 |
+
loss_values['feature_matching'] = value_total
|
| 450 |
+
|
| 451 |
+
if (self.loss_weights['equivariance_value'] + self.loss_weights['equivariance_jacobian']) != 0:
|
| 452 |
+
transform = Transform(x['driving'].shape[0], **self.train_params['transform_params'])
|
| 453 |
+
transformed_frame = transform.transform_frame(x['driving'])
|
| 454 |
+
transformed_landmark = transform.inverse_warp_coordinates(x['driving_landmark'])
|
| 455 |
+
transformed_kp = self.kp_extractor(transformed_frame)
|
| 456 |
+
|
| 457 |
+
generated['transformed_frame'] = transformed_frame
|
| 458 |
+
generated['transformed_kp'] = transformed_kp
|
| 459 |
+
|
| 460 |
+
## Value loss part
|
| 461 |
+
if self.loss_weights['equivariance_value'] != 0:
|
| 462 |
+
value = torch.abs(kp_driving['value'] - transform.warp_coordinates(transformed_kp['value'])).mean()
|
| 463 |
+
loss_values['equivariance_value'] = self.loss_weights['equivariance_value'] * value
|
| 464 |
+
|
| 465 |
+
## jacobian loss part
|
| 466 |
+
if self.loss_weights['equivariance_jacobian'] != 0:
|
| 467 |
+
jacobian_transformed = torch.matmul(transform.jacobian(transformed_kp['value']),
|
| 468 |
+
transformed_kp['jacobian'])
|
| 469 |
+
|
| 470 |
+
normed_driving = torch.inverse(kp_driving['jacobian'])
|
| 471 |
+
normed_transformed = jacobian_transformed
|
| 472 |
+
value = torch.matmul(normed_driving, normed_transformed)
|
| 473 |
+
|
| 474 |
+
eye = torch.eye(2).view(1, 1, 2, 2).type(value.type())
|
| 475 |
+
|
| 476 |
+
value = torch.abs(eye - value).mean()
|
| 477 |
+
loss_values['equivariance_jacobian'] = self.loss_weights['equivariance_jacobian'] * value
|
| 478 |
+
|
| 479 |
+
return loss_values, generated
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
class DiscriminatorFullModel(torch.nn.Module):
|
| 483 |
+
"""
|
| 484 |
+
Merge all discriminator related updates into single model for better multi-gpu usage
|
| 485 |
+
"""
|
| 486 |
+
|
| 487 |
+
def __init__(self, kp_extractor, generator, discriminator, train_params):
|
| 488 |
+
super(DiscriminatorFullModel, self).__init__()
|
| 489 |
+
self.kp_extractor = kp_extractor
|
| 490 |
+
self.generator = generator
|
| 491 |
+
self.discriminator = discriminator
|
| 492 |
+
self.train_params = train_params
|
| 493 |
+
self.scales = self.discriminator.scales
|
| 494 |
+
self.pyramid = ImagePyramide(self.scales, generator.num_channels)
|
| 495 |
+
if torch.cuda.is_available():
|
| 496 |
+
self.pyramid = self.pyramid.cuda()
|
| 497 |
+
|
| 498 |
+
self.loss_weights = train_params['loss_weights']
|
| 499 |
+
|
| 500 |
+
def forward(self, x, generated):
|
| 501 |
+
pyramide_real = self.pyramid(x['driving'])
|
| 502 |
+
pyramide_generated = self.pyramid(generated['prediction'].detach())
|
| 503 |
+
|
| 504 |
+
kp_driving = generated['kp_driving']
|
| 505 |
+
discriminator_maps_generated = self.discriminator(pyramide_generated, kp=detach_kp(kp_driving))
|
| 506 |
+
discriminator_maps_real = self.discriminator(pyramide_real, kp=detach_kp(kp_driving))
|
| 507 |
+
|
| 508 |
+
loss_values = {}
|
| 509 |
+
value_total = 0
|
| 510 |
+
for scale in self.scales:
|
| 511 |
+
key = 'prediction_map_%s' % scale
|
| 512 |
+
value = (1 - discriminator_maps_real[key]) ** 2 + discriminator_maps_generated[key] ** 2
|
| 513 |
+
value_total += self.loss_weights['discriminator_gan'] * value.mean()
|
| 514 |
+
loss_values['disc_gan'] = value_total
|
| 515 |
+
|
| 516 |
+
return loss_values
|
FONT/modules/ops.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torchvision
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.init as init
|
| 5 |
+
from torch.autograd import Variable
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def linear(channel_in, channel_out,
|
| 9 |
+
activation=nn.ReLU,
|
| 10 |
+
normalizer=nn.BatchNorm1d):
|
| 11 |
+
layer = list()
|
| 12 |
+
bias = True if not normalizer else False
|
| 13 |
+
|
| 14 |
+
layer.append(nn.Linear(channel_in, channel_out, bias=bias))
|
| 15 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 16 |
+
# init.kaiming_normal(layer[0].weight)
|
| 17 |
+
|
| 18 |
+
return nn.Sequential(*layer)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def conv2d(channel_in, channel_out,
|
| 22 |
+
ksize=3, stride=1, padding=1,
|
| 23 |
+
activation=nn.ReLU,
|
| 24 |
+
normalizer=nn.BatchNorm2d):
|
| 25 |
+
layer = list()
|
| 26 |
+
bias = True if not normalizer else False
|
| 27 |
+
|
| 28 |
+
layer.append(nn.Conv2d(channel_in, channel_out,
|
| 29 |
+
ksize, stride, padding,
|
| 30 |
+
bias=bias))
|
| 31 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 32 |
+
# init.kaiming_normal(layer[0].weight)
|
| 33 |
+
|
| 34 |
+
return nn.Sequential(*layer)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def conv_transpose2d(channel_in, channel_out,
|
| 38 |
+
ksize=4, stride=2, padding=1,
|
| 39 |
+
activation=nn.ReLU,
|
| 40 |
+
normalizer=nn.BatchNorm2d):
|
| 41 |
+
layer = list()
|
| 42 |
+
bias = True if not normalizer else False
|
| 43 |
+
|
| 44 |
+
layer.append(nn.ConvTranspose2d(channel_in, channel_out,
|
| 45 |
+
ksize, stride, padding,
|
| 46 |
+
bias=bias))
|
| 47 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 48 |
+
# init.kaiming_normal(layer[0].weight)
|
| 49 |
+
|
| 50 |
+
return nn.Sequential(*layer)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def nn_conv2d(channel_in, channel_out,
|
| 54 |
+
ksize=3, stride=1, padding=1,
|
| 55 |
+
scale_factor=2,
|
| 56 |
+
activation=nn.ReLU,
|
| 57 |
+
normalizer=nn.BatchNorm2d):
|
| 58 |
+
layer = list()
|
| 59 |
+
bias = True if not normalizer else False
|
| 60 |
+
|
| 61 |
+
layer.append(nn.UpsamplingNearest2d(scale_factor=scale_factor))
|
| 62 |
+
layer.append(nn.Conv2d(channel_in, channel_out,
|
| 63 |
+
ksize, stride, padding,
|
| 64 |
+
bias=bias))
|
| 65 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 66 |
+
# init.kaiming_normal(layer[1].weight)
|
| 67 |
+
|
| 68 |
+
return nn.Sequential(*layer)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def _apply(layer, activation, normalizer, channel_out=None):
|
| 72 |
+
if normalizer:
|
| 73 |
+
layer.append(normalizer(channel_out))
|
| 74 |
+
if activation:
|
| 75 |
+
layer.append(activation())
|
| 76 |
+
return layer
|
| 77 |
+
|
FONT/modules/stylegan2.py
ADDED
|
@@ -0,0 +1,923 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Created on Thu Jul 8 01:03:50 2021
|
| 5 |
+
|
| 6 |
+
@author: thea
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
"""
|
| 10 |
+
The network architectures is based on PyTorch implemenation of StyleGAN2Encoder.
|
| 11 |
+
Original PyTorch repo: https://github.com/rosinality/style-based-gan-pytorch
|
| 12 |
+
Origianl StyelGAN2 paper: https://github.com/NVlabs/stylegan2
|
| 13 |
+
We use the network architeture for our single-image traning setting.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
import math
|
| 17 |
+
import numpy as np
|
| 18 |
+
import random
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
from torch import nn
|
| 22 |
+
from torch.nn import functional as F
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def fused_leaky_relu(input, bias, negative_slope=0.2, scale=2 ** 0.5):
|
| 26 |
+
return F.leaky_relu(input + bias, negative_slope) * scale
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class FusedLeakyReLU(nn.Module):
|
| 30 |
+
def __init__(self, channel, negative_slope=0.2, scale=2 ** 0.5):
|
| 31 |
+
super().__init__()
|
| 32 |
+
self.bias = nn.Parameter(torch.zeros(1, channel, 1, 1))
|
| 33 |
+
self.negative_slope = negative_slope
|
| 34 |
+
self.scale = scale
|
| 35 |
+
|
| 36 |
+
def forward(self, input):
|
| 37 |
+
# print("FusedLeakyReLU: ", input.abs().mean())
|
| 38 |
+
out = fused_leaky_relu(input, self.bias,
|
| 39 |
+
self.negative_slope,
|
| 40 |
+
self.scale)
|
| 41 |
+
# print("FusedLeakyReLU: ", out.abs().mean())
|
| 42 |
+
return out
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def upfirdn2d_native(
|
| 46 |
+
input, kernel, up_x, up_y, down_x, down_y, pad_x0, pad_x1, pad_y0, pad_y1
|
| 47 |
+
):
|
| 48 |
+
_, minor, in_h, in_w = input.shape
|
| 49 |
+
kernel_h, kernel_w = kernel.shape
|
| 50 |
+
|
| 51 |
+
out = input.view(-1, minor, in_h, 1, in_w, 1)
|
| 52 |
+
out = F.pad(out, [0, up_x - 1, 0, 0, 0, up_y - 1, 0, 0])
|
| 53 |
+
out = out.view(-1, minor, in_h * up_y, in_w * up_x)
|
| 54 |
+
|
| 55 |
+
out = F.pad(
|
| 56 |
+
out, [max(pad_x0, 0), max(pad_x1, 0), max(pad_y0, 0), max(pad_y1, 0)]
|
| 57 |
+
)
|
| 58 |
+
out = out[
|
| 59 |
+
:,
|
| 60 |
+
:,
|
| 61 |
+
max(-pad_y0, 0): out.shape[2] - max(-pad_y1, 0),
|
| 62 |
+
max(-pad_x0, 0): out.shape[3] - max(-pad_x1, 0),
|
| 63 |
+
]
|
| 64 |
+
|
| 65 |
+
# out = out.permute(0, 3, 1, 2)
|
| 66 |
+
out = out.reshape(
|
| 67 |
+
[-1, 1, in_h * up_y + pad_y0 + pad_y1, in_w * up_x + pad_x0 + pad_x1]
|
| 68 |
+
)
|
| 69 |
+
w = torch.flip(kernel, [0, 1]).view(1, 1, kernel_h, kernel_w)
|
| 70 |
+
out = F.conv2d(out, w)
|
| 71 |
+
out = out.reshape(
|
| 72 |
+
-1,
|
| 73 |
+
minor,
|
| 74 |
+
in_h * up_y + pad_y0 + pad_y1 - kernel_h + 1,
|
| 75 |
+
in_w * up_x + pad_x0 + pad_x1 - kernel_w + 1,
|
| 76 |
+
)
|
| 77 |
+
# out = out.permute(0, 2, 3, 1)
|
| 78 |
+
|
| 79 |
+
return out[:, :, ::down_y, ::down_x]
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def upfirdn2d(input, kernel, up=1, down=1, pad=(0, 0)):
|
| 83 |
+
return upfirdn2d_native(input, kernel, up, up, down, down, pad[0], pad[1], pad[0], pad[1])
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class PixelNorm(nn.Module):
|
| 87 |
+
def __init__(self):
|
| 88 |
+
super().__init__()
|
| 89 |
+
|
| 90 |
+
def forward(self, input):
|
| 91 |
+
return input * torch.rsqrt(torch.mean(input ** 2, dim=1, keepdim=True) + 1e-8)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def make_kernel(k):
|
| 95 |
+
k = torch.tensor(k, dtype=torch.float32)
|
| 96 |
+
|
| 97 |
+
if len(k.shape) == 1:
|
| 98 |
+
k = k[None, :] * k[:, None]
|
| 99 |
+
|
| 100 |
+
k /= k.sum()
|
| 101 |
+
|
| 102 |
+
return k
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class Upsample(nn.Module):
|
| 106 |
+
def __init__(self, kernel, factor=2):
|
| 107 |
+
super().__init__()
|
| 108 |
+
|
| 109 |
+
self.factor = factor
|
| 110 |
+
kernel = make_kernel(kernel) * (factor ** 2)
|
| 111 |
+
self.register_buffer('kernel', kernel)
|
| 112 |
+
|
| 113 |
+
p = kernel.shape[0] - factor
|
| 114 |
+
|
| 115 |
+
pad0 = (p + 1) // 2 + factor - 1
|
| 116 |
+
pad1 = p // 2
|
| 117 |
+
|
| 118 |
+
self.pad = (pad0, pad1)
|
| 119 |
+
|
| 120 |
+
def forward(self, input):
|
| 121 |
+
out = upfirdn2d(input, self.kernel, up=self.factor, down=1, pad=self.pad)
|
| 122 |
+
|
| 123 |
+
return out
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class Downsample(nn.Module):
|
| 127 |
+
def __init__(self, kernel, factor=2):
|
| 128 |
+
super().__init__()
|
| 129 |
+
|
| 130 |
+
self.factor = factor
|
| 131 |
+
kernel = make_kernel(kernel)
|
| 132 |
+
self.register_buffer('kernel', kernel)
|
| 133 |
+
|
| 134 |
+
p = kernel.shape[0] - factor
|
| 135 |
+
|
| 136 |
+
pad0 = (p + 1) // 2
|
| 137 |
+
pad1 = p // 2
|
| 138 |
+
|
| 139 |
+
self.pad = (pad0, pad1)
|
| 140 |
+
|
| 141 |
+
def forward(self, input):
|
| 142 |
+
out = upfirdn2d(input, self.kernel, up=1, down=self.factor, pad=self.pad)
|
| 143 |
+
|
| 144 |
+
return out
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
class Blur(nn.Module):
|
| 148 |
+
def __init__(self, kernel, pad, upsample_factor=1):
|
| 149 |
+
super().__init__()
|
| 150 |
+
|
| 151 |
+
kernel = make_kernel(kernel)
|
| 152 |
+
|
| 153 |
+
if upsample_factor > 1:
|
| 154 |
+
kernel = kernel * (upsample_factor ** 2)
|
| 155 |
+
|
| 156 |
+
self.register_buffer('kernel', kernel)
|
| 157 |
+
|
| 158 |
+
self.pad = pad
|
| 159 |
+
|
| 160 |
+
def forward(self, input):
|
| 161 |
+
out = upfirdn2d(input, self.kernel, pad=self.pad)
|
| 162 |
+
|
| 163 |
+
return out
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
class EqualConv2d(nn.Module):
|
| 167 |
+
def __init__(
|
| 168 |
+
self, in_channel, out_channel, kernel_size, stride=1, padding=0, bias=True
|
| 169 |
+
):
|
| 170 |
+
super().__init__()
|
| 171 |
+
|
| 172 |
+
self.weight = nn.Parameter(
|
| 173 |
+
torch.randn(out_channel, in_channel, kernel_size, kernel_size)
|
| 174 |
+
)
|
| 175 |
+
self.scale = math.sqrt(1) / math.sqrt(in_channel * (kernel_size ** 2))
|
| 176 |
+
|
| 177 |
+
self.stride = stride
|
| 178 |
+
self.padding = padding
|
| 179 |
+
|
| 180 |
+
if bias:
|
| 181 |
+
self.bias = nn.Parameter(torch.zeros(out_channel))
|
| 182 |
+
|
| 183 |
+
else:
|
| 184 |
+
self.bias = None
|
| 185 |
+
|
| 186 |
+
def forward(self, input):
|
| 187 |
+
# print("Before EqualConv2d: ", input.abs().mean())
|
| 188 |
+
out = F.conv2d(
|
| 189 |
+
input,
|
| 190 |
+
self.weight * self.scale,
|
| 191 |
+
bias=self.bias,
|
| 192 |
+
stride=self.stride,
|
| 193 |
+
padding=self.padding,
|
| 194 |
+
)
|
| 195 |
+
# print("After EqualConv2d: ", out.abs().mean(), (self.weight * self.scale).abs().mean())
|
| 196 |
+
|
| 197 |
+
return out
|
| 198 |
+
|
| 199 |
+
def __repr__(self):
|
| 200 |
+
return (
|
| 201 |
+
f'{self.__class__.__name__}({self.weight.shape[1]}, {self.weight.shape[0]},'
|
| 202 |
+
f' {self.weight.shape[2]}, stride={self.stride}, padding={self.padding})'
|
| 203 |
+
)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class EqualLinear(nn.Module):
|
| 207 |
+
def __init__(
|
| 208 |
+
self, in_dim, out_dim, bias=True, bias_init=0, lr_mul=1, activation=None
|
| 209 |
+
):
|
| 210 |
+
super().__init__()
|
| 211 |
+
|
| 212 |
+
self.weight = nn.Parameter(torch.randn(out_dim, in_dim).div_(lr_mul))
|
| 213 |
+
|
| 214 |
+
if bias:
|
| 215 |
+
self.bias = nn.Parameter(torch.zeros(out_dim).fill_(bias_init))
|
| 216 |
+
|
| 217 |
+
else:
|
| 218 |
+
self.bias = None
|
| 219 |
+
|
| 220 |
+
self.activation = activation
|
| 221 |
+
|
| 222 |
+
self.scale = (math.sqrt(1) / math.sqrt(in_dim)) * lr_mul
|
| 223 |
+
self.lr_mul = lr_mul
|
| 224 |
+
|
| 225 |
+
def forward(self, input):
|
| 226 |
+
if self.activation:
|
| 227 |
+
out = F.linear(input, self.weight * self.scale)
|
| 228 |
+
out = fused_leaky_relu(out, self.bias * self.lr_mul)
|
| 229 |
+
|
| 230 |
+
else:
|
| 231 |
+
out = F.linear(
|
| 232 |
+
input, self.weight * self.scale, bias=self.bias * self.lr_mul
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
return out
|
| 236 |
+
|
| 237 |
+
def __repr__(self):
|
| 238 |
+
return (
|
| 239 |
+
f'{self.__class__.__name__}({self.weight.shape[1]}, {self.weight.shape[0]})'
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
class ScaledLeakyReLU(nn.Module):
|
| 244 |
+
def __init__(self, negative_slope=0.2):
|
| 245 |
+
super().__init__()
|
| 246 |
+
|
| 247 |
+
self.negative_slope = negative_slope
|
| 248 |
+
|
| 249 |
+
def forward(self, input):
|
| 250 |
+
out = F.leaky_relu(input, negative_slope=self.negative_slope)
|
| 251 |
+
|
| 252 |
+
return out * math.sqrt(2)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
class ModulatedConv2d(nn.Module):
|
| 256 |
+
def __init__(
|
| 257 |
+
self,
|
| 258 |
+
in_channel,
|
| 259 |
+
out_channel,
|
| 260 |
+
kernel_size,
|
| 261 |
+
style_dim,
|
| 262 |
+
demodulate=True,
|
| 263 |
+
upsample=False,
|
| 264 |
+
downsample=False,
|
| 265 |
+
blur_kernel=[1, 3, 3, 1],
|
| 266 |
+
):
|
| 267 |
+
super().__init__()
|
| 268 |
+
|
| 269 |
+
self.eps = 1e-8
|
| 270 |
+
self.kernel_size = kernel_size
|
| 271 |
+
self.in_channel = in_channel
|
| 272 |
+
self.out_channel = out_channel
|
| 273 |
+
self.upsample = upsample
|
| 274 |
+
self.downsample = downsample
|
| 275 |
+
|
| 276 |
+
if upsample:
|
| 277 |
+
factor = 2
|
| 278 |
+
p = (len(blur_kernel) - factor) - (kernel_size - 1)
|
| 279 |
+
pad0 = (p + 1) // 2 + factor - 1
|
| 280 |
+
pad1 = p // 2 + 1
|
| 281 |
+
|
| 282 |
+
self.blur = Blur(blur_kernel, pad=(pad0, pad1), upsample_factor=factor)
|
| 283 |
+
|
| 284 |
+
if downsample:
|
| 285 |
+
factor = 2
|
| 286 |
+
p = (len(blur_kernel) - factor) + (kernel_size - 1)
|
| 287 |
+
pad0 = (p + 1) // 2
|
| 288 |
+
pad1 = p // 2
|
| 289 |
+
|
| 290 |
+
self.blur = Blur(blur_kernel, pad=(pad0, pad1))
|
| 291 |
+
|
| 292 |
+
fan_in = in_channel * kernel_size ** 2
|
| 293 |
+
self.scale = math.sqrt(1) / math.sqrt(fan_in)
|
| 294 |
+
self.padding = kernel_size // 2
|
| 295 |
+
|
| 296 |
+
self.weight = nn.Parameter(
|
| 297 |
+
torch.randn(1, out_channel, in_channel, kernel_size, kernel_size)
|
| 298 |
+
)
|
| 299 |
+
|
| 300 |
+
if style_dim is not None and style_dim > 0:
|
| 301 |
+
self.modulation = EqualLinear(style_dim, in_channel, bias_init=1)
|
| 302 |
+
|
| 303 |
+
self.demodulate = demodulate
|
| 304 |
+
|
| 305 |
+
def __repr__(self):
|
| 306 |
+
return (
|
| 307 |
+
f'{self.__class__.__name__}({self.in_channel}, {self.out_channel}, {self.kernel_size}, '
|
| 308 |
+
f'upsample={self.upsample}, downsample={self.downsample})'
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
def forward(self, input, style):
|
| 312 |
+
batch, in_channel, height, width = input.shape
|
| 313 |
+
|
| 314 |
+
if style is not None:
|
| 315 |
+
style = self.modulation(style).view(batch, 1, in_channel, 1, 1)
|
| 316 |
+
else:
|
| 317 |
+
style = torch.ones(batch, 1, in_channel, 1, 1).cuda()
|
| 318 |
+
weight = self.scale * self.weight * style
|
| 319 |
+
|
| 320 |
+
if self.demodulate:
|
| 321 |
+
demod = torch.rsqrt(weight.pow(2).sum([2, 3, 4]) + 1e-8)
|
| 322 |
+
weight = weight * demod.view(batch, self.out_channel, 1, 1, 1)
|
| 323 |
+
|
| 324 |
+
weight = weight.view(
|
| 325 |
+
batch * self.out_channel, in_channel, self.kernel_size, self.kernel_size
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
if self.upsample:
|
| 329 |
+
input = input.view(1, batch * in_channel, height, width)
|
| 330 |
+
weight = weight.view(
|
| 331 |
+
batch, self.out_channel, in_channel, self.kernel_size, self.kernel_size
|
| 332 |
+
)
|
| 333 |
+
weight = weight.transpose(1, 2).reshape(
|
| 334 |
+
batch * in_channel, self.out_channel, self.kernel_size, self.kernel_size
|
| 335 |
+
)
|
| 336 |
+
out = F.conv_transpose2d(input, weight, padding=0, stride=2, groups=batch)
|
| 337 |
+
_, _, height, width = out.shape
|
| 338 |
+
out = out.view(batch, self.out_channel, height, width)
|
| 339 |
+
out = self.blur(out)
|
| 340 |
+
|
| 341 |
+
elif self.downsample:
|
| 342 |
+
input = self.blur(input)
|
| 343 |
+
_, _, height, width = input.shape
|
| 344 |
+
input = input.view(1, batch * in_channel, height, width)
|
| 345 |
+
out = F.conv2d(input, weight, padding=0, stride=2, groups=batch)
|
| 346 |
+
_, _, height, width = out.shape
|
| 347 |
+
out = out.view(batch, self.out_channel, height, width)
|
| 348 |
+
|
| 349 |
+
else:
|
| 350 |
+
input = input.view(1, batch * in_channel, height, width)
|
| 351 |
+
out = F.conv2d(input, weight, padding=self.padding, groups=batch)
|
| 352 |
+
_, _, height, width = out.shape
|
| 353 |
+
out = out.view(batch, self.out_channel, height, width)
|
| 354 |
+
|
| 355 |
+
return out
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
class NoiseInjection(nn.Module):
|
| 359 |
+
def __init__(self):
|
| 360 |
+
super().__init__()
|
| 361 |
+
|
| 362 |
+
self.weight = nn.Parameter(torch.zeros(1))
|
| 363 |
+
|
| 364 |
+
def forward(self, image, noise=None):
|
| 365 |
+
if noise is None:
|
| 366 |
+
batch, _, height, width = image.shape
|
| 367 |
+
noise = image.new_empty(batch, 1, height, width).normal_()
|
| 368 |
+
|
| 369 |
+
return image + self.weight * noise
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
class ConstantInput(nn.Module):
|
| 373 |
+
def __init__(self, channel, size=4):
|
| 374 |
+
super().__init__()
|
| 375 |
+
|
| 376 |
+
self.input = nn.Parameter(torch.randn(1, channel, size, size))
|
| 377 |
+
|
| 378 |
+
def forward(self, input):
|
| 379 |
+
batch = input.shape[0]
|
| 380 |
+
out = self.input.repeat(batch, 1, 1, 1)
|
| 381 |
+
|
| 382 |
+
return out
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
class StyledConv(nn.Module):
|
| 386 |
+
def __init__(
|
| 387 |
+
self,
|
| 388 |
+
in_channel,
|
| 389 |
+
out_channel,
|
| 390 |
+
kernel_size,
|
| 391 |
+
style_dim=None,
|
| 392 |
+
upsample=False,
|
| 393 |
+
blur_kernel=[1, 3, 3, 1],
|
| 394 |
+
demodulate=True,
|
| 395 |
+
inject_noise=False, #True
|
| 396 |
+
):
|
| 397 |
+
super().__init__()
|
| 398 |
+
|
| 399 |
+
self.inject_noise = inject_noise
|
| 400 |
+
self.conv = ModulatedConv2d(
|
| 401 |
+
in_channel,
|
| 402 |
+
out_channel,
|
| 403 |
+
kernel_size,
|
| 404 |
+
style_dim,
|
| 405 |
+
upsample=upsample,
|
| 406 |
+
blur_kernel=blur_kernel,
|
| 407 |
+
demodulate=demodulate,
|
| 408 |
+
)
|
| 409 |
+
|
| 410 |
+
self.noise = NoiseInjection()
|
| 411 |
+
# self.bias = nn.Parameter(torch.zeros(1, out_channel, 1, 1))
|
| 412 |
+
# self.activate = ScaledLeakyReLU(0.2)
|
| 413 |
+
self.activate = FusedLeakyReLU(out_channel)
|
| 414 |
+
|
| 415 |
+
def forward(self, input, style=None, noise=None):
|
| 416 |
+
out = self.conv(input, style)
|
| 417 |
+
if self.inject_noise:
|
| 418 |
+
out = self.noise(out, noise=noise)
|
| 419 |
+
# out = out + self.bias
|
| 420 |
+
out = self.activate(out)
|
| 421 |
+
|
| 422 |
+
return out
|
| 423 |
+
|
| 424 |
+
|
| 425 |
+
class ToRGB(nn.Module):
|
| 426 |
+
def __init__(self, in_channel, style_dim, upsample=True, blur_kernel=[1, 3, 3, 1]):
|
| 427 |
+
super().__init__()
|
| 428 |
+
|
| 429 |
+
if upsample:
|
| 430 |
+
self.upsample = Upsample(blur_kernel)
|
| 431 |
+
|
| 432 |
+
self.conv = ModulatedConv2d(in_channel, 3+32, 1, style_dim, demodulate=False)
|
| 433 |
+
self.bias = nn.Parameter(torch.zeros(1, 3+32, 1, 1))
|
| 434 |
+
|
| 435 |
+
def forward(self, input, style, skip=None):
|
| 436 |
+
out = self.conv(input, style)
|
| 437 |
+
out = out + self.bias
|
| 438 |
+
|
| 439 |
+
if skip is not None:
|
| 440 |
+
skip = self.upsample(skip)
|
| 441 |
+
|
| 442 |
+
out = out + skip
|
| 443 |
+
|
| 444 |
+
return out
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
class Generator(nn.Module):
|
| 448 |
+
def __init__(
|
| 449 |
+
self,
|
| 450 |
+
size,
|
| 451 |
+
style_dim,
|
| 452 |
+
n_mlp,
|
| 453 |
+
channel_multiplier=1,
|
| 454 |
+
blur_kernel=[1, 3, 3, 1],
|
| 455 |
+
lr_mlp=0.01,
|
| 456 |
+
):
|
| 457 |
+
super().__init__()
|
| 458 |
+
|
| 459 |
+
self.size = size
|
| 460 |
+
|
| 461 |
+
self.style_dim = style_dim
|
| 462 |
+
|
| 463 |
+
layers = [PixelNorm()]
|
| 464 |
+
|
| 465 |
+
for i in range(n_mlp):
|
| 466 |
+
layers.append(
|
| 467 |
+
EqualLinear(
|
| 468 |
+
style_dim, style_dim, lr_mul=lr_mlp, activation='fused_lrelu'
|
| 469 |
+
)
|
| 470 |
+
)
|
| 471 |
+
|
| 472 |
+
self.style = nn.Sequential(*layers)
|
| 473 |
+
|
| 474 |
+
self.channels = {
|
| 475 |
+
4: 256,
|
| 476 |
+
8: 256,
|
| 477 |
+
16: 128,
|
| 478 |
+
32: 64,
|
| 479 |
+
64: 32 * channel_multiplier,
|
| 480 |
+
128: 16 * channel_multiplier,
|
| 481 |
+
256: 8 * channel_multiplier,
|
| 482 |
+
512: 4 * channel_multiplier,
|
| 483 |
+
1024: 2 * channel_multiplier,
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
self.input = ConstantInput(self.channels[4])
|
| 487 |
+
self.conv1 = StyledConv(
|
| 488 |
+
self.channels[4], self.channels[4], 3, style_dim, blur_kernel=blur_kernel
|
| 489 |
+
)
|
| 490 |
+
self.to_rgb1 = ToRGB(self.channels[4], style_dim, upsample=False)
|
| 491 |
+
|
| 492 |
+
self.log_size = int(math.log(size, 2))
|
| 493 |
+
self.num_layers = (self.log_size - 2) * 2 + 1
|
| 494 |
+
|
| 495 |
+
self.convs = nn.ModuleList()
|
| 496 |
+
self.upsamples = nn.ModuleList()
|
| 497 |
+
self.to_rgbs = nn.ModuleList()
|
| 498 |
+
self.noises = nn.Module()
|
| 499 |
+
|
| 500 |
+
in_channel = self.channels[4]
|
| 501 |
+
|
| 502 |
+
for layer_idx in range(self.num_layers):
|
| 503 |
+
res = (layer_idx + 5) // 2
|
| 504 |
+
shape = [1, 1, 2 ** res, 2 ** res]
|
| 505 |
+
self.noises.register_buffer(f'noise_{layer_idx}', torch.randn(*shape))
|
| 506 |
+
|
| 507 |
+
for i in range(3, self.log_size + 1):
|
| 508 |
+
out_channel = self.channels[2 ** i]
|
| 509 |
+
|
| 510 |
+
self.convs.append(
|
| 511 |
+
StyledConv(
|
| 512 |
+
in_channel,
|
| 513 |
+
out_channel,
|
| 514 |
+
3,
|
| 515 |
+
style_dim,
|
| 516 |
+
upsample=True,
|
| 517 |
+
blur_kernel=blur_kernel,
|
| 518 |
+
)
|
| 519 |
+
)
|
| 520 |
+
|
| 521 |
+
self.convs.append(
|
| 522 |
+
StyledConv(
|
| 523 |
+
out_channel, out_channel, 3, style_dim, blur_kernel=blur_kernel
|
| 524 |
+
)
|
| 525 |
+
)
|
| 526 |
+
|
| 527 |
+
self.to_rgbs.append(ToRGB(out_channel, style_dim))
|
| 528 |
+
|
| 529 |
+
in_channel = out_channel
|
| 530 |
+
|
| 531 |
+
self.n_latent = self.log_size * 2 - 2
|
| 532 |
+
|
| 533 |
+
def make_noise(self):
|
| 534 |
+
device = self.input.input.device
|
| 535 |
+
|
| 536 |
+
noises = [torch.randn(1, 1, 2 ** 2, 2 ** 2, device=device)]
|
| 537 |
+
|
| 538 |
+
for i in range(3, self.log_size + 1):
|
| 539 |
+
for _ in range(2):
|
| 540 |
+
noises.append(torch.randn(1, 1, 2 ** i, 2 ** i, device=device))
|
| 541 |
+
|
| 542 |
+
return noises
|
| 543 |
+
|
| 544 |
+
def mean_latent(self, n_latent):
|
| 545 |
+
latent_in = torch.randn(
|
| 546 |
+
n_latent, self.style_dim, device=self.input.input.device
|
| 547 |
+
)
|
| 548 |
+
latent = self.style(latent_in).mean(0, keepdim=True)
|
| 549 |
+
|
| 550 |
+
return latent
|
| 551 |
+
|
| 552 |
+
def get_latent(self, input):
|
| 553 |
+
return self.style(input)
|
| 554 |
+
|
| 555 |
+
def forward(
|
| 556 |
+
self,
|
| 557 |
+
styles,
|
| 558 |
+
return_latents=False,
|
| 559 |
+
inject_index=None,
|
| 560 |
+
truncation=1,
|
| 561 |
+
truncation_latent=None,
|
| 562 |
+
input_is_latent=False,
|
| 563 |
+
noise=None,
|
| 564 |
+
randomize_noise=True,
|
| 565 |
+
):
|
| 566 |
+
if not input_is_latent:
|
| 567 |
+
styles = [self.style(s) for s in styles]
|
| 568 |
+
|
| 569 |
+
if noise is None:
|
| 570 |
+
if randomize_noise:
|
| 571 |
+
noise = [None] * self.num_layers
|
| 572 |
+
else:
|
| 573 |
+
noise = [
|
| 574 |
+
getattr(self.noises, f'noise_{i}') for i in range(self.num_layers)
|
| 575 |
+
]
|
| 576 |
+
|
| 577 |
+
if truncation < 1:
|
| 578 |
+
style_t = []
|
| 579 |
+
|
| 580 |
+
for style in styles:
|
| 581 |
+
style_t.append(
|
| 582 |
+
truncation_latent + truncation * (style - truncation_latent)
|
| 583 |
+
)
|
| 584 |
+
|
| 585 |
+
styles = style_t
|
| 586 |
+
|
| 587 |
+
if len(styles) < 2:
|
| 588 |
+
inject_index = self.n_latent
|
| 589 |
+
|
| 590 |
+
if len(styles[0].shape) < 3:
|
| 591 |
+
latent = styles[0].unsqueeze(1).repeat(1, inject_index, 1)
|
| 592 |
+
|
| 593 |
+
else:
|
| 594 |
+
latent = styles[0]
|
| 595 |
+
|
| 596 |
+
else:
|
| 597 |
+
if inject_index is None:
|
| 598 |
+
inject_index = random.randint(1, self.n_latent - 1)
|
| 599 |
+
|
| 600 |
+
latent = styles[0].unsqueeze(1).repeat(1, inject_index, 1)
|
| 601 |
+
latent2 = styles[1].unsqueeze(1).repeat(1, self.n_latent - inject_index, 1)
|
| 602 |
+
|
| 603 |
+
latent = torch.cat([latent, latent2], 1)
|
| 604 |
+
|
| 605 |
+
# out = self.input(latent)
|
| 606 |
+
out = styles[0].unsqueeze(-1).unsqueeze(-1).repeat(1,1,4,4)
|
| 607 |
+
out = self.conv1(out, latent[:, 0], noise=noise[0])
|
| 608 |
+
|
| 609 |
+
skip = self.to_rgb1(out, latent[:, 1])
|
| 610 |
+
|
| 611 |
+
i = 1
|
| 612 |
+
for conv1, conv2, noise1, noise2, to_rgb in zip(
|
| 613 |
+
self.convs[::2], self.convs[1::2], noise[1::2], noise[2::2], self.to_rgbs
|
| 614 |
+
):
|
| 615 |
+
out = conv1(out, latent[:, i], noise=noise1)
|
| 616 |
+
out = conv2(out, latent[:, i + 1], noise=noise2)
|
| 617 |
+
skip = to_rgb(out, latent[:, i + 2], skip)
|
| 618 |
+
|
| 619 |
+
i += 2
|
| 620 |
+
|
| 621 |
+
image = skip
|
| 622 |
+
|
| 623 |
+
if return_latents:
|
| 624 |
+
return image, latent
|
| 625 |
+
|
| 626 |
+
else:
|
| 627 |
+
return image, None
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
class ConvLayer(nn.Sequential):
|
| 631 |
+
def __init__(
|
| 632 |
+
self,
|
| 633 |
+
in_channel,
|
| 634 |
+
out_channel,
|
| 635 |
+
kernel_size,
|
| 636 |
+
downsample=False,
|
| 637 |
+
blur_kernel=[1, 3, 3, 1],
|
| 638 |
+
bias=True,
|
| 639 |
+
activate=True,
|
| 640 |
+
):
|
| 641 |
+
layers = []
|
| 642 |
+
|
| 643 |
+
if downsample:
|
| 644 |
+
factor = 2
|
| 645 |
+
p = (len(blur_kernel) - factor) + (kernel_size - 1)
|
| 646 |
+
pad0 = (p + 1) // 2
|
| 647 |
+
pad1 = p // 2
|
| 648 |
+
|
| 649 |
+
layers.append(Blur(blur_kernel, pad=(pad0, pad1)))
|
| 650 |
+
|
| 651 |
+
stride = 2
|
| 652 |
+
self.padding = 0
|
| 653 |
+
|
| 654 |
+
else:
|
| 655 |
+
stride = 1
|
| 656 |
+
self.padding = kernel_size // 2
|
| 657 |
+
|
| 658 |
+
layers.append(
|
| 659 |
+
EqualConv2d(
|
| 660 |
+
in_channel,
|
| 661 |
+
out_channel,
|
| 662 |
+
kernel_size,
|
| 663 |
+
padding=self.padding,
|
| 664 |
+
stride=stride,
|
| 665 |
+
bias=bias and not activate,
|
| 666 |
+
)
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
if activate:
|
| 670 |
+
if bias:
|
| 671 |
+
layers.append(FusedLeakyReLU(out_channel))
|
| 672 |
+
|
| 673 |
+
else:
|
| 674 |
+
layers.append(ScaledLeakyReLU(0.2))
|
| 675 |
+
|
| 676 |
+
super().__init__(*layers)
|
| 677 |
+
|
| 678 |
+
|
| 679 |
+
class ResBlock(nn.Module):
|
| 680 |
+
def __init__(self, in_channel, out_channel, blur_kernel=[1, 3, 3, 1], downsample=True, skip_gain=1.0):
|
| 681 |
+
super().__init__()
|
| 682 |
+
|
| 683 |
+
self.skip_gain = skip_gain
|
| 684 |
+
self.conv1 = ConvLayer(in_channel, in_channel, 3)
|
| 685 |
+
self.conv2 = ConvLayer(in_channel, out_channel, 3, downsample=downsample, blur_kernel=blur_kernel)
|
| 686 |
+
|
| 687 |
+
if in_channel != out_channel or downsample:
|
| 688 |
+
self.skip = ConvLayer(
|
| 689 |
+
in_channel, out_channel, 1, downsample=downsample, activate=False, bias=False
|
| 690 |
+
)
|
| 691 |
+
else:
|
| 692 |
+
self.skip = nn.Identity()
|
| 693 |
+
|
| 694 |
+
def forward(self, input):
|
| 695 |
+
out = self.conv1(input)
|
| 696 |
+
out = self.conv2(out)
|
| 697 |
+
|
| 698 |
+
skip = self.skip(input)
|
| 699 |
+
out = (out * self.skip_gain + skip) / math.sqrt(self.skip_gain ** 2 + 1.0)
|
| 700 |
+
|
| 701 |
+
return out
|
| 702 |
+
|
| 703 |
+
|
| 704 |
+
class StyleGAN2Discriminator(nn.Module):
|
| 705 |
+
def __init__(self, input_nc, ndf=64, n_layers=3, no_antialias=False, size=None, opt=None):
|
| 706 |
+
super().__init__()
|
| 707 |
+
self.opt = opt
|
| 708 |
+
self.stddev_group = 16
|
| 709 |
+
if size is None:
|
| 710 |
+
size = 2 ** int((np.rint(np.log2(min(opt.load_size, opt.crop_size)))))
|
| 711 |
+
if "patch" in self.opt.netD and self.opt.D_patch_size is not None:
|
| 712 |
+
size = 2 ** int(np.log2(self.opt.D_patch_size))
|
| 713 |
+
|
| 714 |
+
blur_kernel = [1, 3, 3, 1]
|
| 715 |
+
channel_multiplier = ndf / 64
|
| 716 |
+
channels = {
|
| 717 |
+
4: min(384, int(4096 * channel_multiplier)),
|
| 718 |
+
8: min(384, int(2048 * channel_multiplier)),
|
| 719 |
+
16: min(384, int(1024 * channel_multiplier)),
|
| 720 |
+
32: min(384, int(512 * channel_multiplier)),
|
| 721 |
+
64: int(256 * channel_multiplier),
|
| 722 |
+
128: int(128 * channel_multiplier),
|
| 723 |
+
256: int(64 * channel_multiplier),
|
| 724 |
+
512: int(32 * channel_multiplier),
|
| 725 |
+
1024: int(16 * channel_multiplier),
|
| 726 |
+
}
|
| 727 |
+
|
| 728 |
+
convs = [ConvLayer(3, channels[size], 1)]
|
| 729 |
+
|
| 730 |
+
log_size = int(math.log(size, 2))
|
| 731 |
+
|
| 732 |
+
in_channel = channels[size]
|
| 733 |
+
|
| 734 |
+
if "smallpatch" in self.opt.netD:
|
| 735 |
+
final_res_log2 = 4
|
| 736 |
+
elif "patch" in self.opt.netD:
|
| 737 |
+
final_res_log2 = 3
|
| 738 |
+
else:
|
| 739 |
+
final_res_log2 = 2
|
| 740 |
+
|
| 741 |
+
for i in range(log_size, final_res_log2, -1):
|
| 742 |
+
out_channel = channels[2 ** (i - 1)]
|
| 743 |
+
|
| 744 |
+
convs.append(ResBlock(in_channel, out_channel, blur_kernel))
|
| 745 |
+
|
| 746 |
+
in_channel = out_channel
|
| 747 |
+
|
| 748 |
+
self.convs = nn.Sequential(*convs)
|
| 749 |
+
|
| 750 |
+
if False and "tile" in self.opt.netD:
|
| 751 |
+
in_channel += 1
|
| 752 |
+
self.final_conv = ConvLayer(in_channel, channels[4], 3)
|
| 753 |
+
if "patch" in self.opt.netD:
|
| 754 |
+
self.final_linear = ConvLayer(channels[4], 1, 3, bias=False, activate=False)
|
| 755 |
+
else:
|
| 756 |
+
self.final_linear = nn.Sequential(
|
| 757 |
+
EqualLinear(channels[4] * 4 * 4, channels[4], activation='fused_lrelu'),
|
| 758 |
+
EqualLinear(channels[4], 1),
|
| 759 |
+
)
|
| 760 |
+
|
| 761 |
+
def forward(self, input, get_minibatch_features=False):
|
| 762 |
+
if "patch" in self.opt.netD and self.opt.D_patch_size is not None:
|
| 763 |
+
h, w = input.size(2), input.size(3)
|
| 764 |
+
y = torch.randint(h - self.opt.D_patch_size, ())
|
| 765 |
+
x = torch.randint(w - self.opt.D_patch_size, ())
|
| 766 |
+
input = input[:, :, y:y + self.opt.D_patch_size, x:x + self.opt.D_patch_size]
|
| 767 |
+
out = input
|
| 768 |
+
for i, conv in enumerate(self.convs):
|
| 769 |
+
out = conv(out)
|
| 770 |
+
# print(i, out.abs().mean())
|
| 771 |
+
# out = self.convs(input)
|
| 772 |
+
|
| 773 |
+
batch, channel, height, width = out.shape
|
| 774 |
+
|
| 775 |
+
if False and "tile" in self.opt.netD:
|
| 776 |
+
group = min(batch, self.stddev_group)
|
| 777 |
+
stddev = out.view(
|
| 778 |
+
group, -1, 1, channel // 1, height, width
|
| 779 |
+
)
|
| 780 |
+
stddev = torch.sqrt(stddev.var(0, unbiased=False) + 1e-8)
|
| 781 |
+
stddev = stddev.mean([2, 3, 4], keepdim=True).squeeze(2)
|
| 782 |
+
stddev = stddev.repeat(group, 1, height, width)
|
| 783 |
+
out = torch.cat([out, stddev], 1)
|
| 784 |
+
|
| 785 |
+
out = self.final_conv(out)
|
| 786 |
+
# print(out.abs().mean())
|
| 787 |
+
|
| 788 |
+
if "patch" not in self.opt.netD:
|
| 789 |
+
out = out.view(batch, -1)
|
| 790 |
+
out = self.final_linear(out)
|
| 791 |
+
|
| 792 |
+
return out
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
class TileStyleGAN2Discriminator(StyleGAN2Discriminator):
|
| 796 |
+
def forward(self, input):
|
| 797 |
+
B, C, H, W = input.size(0), input.size(1), input.size(2), input.size(3)
|
| 798 |
+
size = self.opt.D_patch_size
|
| 799 |
+
Y = H // size
|
| 800 |
+
X = W // size
|
| 801 |
+
input = input.view(B, C, Y, size, X, size)
|
| 802 |
+
input = input.permute(0, 2, 4, 1, 3, 5).contiguous().view(B * Y * X, C, size, size)
|
| 803 |
+
return super().forward(input)
|
| 804 |
+
|
| 805 |
+
|
| 806 |
+
class StyleGAN2Encoder(nn.Module):
|
| 807 |
+
def __init__(self, input_nc, output_nc, ngf=64, use_dropout=False, n_blocks=6, padding_type='reflect', no_antialias=False, opt=None):
|
| 808 |
+
super().__init__()
|
| 809 |
+
assert opt is not None
|
| 810 |
+
self.opt = opt
|
| 811 |
+
channel_multiplier = ngf / 32
|
| 812 |
+
channels = {
|
| 813 |
+
4: min(512, int(round(4096 * channel_multiplier))),
|
| 814 |
+
8: min(512, int(round(2048 * channel_multiplier))),
|
| 815 |
+
16: min(512, int(round(1024 * channel_multiplier))),
|
| 816 |
+
32: min(512, int(round(512 * channel_multiplier))),
|
| 817 |
+
64: int(round(256 * channel_multiplier)),
|
| 818 |
+
128: int(round(128 * channel_multiplier)),
|
| 819 |
+
256: int(round(64 * channel_multiplier)),
|
| 820 |
+
512: int(round(32 * channel_multiplier)),
|
| 821 |
+
1024: int(round(16 * channel_multiplier)),
|
| 822 |
+
}
|
| 823 |
+
|
| 824 |
+
blur_kernel = [1, 3, 3, 1]
|
| 825 |
+
|
| 826 |
+
cur_res = 2 ** int((np.rint(np.log2(min(opt.load_size, opt.crop_size)))))
|
| 827 |
+
convs = [nn.Identity(),
|
| 828 |
+
ConvLayer(3, channels[cur_res], 1)]
|
| 829 |
+
|
| 830 |
+
num_downsampling = self.opt.stylegan2_G_num_downsampling
|
| 831 |
+
for i in range(num_downsampling):
|
| 832 |
+
in_channel = channels[cur_res]
|
| 833 |
+
out_channel = channels[cur_res // 2]
|
| 834 |
+
convs.append(ResBlock(in_channel, out_channel, blur_kernel, downsample=True))
|
| 835 |
+
cur_res = cur_res // 2
|
| 836 |
+
|
| 837 |
+
for i in range(n_blocks // 2):
|
| 838 |
+
n_channel = channels[cur_res]
|
| 839 |
+
convs.append(ResBlock(n_channel, n_channel, downsample=False))
|
| 840 |
+
|
| 841 |
+
self.convs = nn.Sequential(*convs)
|
| 842 |
+
|
| 843 |
+
def forward(self, input, layers=[], get_features=False):
|
| 844 |
+
feat = input
|
| 845 |
+
feats = []
|
| 846 |
+
if -1 in layers:
|
| 847 |
+
layers.append(len(self.convs) - 1)
|
| 848 |
+
for layer_id, layer in enumerate(self.convs):
|
| 849 |
+
feat = layer(feat)
|
| 850 |
+
# print(layer_id, " features ", feat.abs().mean())
|
| 851 |
+
if layer_id in layers:
|
| 852 |
+
feats.append(feat)
|
| 853 |
+
|
| 854 |
+
if get_features:
|
| 855 |
+
return feat, feats
|
| 856 |
+
else:
|
| 857 |
+
return feat
|
| 858 |
+
|
| 859 |
+
|
| 860 |
+
class StyleGAN2Decoder(nn.Module):
|
| 861 |
+
def __init__(self, input_nc, output_nc, ngf=64, use_dropout=False, n_blocks=6, padding_type='reflect', no_antialias=False, opt=None):
|
| 862 |
+
super().__init__()
|
| 863 |
+
assert opt is not None
|
| 864 |
+
self.opt = opt
|
| 865 |
+
|
| 866 |
+
blur_kernel = [1, 3, 3, 1]
|
| 867 |
+
|
| 868 |
+
channel_multiplier = ngf / 32
|
| 869 |
+
channels = {
|
| 870 |
+
4: min(512, int(round(4096 * channel_multiplier))),
|
| 871 |
+
8: min(512, int(round(2048 * channel_multiplier))),
|
| 872 |
+
16: min(512, int(round(1024 * channel_multiplier))),
|
| 873 |
+
32: min(512, int(round(512 * channel_multiplier))),
|
| 874 |
+
64: int(round(256 * channel_multiplier)),
|
| 875 |
+
128: int(round(128 * channel_multiplier)),
|
| 876 |
+
256: int(round(64 * channel_multiplier)),
|
| 877 |
+
512: int(round(32 * channel_multiplier)),
|
| 878 |
+
1024: int(round(16 * channel_multiplier)),
|
| 879 |
+
}
|
| 880 |
+
|
| 881 |
+
num_downsampling = self.opt.stylegan2_G_num_downsampling
|
| 882 |
+
cur_res = 2 ** int((np.rint(np.log2(min(opt.load_size, opt.crop_size))))) // (2 ** num_downsampling)
|
| 883 |
+
convs = []
|
| 884 |
+
|
| 885 |
+
for i in range(n_blocks // 2):
|
| 886 |
+
n_channel = channels[cur_res]
|
| 887 |
+
convs.append(ResBlock(n_channel, n_channel, downsample=False))
|
| 888 |
+
|
| 889 |
+
for i in range(num_downsampling):
|
| 890 |
+
in_channel = channels[cur_res]
|
| 891 |
+
out_channel = channels[cur_res * 2]
|
| 892 |
+
inject_noise = "small" not in self.opt.netG
|
| 893 |
+
convs.append(
|
| 894 |
+
StyledConv(in_channel, out_channel, 3, upsample=True, blur_kernel=blur_kernel, inject_noise=inject_noise)
|
| 895 |
+
)
|
| 896 |
+
cur_res = cur_res * 2
|
| 897 |
+
|
| 898 |
+
convs.append(ConvLayer(channels[cur_res], 3, 1))
|
| 899 |
+
|
| 900 |
+
self.convs = nn.Sequential(*convs)
|
| 901 |
+
|
| 902 |
+
def forward(self, input):
|
| 903 |
+
return self.convs(input)
|
| 904 |
+
|
| 905 |
+
|
| 906 |
+
class StyleGAN2Generator(nn.Module):
|
| 907 |
+
def __init__(self, input_nc, output_nc, ngf=64, use_dropout=False, n_blocks=6, padding_type='reflect', no_antialias=False, opt=None):
|
| 908 |
+
super().__init__()
|
| 909 |
+
self.opt = opt
|
| 910 |
+
self.encoder = StyleGAN2Encoder(input_nc, output_nc, ngf, use_dropout, n_blocks, padding_type, no_antialias, opt)
|
| 911 |
+
self.decoder = StyleGAN2Decoder(input_nc, output_nc, ngf, use_dropout, n_blocks, padding_type, no_antialias, opt)
|
| 912 |
+
|
| 913 |
+
def forward(self, input, layers=[], encode_only=False):
|
| 914 |
+
feat, feats = self.encoder(input, layers, True)
|
| 915 |
+
if encode_only:
|
| 916 |
+
return feats
|
| 917 |
+
else:
|
| 918 |
+
fake = self.decoder(feat)
|
| 919 |
+
|
| 920 |
+
if len(layers) > 0:
|
| 921 |
+
return fake, feats
|
| 922 |
+
else:
|
| 923 |
+
return fake
|
FONT/modules/util.py
ADDED
|
@@ -0,0 +1,2008 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from torch import nn
|
| 2 |
+
|
| 3 |
+
import torch.nn.functional as F
|
| 4 |
+
import torch
|
| 5 |
+
import numpy as np
|
| 6 |
+
import cv2
|
| 7 |
+
from FONT.sync_batchnorm import SynchronizedBatchNorm2d as BatchNorm2d
|
| 8 |
+
|
| 9 |
+
from .stylegan2 import Generator
|
| 10 |
+
|
| 11 |
+
import torch.nn as nn
|
| 12 |
+
import math
|
| 13 |
+
import torch.utils.model_zoo as model_zoo
|
| 14 |
+
from .function import adaptive_instance_normalization as adain
|
| 15 |
+
|
| 16 |
+
import pdb
|
| 17 |
+
from .conv import Conv2dTranspose, Conv2d, nonorm_Conv2d
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# Misc
|
| 22 |
+
img2mse = lambda x, y : torch.mean((x - y) ** 2)
|
| 23 |
+
mse2psnr = lambda x : -10. * torch.log(x) / torch.log(torch.Tensor([10.]))
|
| 24 |
+
to8b = lambda x : (255*np.clip(x,0,1)).astype(np.uint8)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class InstanceNorm(nn.Module):
|
| 28 |
+
def __init__(self, epsilon=1e-8):
|
| 29 |
+
"""
|
| 30 |
+
@notice: avoid in-place ops.
|
| 31 |
+
https://discuss.pytorch.org/t/encounter-the-runtimeerror-one-of-the-variables-needed-for-gradient-computation-has-been-modified-by-an-inplace-operation/836/3
|
| 32 |
+
"""
|
| 33 |
+
super(InstanceNorm, self).__init__()
|
| 34 |
+
self.epsilon = epsilon
|
| 35 |
+
|
| 36 |
+
def forward(self, x):
|
| 37 |
+
x = x - torch.mean(x, (2, 3), True)
|
| 38 |
+
tmp = torch.mul(x, x) # or x ** 2
|
| 39 |
+
tmp = torch.rsqrt(torch.mean(tmp, (2, 3), True) + self.epsilon)
|
| 40 |
+
return x * tmp
|
| 41 |
+
|
| 42 |
+
class ApplyStyle(nn.Module):
|
| 43 |
+
"""
|
| 44 |
+
@ref: https://github.com/lernapparat/lernapparat/blob/master/style_gan/pytorch_style_gan.ipynb
|
| 45 |
+
"""
|
| 46 |
+
def __init__(self, latent_size, channels, use_wscale):
|
| 47 |
+
super(ApplyStyle, self).__init__()
|
| 48 |
+
self.linear = FC(latent_size,
|
| 49 |
+
channels * 2,
|
| 50 |
+
gain=1.0,
|
| 51 |
+
use_wscale=use_wscale)
|
| 52 |
+
|
| 53 |
+
def forward(self, x, latent):
|
| 54 |
+
style = self.linear(latent) # style => [batch_size, n_channels*2]
|
| 55 |
+
shape = [-1, 2, x.size(1), 1, 1]
|
| 56 |
+
style = style.view(shape) # [batch_size, 2, n_channels, ...]
|
| 57 |
+
x = x * (style[:, 0] + 1.) + style[:, 1]
|
| 58 |
+
return x
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class FC(nn.Module):
|
| 62 |
+
def __init__(self,
|
| 63 |
+
in_channels,
|
| 64 |
+
out_channels,
|
| 65 |
+
gain=2**(0.5),
|
| 66 |
+
use_wscale=False,
|
| 67 |
+
lrmul=1.0,
|
| 68 |
+
bias=True):
|
| 69 |
+
"""
|
| 70 |
+
The complete conversion of Dense/FC/Linear Layer of original Tensorflow version.
|
| 71 |
+
"""
|
| 72 |
+
super(FC, self).__init__()
|
| 73 |
+
he_std = gain * in_channels ** (-0.5) # He init
|
| 74 |
+
if use_wscale:
|
| 75 |
+
init_std = 1.0 / lrmul
|
| 76 |
+
self.w_lrmul = he_std * lrmul
|
| 77 |
+
else:
|
| 78 |
+
init_std = he_std / lrmul
|
| 79 |
+
self.w_lrmul = lrmul
|
| 80 |
+
|
| 81 |
+
self.weight = torch.nn.Parameter(torch.randn(out_channels, in_channels) * init_std)
|
| 82 |
+
if bias:
|
| 83 |
+
self.bias = torch.nn.Parameter(torch.zeros(out_channels))
|
| 84 |
+
self.b_lrmul = lrmul
|
| 85 |
+
else:
|
| 86 |
+
self.bias = None
|
| 87 |
+
|
| 88 |
+
def forward(self, x):
|
| 89 |
+
if self.bias is not None:
|
| 90 |
+
out = F.linear(x, self.weight * self.w_lrmul, self.bias * self.b_lrmul)
|
| 91 |
+
else:
|
| 92 |
+
out = F.linear(x, self.weight * self.w_lrmul)
|
| 93 |
+
out = F.leaky_relu(out, 0.2, inplace=True)
|
| 94 |
+
return out
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
# Positional encoding (section 5.1)
|
| 98 |
+
class Embedder:
|
| 99 |
+
def __init__(self, **kwargs):
|
| 100 |
+
self.kwargs = kwargs
|
| 101 |
+
self.create_embedding_fn()
|
| 102 |
+
|
| 103 |
+
def create_embedding_fn(self):
|
| 104 |
+
embed_fns = []
|
| 105 |
+
d = self.kwargs['input_dims']
|
| 106 |
+
out_dim = 0
|
| 107 |
+
if self.kwargs['include_input']:
|
| 108 |
+
embed_fns.append(lambda x : x)
|
| 109 |
+
out_dim += d
|
| 110 |
+
|
| 111 |
+
max_freq = self.kwargs['max_freq_log2']
|
| 112 |
+
N_freqs = self.kwargs['num_freqs']
|
| 113 |
+
|
| 114 |
+
if self.kwargs['log_sampling']:
|
| 115 |
+
freq_bands = 2.**torch.linspace(0., max_freq, steps=N_freqs)
|
| 116 |
+
else:
|
| 117 |
+
freq_bands = torch.linspace(2.**0., 2.**max_freq, steps=N_freqs)
|
| 118 |
+
|
| 119 |
+
for freq in freq_bands:
|
| 120 |
+
for p_fn in self.kwargs['periodic_fns']:
|
| 121 |
+
embed_fns.append(lambda x, p_fn=p_fn, freq=freq : p_fn(x * freq))
|
| 122 |
+
out_dim += d
|
| 123 |
+
|
| 124 |
+
self.embed_fns = embed_fns
|
| 125 |
+
self.out_dim = out_dim
|
| 126 |
+
|
| 127 |
+
def embed(self, inputs):
|
| 128 |
+
return torch.cat([fn(inputs) for fn in self.embed_fns], -1)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def get_embedder(multires, i=0):
|
| 132 |
+
if i == -1:
|
| 133 |
+
return nn.Identity(), 6
|
| 134 |
+
|
| 135 |
+
embed_kwargs = {
|
| 136 |
+
'include_input' : True,
|
| 137 |
+
'input_dims' : 6,
|
| 138 |
+
'max_freq_log2' : multires-1,
|
| 139 |
+
'num_freqs' : multires,
|
| 140 |
+
'log_sampling' : True,
|
| 141 |
+
'periodic_fns' : [torch.sin, torch.cos],
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
embedder_obj = Embedder(**embed_kwargs)
|
| 145 |
+
embed = lambda x, eo=embedder_obj : eo.embed(x)
|
| 146 |
+
return embed, embedder_obj.out_dim
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def draw_heatmap(landmark, width, height):
|
| 150 |
+
batch = landmark.shape[0]
|
| 151 |
+
number = landmark.shape[1]
|
| 152 |
+
heatmap = np.zeros((batch, number,width, height), dtype=np.float32)
|
| 153 |
+
# draw mouth from mouth landmarks, landmarks: mouth landmark points, format: x1, y1, x2, y2, ..., x20,
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
landmark = (landmark+1)*29
|
| 157 |
+
for i in range(batch):
|
| 158 |
+
for pts_idx in range(number):
|
| 159 |
+
if int(landmark[i,pts_idx,0])<0:
|
| 160 |
+
landmark[i,pts_idx,0] = 0
|
| 161 |
+
if int(landmark[i,pts_idx,1])<0:
|
| 162 |
+
landmark[i,pts_idx,1] = 0
|
| 163 |
+
if int(landmark[i,pts_idx,0])>57:
|
| 164 |
+
landmark[i,pts_idx,0] = 57
|
| 165 |
+
if int(landmark[i,pts_idx,1])>57:
|
| 166 |
+
landmark[i,pts_idx,1] = 57
|
| 167 |
+
heatmap[i,pts_idx, int(landmark[i,pts_idx,1]), int(landmark[i,pts_idx,0])]=1
|
| 168 |
+
if heatmap[i,pts_idx].sum()== 1 :
|
| 169 |
+
|
| 170 |
+
heatmap[i,pts_idx] = cv2.GaussianBlur(heatmap[i,pts_idx], ksize=(3, 3), sigmaX=1, sigmaY=1)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
heatmap = torch.tensor(heatmap).cuda()
|
| 174 |
+
return heatmap
|
| 175 |
+
|
| 176 |
+
class NA_net(nn.Module):
|
| 177 |
+
def __init__(self):
|
| 178 |
+
super(NA_net, self).__init__()
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
self.decon = nn.Sequential(
|
| 183 |
+
nn.ConvTranspose2d(1, 16, kernel_size=(2,3), stride=2, padding=(2,1), bias=True),#16,16
|
| 184 |
+
nn.BatchNorm2d(16),
|
| 185 |
+
nn.ReLU(True),
|
| 186 |
+
nn.ConvTranspose2d(16, 32, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 187 |
+
nn.BatchNorm2d(32),
|
| 188 |
+
nn.ReLU(True),
|
| 189 |
+
nn.ConvTranspose2d(32, 32+3, kernel_size=4, stride=2, padding=1, bias=True)#16,16
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
def forward(self, neutral):
|
| 197 |
+
|
| 198 |
+
feature = neutral.unsqueeze(1)
|
| 199 |
+
current_feature = self.decon(feature)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
return current_feature
|
| 203 |
+
|
| 204 |
+
class AT_net(nn.Module):
|
| 205 |
+
def __init__(self):
|
| 206 |
+
super(AT_net, self).__init__()
|
| 207 |
+
|
| 208 |
+
down_blocks = []
|
| 209 |
+
for i in range(8):
|
| 210 |
+
down_blocks.append(DownBlock2d(3 if i == 0 else 2 * (2 ** i),
|
| 211 |
+
2 * (2 ** (i + 1)),
|
| 212 |
+
kernel_size=3, padding=1))
|
| 213 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
# self.lmark_encoder = nn.Sequential(
|
| 217 |
+
# nn.Linear(16,256),
|
| 218 |
+
# nn.ReLU(True),
|
| 219 |
+
# nn.Linear(256,512),
|
| 220 |
+
# nn.ReLU(True),
|
| 221 |
+
# )
|
| 222 |
+
self.pose_encoder = nn.Sequential(
|
| 223 |
+
nn.Linear(6,128),
|
| 224 |
+
nn.ReLU(True),
|
| 225 |
+
nn.Linear(128,256),
|
| 226 |
+
nn.ReLU(True),
|
| 227 |
+
|
| 228 |
+
)
|
| 229 |
+
self.audio_eocder = nn.Sequential(
|
| 230 |
+
conv2d(1,64,3,1,1),
|
| 231 |
+
conv2d(64,128,3,1,1),
|
| 232 |
+
nn.MaxPool2d(3, stride=(1,2)),
|
| 233 |
+
conv2d(128,256,3,1,1),
|
| 234 |
+
conv2d(256,256,3,1,1),
|
| 235 |
+
conv2d(256,512,3,1,1),
|
| 236 |
+
nn.MaxPool2d(3, stride=(2,2))
|
| 237 |
+
)
|
| 238 |
+
self.audio_eocder_fc = nn.Sequential(
|
| 239 |
+
nn.Linear(1024 *12,2048),
|
| 240 |
+
nn.ReLU(True),
|
| 241 |
+
nn.Linear(2048,256),
|
| 242 |
+
nn.ReLU(True),
|
| 243 |
+
|
| 244 |
+
)
|
| 245 |
+
self.lstm = nn.LSTM(256*4,256,3,batch_first = True)
|
| 246 |
+
# self.lstm_fc = nn.Sequential(
|
| 247 |
+
# nn.Linear(256,16),
|
| 248 |
+
# )
|
| 249 |
+
self.decon = nn.Sequential(
|
| 250 |
+
nn.ConvTranspose2d(256, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 251 |
+
nn.BatchNorm2d(256),
|
| 252 |
+
nn.ReLU(True),
|
| 253 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 254 |
+
nn.BatchNorm2d(128),
|
| 255 |
+
nn.ReLU(True),
|
| 256 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 257 |
+
nn.BatchNorm2d(128),
|
| 258 |
+
nn.ReLU(True),
|
| 259 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 260 |
+
nn.BatchNorm2d(128),
|
| 261 |
+
nn.ReLU(True),
|
| 262 |
+
nn.ConvTranspose2d(128, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 263 |
+
# nn.ConvTranspose2d(128, 32*4, kernel_size=2, stride=2, padding=3, bias=True),#64,64
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
)
|
| 267 |
+
self.generator = Generator(64,256,8)
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def forward(self, example_image, audio, pose, jaco_net):
|
| 272 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 273 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 274 |
+
outs = example_image
|
| 275 |
+
for down_block in self.down_blocks:
|
| 276 |
+
outs = down_block(outs)
|
| 277 |
+
image_feature = outs
|
| 278 |
+
image_feature = image_feature.view(image_feature.shape[0], -1)
|
| 279 |
+
lstm_input = []
|
| 280 |
+
for step_t in range(audio.size(1)):
|
| 281 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 282 |
+
current_feature = self.audio_eocder(current_audio)
|
| 283 |
+
current_feature = current_feature.view(current_feature.size(0), -1)
|
| 284 |
+
current_feature = self.audio_eocder_fc(current_feature)
|
| 285 |
+
pose_f = self.pose_encoder(pose[:,step_t])
|
| 286 |
+
features = torch.cat([image_feature, current_feature, pose_f], 1)
|
| 287 |
+
lstm_input.append(features)
|
| 288 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 289 |
+
lstm_out, _ = self.lstm(lstm_input, hidden)
|
| 290 |
+
fc_out = []
|
| 291 |
+
deco_out = []
|
| 292 |
+
for step_t in range(audio.size(1)):
|
| 293 |
+
fc_in = lstm_out[:,step_t,:]
|
| 294 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 295 |
+
if jaco_net == 'cnn':
|
| 296 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 297 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 298 |
+
deco_out.append(self.decon(fc_feature))
|
| 299 |
+
elif jaco_net == 'gan':
|
| 300 |
+
result,_ = self.generator([fc_in])
|
| 301 |
+
deco_out.append(result)
|
| 302 |
+
else:
|
| 303 |
+
raise Exception("jaco_net type wrong")
|
| 304 |
+
|
| 305 |
+
return torch.stack(deco_out,dim=1)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
class AT_net2(nn.Module):
|
| 309 |
+
def __init__(self):
|
| 310 |
+
super(AT_net2, self).__init__()
|
| 311 |
+
|
| 312 |
+
down_blocks = []
|
| 313 |
+
for i in range(8):
|
| 314 |
+
down_blocks.append(DownBlock2d(3 if i == 0 else 2 * (2 ** i),
|
| 315 |
+
2 * (2 ** (i + 1)),
|
| 316 |
+
kernel_size=3, padding=1))
|
| 317 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
# self.lmark_encoder = nn.Sequential(
|
| 321 |
+
# nn.Linear(16,256),
|
| 322 |
+
# nn.ReLU(True),
|
| 323 |
+
# nn.Linear(256,512),
|
| 324 |
+
# nn.ReLU(True),
|
| 325 |
+
# )
|
| 326 |
+
self.pose_encoder = nn.Sequential(
|
| 327 |
+
nn.Linear(6,128),
|
| 328 |
+
nn.ReLU(True),
|
| 329 |
+
nn.Linear(128,256),
|
| 330 |
+
nn.ReLU(True),
|
| 331 |
+
|
| 332 |
+
)
|
| 333 |
+
self.audio_eocder = nn.Sequential(
|
| 334 |
+
conv2d(1,64,3,1,1),
|
| 335 |
+
conv2d(64,128,3,1,1),
|
| 336 |
+
nn.MaxPool2d(3, stride=(1,2)),
|
| 337 |
+
conv2d(128,256,3,1,1),
|
| 338 |
+
conv2d(256,256,3,1,1),
|
| 339 |
+
conv2d(256,512,3,1,1),
|
| 340 |
+
nn.MaxPool2d(3, stride=(2,2))
|
| 341 |
+
)
|
| 342 |
+
self.audio_eocder_fc = nn.Sequential(
|
| 343 |
+
nn.Linear(1024 *12,2048),
|
| 344 |
+
nn.ReLU(True),
|
| 345 |
+
nn.Linear(2048,256),
|
| 346 |
+
nn.ReLU(True),
|
| 347 |
+
|
| 348 |
+
)
|
| 349 |
+
self.lstm = nn.LSTM(256*4,256,3,batch_first = True)
|
| 350 |
+
# self.lstm_fc = nn.Sequential(
|
| 351 |
+
# nn.Linear(256,16),
|
| 352 |
+
# )
|
| 353 |
+
self.decon = nn.Sequential(
|
| 354 |
+
nn.ConvTranspose2d(256, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 355 |
+
nn.BatchNorm2d(256),
|
| 356 |
+
nn.ReLU(True),
|
| 357 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 358 |
+
nn.BatchNorm2d(128),
|
| 359 |
+
nn.ReLU(True),
|
| 360 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 361 |
+
nn.BatchNorm2d(128),
|
| 362 |
+
nn.ReLU(True),
|
| 363 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 364 |
+
nn.BatchNorm2d(128),
|
| 365 |
+
nn.ReLU(True),
|
| 366 |
+
nn.ConvTranspose2d(128, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 367 |
+
# nn.ConvTranspose2d(128, 32*4, kernel_size=2, stride=2, padding=3, bias=True),#64,64
|
| 368 |
+
|
| 369 |
+
|
| 370 |
+
)
|
| 371 |
+
self.generator = Generator(64,256,8)
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def forward(self, example_image, audio, pose, jaco_net, weight):
|
| 375 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 376 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 377 |
+
outs = example_image
|
| 378 |
+
for down_block in self.down_blocks:
|
| 379 |
+
outs = down_block(outs)
|
| 380 |
+
image_feature = outs
|
| 381 |
+
image_feature = image_feature.view(image_feature.shape[0], -1)
|
| 382 |
+
lstm_input = []
|
| 383 |
+
for step_t in range(audio.size(1)):
|
| 384 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 385 |
+
current_feature = self.audio_eocder(current_audio)
|
| 386 |
+
current_feature = current_feature.view(current_feature.size(0), -1)
|
| 387 |
+
current_feature = self.audio_eocder_fc(current_feature)*weight
|
| 388 |
+
pose_f = self.pose_encoder(pose[:,step_t])
|
| 389 |
+
features = torch.cat([image_feature, current_feature, pose_f], 1)
|
| 390 |
+
lstm_input.append(features)
|
| 391 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 392 |
+
lstm_out, _ = self.lstm(lstm_input, hidden)
|
| 393 |
+
fc_out = []
|
| 394 |
+
deco_out = []
|
| 395 |
+
for step_t in range(audio.size(1)):
|
| 396 |
+
fc_in = lstm_out[:,step_t,:]
|
| 397 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 398 |
+
if jaco_net == 'cnn':
|
| 399 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 400 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 401 |
+
deco_out.append(self.decon(fc_feature))
|
| 402 |
+
elif jaco_net == 'gan':
|
| 403 |
+
result,_ = self.generator([fc_in])
|
| 404 |
+
deco_out.append(result)
|
| 405 |
+
else:
|
| 406 |
+
raise Exception("jaco_net type wrong")
|
| 407 |
+
|
| 408 |
+
return torch.stack(deco_out,dim=1)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
class AT_net_liujin(nn.Module):
|
| 412 |
+
def __init__(self):
|
| 413 |
+
super(AT_net_liujin, self).__init__()
|
| 414 |
+
|
| 415 |
+
down_blocks = []
|
| 416 |
+
for i in range(8):
|
| 417 |
+
down_blocks.append(DownBlock2d(3 if i == 0 else 2 * (2 ** i),
|
| 418 |
+
2 * (2 ** (i + 1)),
|
| 419 |
+
kernel_size=3, padding=1))
|
| 420 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 421 |
+
|
| 422 |
+
|
| 423 |
+
# self.lmark_encoder = nn.Sequential(
|
| 424 |
+
# nn.Linear(16,256),
|
| 425 |
+
# nn.ReLU(True),
|
| 426 |
+
# nn.Linear(256,512),
|
| 427 |
+
# nn.ReLU(True),
|
| 428 |
+
# )
|
| 429 |
+
self.pose_encoder = nn.Sequential(
|
| 430 |
+
nn.Linear(6,128),
|
| 431 |
+
nn.ReLU(True),
|
| 432 |
+
nn.Linear(128,256),
|
| 433 |
+
nn.ReLU(True),
|
| 434 |
+
|
| 435 |
+
)
|
| 436 |
+
self.audio_eocder = nn.Sequential(
|
| 437 |
+
conv2d(1,64,3,1,1),
|
| 438 |
+
conv2d(64,128,3,1,1),
|
| 439 |
+
nn.MaxPool2d(3, stride=(1,2)),
|
| 440 |
+
conv2d(128,256,3,1,1),
|
| 441 |
+
conv2d(256,256,3,1,1),
|
| 442 |
+
conv2d(256,512,3,1,1),
|
| 443 |
+
nn.MaxPool2d(3, stride=(2,2))
|
| 444 |
+
)
|
| 445 |
+
self.audio_eocder_fc = nn.Sequential(
|
| 446 |
+
nn.Linear(1024 *12,2048),
|
| 447 |
+
nn.ReLU(True),
|
| 448 |
+
nn.Linear(2048,256),
|
| 449 |
+
nn.ReLU(True),
|
| 450 |
+
)
|
| 451 |
+
|
| 452 |
+
self.audio_encoder_liujin = nn.Sequential(
|
| 453 |
+
Conv2d(1, 32, kernel_size=3, stride=1, padding=1),
|
| 454 |
+
Conv2d(32, 32, kernel_size=3, stride=1, padding=1, residual=True),
|
| 455 |
+
Conv2d(32, 32, kernel_size=3, stride=1, padding=1, residual=True),
|
| 456 |
+
|
| 457 |
+
Conv2d(32, 64, kernel_size=3, stride=(3, 1), padding=1),
|
| 458 |
+
Conv2d(64, 64, kernel_size=3, stride=1, padding=1, residual=True),
|
| 459 |
+
Conv2d(64, 64, kernel_size=3, stride=1, padding=1, residual=True),
|
| 460 |
+
|
| 461 |
+
Conv2d(64, 128, kernel_size=3, stride=3, padding=1),
|
| 462 |
+
Conv2d(128, 128, kernel_size=3, stride=1, padding=1, residual=True),
|
| 463 |
+
Conv2d(128, 128, kernel_size=3, stride=1, padding=1, residual=True),
|
| 464 |
+
|
| 465 |
+
Conv2d(128, 256, kernel_size=3, stride=(3, 2), padding=1),
|
| 466 |
+
Conv2d(256, 256, kernel_size=3, stride=1, padding=1, residual=True),
|
| 467 |
+
|
| 468 |
+
Conv2d(256, 512, kernel_size=3, stride=1, padding=0),
|
| 469 |
+
Conv2d(512, 512, kernel_size=1, stride=1, padding=0), )
|
| 470 |
+
|
| 471 |
+
for child in self.audio_encoder_liujin.children():
|
| 472 |
+
for param in child.parameters():
|
| 473 |
+
param.requires_grad = False
|
| 474 |
+
|
| 475 |
+
# self.lstm = nn.LSTM(256*4,256,3,batch_first = True)
|
| 476 |
+
|
| 477 |
+
self.lstm_liujin = nn.LSTM(256 * 5, 256, 3, batch_first=True)
|
| 478 |
+
|
| 479 |
+
# self.lstm_fc = nn.Sequential(
|
| 480 |
+
# nn.Linear(256,16),
|
| 481 |
+
# )
|
| 482 |
+
self.decon = nn.Sequential(
|
| 483 |
+
nn.ConvTranspose2d(256, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 484 |
+
nn.BatchNorm2d(256),
|
| 485 |
+
nn.ReLU(True),
|
| 486 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 487 |
+
nn.BatchNorm2d(128),
|
| 488 |
+
nn.ReLU(True),
|
| 489 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 490 |
+
nn.BatchNorm2d(128),
|
| 491 |
+
nn.ReLU(True),
|
| 492 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 493 |
+
nn.BatchNorm2d(128),
|
| 494 |
+
nn.ReLU(True),
|
| 495 |
+
nn.ConvTranspose2d(128, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 496 |
+
# nn.ConvTranspose2d(128, 32*4, kernel_size=2, stride=2, padding=3, bias=True),#64,64
|
| 497 |
+
|
| 498 |
+
|
| 499 |
+
)
|
| 500 |
+
self.generator = Generator(64,256,8)
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
def forward(self, example_image, audio, pose, jaco_net):
|
| 505 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 506 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 507 |
+
outs = example_image
|
| 508 |
+
for down_block in self.down_blocks:
|
| 509 |
+
outs = down_block(outs)
|
| 510 |
+
image_feature = outs
|
| 511 |
+
image_feature = image_feature.view(image_feature.shape[0], -1)
|
| 512 |
+
lstm_input = []
|
| 513 |
+
for step_t in range(audio.size(1)):
|
| 514 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 515 |
+
# current_feature = self.audio_eocder(current_audio)
|
| 516 |
+
# current_feature = current_feature.view(current_feature.size(0), -1)
|
| 517 |
+
# current_feature = self.audio_eocder_fc(current_feature)
|
| 518 |
+
|
| 519 |
+
current_feature = self.audio_encoder_liujin(current_audio)
|
| 520 |
+
current_feature = current_feature.reshape(current_audio.shape[0], -1)
|
| 521 |
+
|
| 522 |
+
pose_f = self.pose_encoder(pose[:,step_t])
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
features = torch.cat([image_feature, current_feature, pose_f], 1)
|
| 526 |
+
lstm_input.append(features)
|
| 527 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 528 |
+
# lstm_out, _ = self.lstm(lstm_input, hidden)
|
| 529 |
+
lstm_out, _ = self.lstm_liujin(lstm_input, hidden)
|
| 530 |
+
fc_out = []
|
| 531 |
+
deco_out = []
|
| 532 |
+
for step_t in range(audio.size(1)):
|
| 533 |
+
fc_in = lstm_out[:,step_t,:]
|
| 534 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 535 |
+
if jaco_net == 'cnn':
|
| 536 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 537 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 538 |
+
deco_out.append(self.decon(fc_feature))
|
| 539 |
+
elif jaco_net == 'gan':
|
| 540 |
+
result,_ = self.generator([fc_in])
|
| 541 |
+
deco_out.append(result)
|
| 542 |
+
else:
|
| 543 |
+
raise Exception("jaco_net type wrong")
|
| 544 |
+
|
| 545 |
+
return torch.stack(deco_out,dim=1)
|
| 546 |
+
|
| 547 |
+
class Classify(nn.Module):
|
| 548 |
+
def __init__(self):
|
| 549 |
+
super(Classify, self).__init__()
|
| 550 |
+
|
| 551 |
+
|
| 552 |
+
|
| 553 |
+
self.last_fc = nn.Linear(512,8)
|
| 554 |
+
|
| 555 |
+
def forward(self, feature):
|
| 556 |
+
# mfcc= torch.unsqueeze(mfcc, 1)
|
| 557 |
+
|
| 558 |
+
x = self.last_fc(feature)
|
| 559 |
+
|
| 560 |
+
return x
|
| 561 |
+
|
| 562 |
+
class TF_net(nn.Module):
|
| 563 |
+
def __init__(self):
|
| 564 |
+
super(TF_net, self).__init__()
|
| 565 |
+
|
| 566 |
+
down_blocks = []
|
| 567 |
+
for i in range(8):
|
| 568 |
+
down_blocks.append(DownBlock2d(3 if i == 0 else 2 * (2 ** i),
|
| 569 |
+
2 * (2 ** (i + 1)),
|
| 570 |
+
kernel_size=3, padding=1))
|
| 571 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
# self.lmark_encoder = nn.Sequential(
|
| 575 |
+
# nn.Linear(16,256),
|
| 576 |
+
# nn.ReLU(True),
|
| 577 |
+
# nn.Linear(256,512),
|
| 578 |
+
# nn.ReLU(True),
|
| 579 |
+
# )
|
| 580 |
+
self.pose_encoder = nn.Sequential(
|
| 581 |
+
nn.Linear(6,128),
|
| 582 |
+
nn.ReLU(True),
|
| 583 |
+
nn.Linear(128,256),
|
| 584 |
+
nn.ReLU(True),
|
| 585 |
+
|
| 586 |
+
)
|
| 587 |
+
self.audio_eocder = nn.Sequential(
|
| 588 |
+
conv2d(1,64,3,1,1),
|
| 589 |
+
conv2d(64,128,3,1,1),
|
| 590 |
+
nn.MaxPool2d(3, stride=(1,2)),
|
| 591 |
+
conv2d(128,256,3,1,1),
|
| 592 |
+
conv2d(256,256,3,1,1),
|
| 593 |
+
conv2d(256,512,3,1,1),
|
| 594 |
+
nn.MaxPool2d(3, stride=(2,2))
|
| 595 |
+
)
|
| 596 |
+
self.audio_eocder_fc = nn.Sequential(
|
| 597 |
+
nn.Linear(1024 *12,2048),
|
| 598 |
+
nn.ReLU(True),
|
| 599 |
+
nn.Linear(2048,256),
|
| 600 |
+
nn.ReLU(True),
|
| 601 |
+
|
| 602 |
+
)
|
| 603 |
+
self.lstm = nn.LSTM(256*4,256,3,batch_first = True)
|
| 604 |
+
self.lstm_two = nn.LSTM(256*6,256,3,batch_first = True)
|
| 605 |
+
# self.lstm_fc = nn.Sequential(
|
| 606 |
+
# nn.Linear(256,16),
|
| 607 |
+
# )
|
| 608 |
+
self.decon = nn.Sequential(
|
| 609 |
+
nn.ConvTranspose2d(256, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 610 |
+
nn.BatchNorm2d(256),
|
| 611 |
+
nn.ReLU(True),
|
| 612 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 613 |
+
nn.BatchNorm2d(128),
|
| 614 |
+
nn.ReLU(True),
|
| 615 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 616 |
+
nn.BatchNorm2d(128),
|
| 617 |
+
nn.ReLU(True),
|
| 618 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 619 |
+
nn.BatchNorm2d(128),
|
| 620 |
+
nn.ReLU(True),
|
| 621 |
+
nn.ConvTranspose2d(128, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 622 |
+
# nn.ConvTranspose2d(128, 32*4, kernel_size=2, stride=2, padding=3, bias=True),#64,64
|
| 623 |
+
|
| 624 |
+
|
| 625 |
+
)
|
| 626 |
+
self.generator = Generator(64,256,8)
|
| 627 |
+
self.instance_norm = InstanceNorm()
|
| 628 |
+
self.style_mod = ApplyStyle(512, 1024, use_wscale=True)
|
| 629 |
+
self.style_mod1 = ApplyStyle(512, 35, use_wscale=True)
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
def adain_forward(self, example_image, audio, pose, jaco_net, emo_features):
|
| 633 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 634 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 635 |
+
outs = example_image
|
| 636 |
+
for down_block in self.down_blocks:
|
| 637 |
+
outs = down_block(outs)
|
| 638 |
+
image_feature = outs
|
| 639 |
+
image_feature = image_feature.view(image_feature.shape[0], -1)
|
| 640 |
+
lstm_input = []
|
| 641 |
+
for step_t in range(audio.size(1)):
|
| 642 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 643 |
+
current_feature = self.audio_eocder(current_audio)
|
| 644 |
+
current_feature = current_feature.view(current_feature.size(0), -1)
|
| 645 |
+
current_feature = self.audio_eocder_fc(current_feature) #256
|
| 646 |
+
pose_f = self.pose_encoder(pose[:,step_t]) #256
|
| 647 |
+
features = torch.cat([image_feature, current_feature, pose_f], 1)
|
| 648 |
+
features = torch.unsqueeze(torch.unsqueeze(features,-1),-1)
|
| 649 |
+
features = self.instance_norm(features)
|
| 650 |
+
x = self.style_mod(features, emo_features[step_t])
|
| 651 |
+
# t = adain(torch.unsqueeze(torch.unsqueeze(features,-1),-1), torch.unsqueeze(torch.unsqueeze(emo_features[step_t],1),2))
|
| 652 |
+
|
| 653 |
+
lstm_input.append(torch.squeeze(torch.squeeze(x,-1),-1))
|
| 654 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 655 |
+
lstm_out, _ = self.lstm(lstm_input, hidden)
|
| 656 |
+
# fc_out = []
|
| 657 |
+
deco_out = []
|
| 658 |
+
for step_t in range(audio.size(1)):
|
| 659 |
+
fc_in = lstm_out[:,step_t,:]
|
| 660 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 661 |
+
if jaco_net == 'cnn':
|
| 662 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 663 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 664 |
+
deco_out.append(self.decon(fc_feature))
|
| 665 |
+
elif jaco_net == 'gan':
|
| 666 |
+
result,_ = self.generator([fc_in])
|
| 667 |
+
deco_out.append(result)
|
| 668 |
+
else:
|
| 669 |
+
raise Exception("jaco_net type wrong")
|
| 670 |
+
|
| 671 |
+
return torch.stack(deco_out,dim=1)
|
| 672 |
+
|
| 673 |
+
|
| 674 |
+
|
| 675 |
+
def adain_feature2(self, example_image, audio, pose, jaco_net, emo_features):
|
| 676 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 677 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 678 |
+
outs = example_image
|
| 679 |
+
for down_block in self.down_blocks:
|
| 680 |
+
outs = down_block(outs)
|
| 681 |
+
image_feature = outs
|
| 682 |
+
image_feature = image_feature.view(image_feature.shape[0], -1)
|
| 683 |
+
lstm_input = []
|
| 684 |
+
for step_t in range(audio.size(1)):
|
| 685 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 686 |
+
current_feature = self.audio_eocder(current_audio)
|
| 687 |
+
current_feature = current_feature.view(current_feature.size(0), -1)
|
| 688 |
+
current_feature = self.audio_eocder_fc(current_feature) #256
|
| 689 |
+
pose_f = self.pose_encoder(pose[:,step_t]) #256
|
| 690 |
+
features = torch.cat([image_feature, current_feature, pose_f], 1)
|
| 691 |
+
|
| 692 |
+
lstm_input.append(features)
|
| 693 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 694 |
+
lstm_out, _ = self.lstm(lstm_input, hidden)
|
| 695 |
+
# fc_out = []
|
| 696 |
+
deco_out = []
|
| 697 |
+
for step_t in range(audio.size(1)):
|
| 698 |
+
fc_in = lstm_out[:,step_t,:]
|
| 699 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 700 |
+
if jaco_net == 'cnn':
|
| 701 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 702 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 703 |
+
fc_feature = self.decon(fc_feature)
|
| 704 |
+
fc_feature = self.instance_norm(fc_feature)
|
| 705 |
+
t = self.style_mod1(fc_feature, emo_features[step_t])
|
| 706 |
+
# emo_feature = torch.unsqueeze(torch.unsqueeze(emo_features[step_t],-1),-1)
|
| 707 |
+
# emo_feature = emo_feature.repeat(1,fc_feature.shape[1],1,1)
|
| 708 |
+
# t = adain(fc_feature, emo_feature)
|
| 709 |
+
deco_out.append(t)
|
| 710 |
+
elif jaco_net == 'gan':
|
| 711 |
+
result,_ = self.generator([fc_in])
|
| 712 |
+
deco_out.append(result)
|
| 713 |
+
else:
|
| 714 |
+
raise Exception("jaco_net type wrong")
|
| 715 |
+
|
| 716 |
+
return torch.stack(deco_out,dim=1)
|
| 717 |
+
|
| 718 |
+
def forward(self, example_image, audio, pose, jaco_net, emo_features):
|
| 719 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 720 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 721 |
+
outs = example_image
|
| 722 |
+
for down_block in self.down_blocks:
|
| 723 |
+
outs = down_block(outs)
|
| 724 |
+
image_feature = outs
|
| 725 |
+
image_feature = image_feature.view(image_feature.shape[0], -1)
|
| 726 |
+
lstm_input = []
|
| 727 |
+
for step_t in range(audio.size(1)):
|
| 728 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 729 |
+
current_feature = self.audio_eocder(current_audio)
|
| 730 |
+
current_feature = current_feature.view(current_feature.size(0), -1)
|
| 731 |
+
current_feature = self.audio_eocder_fc(current_feature) #256
|
| 732 |
+
pose_f = self.pose_encoder(pose[:,step_t]) #256
|
| 733 |
+
features = torch.cat([image_feature, current_feature, pose_f, emo_features[step_t]], 1)
|
| 734 |
+
lstm_input.append(features)
|
| 735 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 736 |
+
lstm_out, _ = self.lstm_two(lstm_input, hidden)
|
| 737 |
+
fc_out = []
|
| 738 |
+
deco_out = []
|
| 739 |
+
for step_t in range(audio.size(1)):
|
| 740 |
+
fc_in = lstm_out[:,step_t,:]
|
| 741 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 742 |
+
if jaco_net == 'cnn':
|
| 743 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 744 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 745 |
+
deco_out.append(self.decon(fc_feature))
|
| 746 |
+
elif jaco_net == 'gan':
|
| 747 |
+
result,_ = self.generator([fc_in])
|
| 748 |
+
deco_out.append(result)
|
| 749 |
+
else:
|
| 750 |
+
raise Exception("jaco_net type wrong")
|
| 751 |
+
|
| 752 |
+
return torch.stack(deco_out,dim=1)
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
class AT_net2_liujin(nn.Module):
|
| 756 |
+
def __init__(self):
|
| 757 |
+
super(AT_net2_liujin, self).__init__()
|
| 758 |
+
|
| 759 |
+
down_blocks = []
|
| 760 |
+
for i in range(8):
|
| 761 |
+
down_blocks.append(DownBlock2d(3 if i == 0 else 2 * (2 ** i),
|
| 762 |
+
2 * (2 ** (i + 1)),
|
| 763 |
+
kernel_size=3, padding=1))
|
| 764 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
# self.lmark_encoder = nn.Sequential(
|
| 768 |
+
# nn.Linear(16,256),
|
| 769 |
+
# nn.ReLU(True),
|
| 770 |
+
# nn.Linear(256,512),
|
| 771 |
+
# nn.ReLU(True),
|
| 772 |
+
# )
|
| 773 |
+
self.pose_encoder = nn.Sequential(
|
| 774 |
+
nn.Linear(6,128),
|
| 775 |
+
nn.ReLU(True),
|
| 776 |
+
nn.Linear(128,256),
|
| 777 |
+
nn.ReLU(True),
|
| 778 |
+
)
|
| 779 |
+
|
| 780 |
+
self.audio_eocder = nn.Sequential(
|
| 781 |
+
conv2d(1,64,3,1,1),
|
| 782 |
+
conv2d(64,128,3,1,1),
|
| 783 |
+
nn.MaxPool2d(3, stride=(1,2)),
|
| 784 |
+
conv2d(128,256,3,1,1),
|
| 785 |
+
conv2d(256,256,3,1,1),
|
| 786 |
+
conv2d(256,512,3,1,1),
|
| 787 |
+
nn.MaxPool2d(3, stride=(2,2))
|
| 788 |
+
)
|
| 789 |
+
|
| 790 |
+
self.audio_eocder_fc = nn.Sequential(
|
| 791 |
+
nn.Linear(1024 *12,2048),
|
| 792 |
+
nn.ReLU(True),
|
| 793 |
+
nn.Linear(2048,256),
|
| 794 |
+
nn.ReLU(True),
|
| 795 |
+
)
|
| 796 |
+
|
| 797 |
+
|
| 798 |
+
|
| 799 |
+
|
| 800 |
+
# self.lstm = nn.LSTM(256*4,256,3,batch_first = True)
|
| 801 |
+
self.lstm_liujin = nn.LSTM(256 * 5, 256, 3, batch_first=True)
|
| 802 |
+
# self.lstm_fc = nn.Sequential(
|
| 803 |
+
# nn.Linear(256,16),
|
| 804 |
+
# )
|
| 805 |
+
self.decon = nn.Sequential(
|
| 806 |
+
nn.ConvTranspose2d(256, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 807 |
+
nn.BatchNorm2d(256),
|
| 808 |
+
nn.ReLU(True),
|
| 809 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 810 |
+
nn.BatchNorm2d(128),
|
| 811 |
+
nn.ReLU(True),
|
| 812 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 813 |
+
nn.BatchNorm2d(128),
|
| 814 |
+
nn.ReLU(True),
|
| 815 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 816 |
+
nn.BatchNorm2d(128),
|
| 817 |
+
nn.ReLU(True),
|
| 818 |
+
nn.ConvTranspose2d(128, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 819 |
+
# nn.ConvTranspose2d(128, 32*4, kernel_size=2, stride=2, padding=3, bias=True),#64,64
|
| 820 |
+
|
| 821 |
+
|
| 822 |
+
)
|
| 823 |
+
self.generator = Generator(64,256,8)
|
| 824 |
+
|
| 825 |
+
|
| 826 |
+
def forward(self, example_image, audio, pose, jaco_net, weight):
|
| 827 |
+
hidden = ( torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()),
|
| 828 |
+
torch.autograd.Variable(torch.zeros(3, audio.size(0), 256).cuda()))
|
| 829 |
+
outs = example_image
|
| 830 |
+
for down_block in self.down_blocks:
|
| 831 |
+
outs = down_block(outs)
|
| 832 |
+
image_feature = outs
|
| 833 |
+
image_feature = image_feature.view(image_feature.shape[0], -1) #1, 512, 1, 1
|
| 834 |
+
lstm_input = []
|
| 835 |
+
for step_t in range(audio.size(1)):
|
| 836 |
+
current_audio = audio[ : ,step_t , :, :].unsqueeze(1)
|
| 837 |
+
current_feature = self.audio_eocder(current_audio)
|
| 838 |
+
current_feature = current_feature.view(current_feature.size(0), -1)
|
| 839 |
+
current_feature = self.audio_eocder_fc(current_feature)*weight
|
| 840 |
+
pose_f = self.pose_encoder(pose[:,step_t])
|
| 841 |
+
features = torch.cat([image_feature, current_feature, pose_f], 1)
|
| 842 |
+
lstm_input.append(features)
|
| 843 |
+
lstm_input = torch.stack(lstm_input, dim = 1)
|
| 844 |
+
lstm_out, _ = self.lstm(lstm_input, hidden) #1,N,256
|
| 845 |
+
|
| 846 |
+
|
| 847 |
+
fc_out = []
|
| 848 |
+
deco_out = []
|
| 849 |
+
for step_t in range(audio.size(1)):
|
| 850 |
+
fc_in = lstm_out[:,step_t,:]
|
| 851 |
+
# fc_out.append(self.lstm_fc(fc_in))
|
| 852 |
+
if jaco_net == 'cnn':
|
| 853 |
+
fc_feature = torch.unsqueeze(fc_in,2)
|
| 854 |
+
fc_feature = torch.unsqueeze(fc_feature,3)
|
| 855 |
+
deco_out.append(self.decon(fc_feature))
|
| 856 |
+
elif jaco_net == 'gan':
|
| 857 |
+
result,_ = self.generator([fc_in])
|
| 858 |
+
deco_out.append(result)
|
| 859 |
+
else:
|
| 860 |
+
raise Exception("jaco_net type wrong")
|
| 861 |
+
|
| 862 |
+
return torch.stack(deco_out,dim=1)
|
| 863 |
+
|
| 864 |
+
|
| 865 |
+
|
| 866 |
+
class Ct_encoder(nn.Module):
|
| 867 |
+
def __init__(self):
|
| 868 |
+
super(Ct_encoder, self).__init__()
|
| 869 |
+
self.audio_eocder = nn.Sequential(
|
| 870 |
+
conv2d(1,64,3,1,1),
|
| 871 |
+
conv2d(64,128,3,1,1),
|
| 872 |
+
nn.MaxPool2d(3, stride=(1,2)),
|
| 873 |
+
conv2d(128,256,3,1,1),
|
| 874 |
+
conv2d(256,256,3,1,1),
|
| 875 |
+
conv2d(256,512,3,1,1),
|
| 876 |
+
nn.MaxPool2d(3, stride=(2,2))
|
| 877 |
+
)
|
| 878 |
+
self.audio_eocder_fc = nn.Sequential(
|
| 879 |
+
nn.Linear(1024 *12,2048),
|
| 880 |
+
nn.ReLU(True),
|
| 881 |
+
nn.Linear(2048,256),
|
| 882 |
+
nn.ReLU(True),
|
| 883 |
+
|
| 884 |
+
)
|
| 885 |
+
|
| 886 |
+
def forward(self, audio):
|
| 887 |
+
|
| 888 |
+
feature = self.audio_eocder(audio)
|
| 889 |
+
feature = feature.view(feature.size(0),-1)
|
| 890 |
+
x = self.audio_eocder_fc(feature)
|
| 891 |
+
|
| 892 |
+
return x
|
| 893 |
+
|
| 894 |
+
|
| 895 |
+
class EmotionNet(nn.Module):
|
| 896 |
+
def __init__(self):
|
| 897 |
+
super(EmotionNet, self).__init__()
|
| 898 |
+
|
| 899 |
+
self.emotion_eocder = nn.Sequential(
|
| 900 |
+
conv2d(1,64,3,1,1),
|
| 901 |
+
|
| 902 |
+
nn.MaxPool2d((1,3), stride=(1,2)), #[1, 64, 12, 12]
|
| 903 |
+
conv2d(64,128,3,1,1),
|
| 904 |
+
|
| 905 |
+
conv2d(128,256,3,1,1),
|
| 906 |
+
|
| 907 |
+
nn.MaxPool2d((12,1), stride=(12,1)), #[1, 256, 1, 12]
|
| 908 |
+
|
| 909 |
+
conv2d(256,512,3,1,1),
|
| 910 |
+
|
| 911 |
+
nn.MaxPool2d((1,2), stride=(1,2)) #[1, 512, 1, 6]
|
| 912 |
+
|
| 913 |
+
)
|
| 914 |
+
self.emotion_eocder_fc = nn.Sequential(
|
| 915 |
+
nn.Linear(512 *6,2048),
|
| 916 |
+
nn.ReLU(True),
|
| 917 |
+
nn.Linear(2048,128),
|
| 918 |
+
nn.ReLU(True),
|
| 919 |
+
|
| 920 |
+
)
|
| 921 |
+
|
| 922 |
+
self.last_fc = nn.Linear(128,8)
|
| 923 |
+
|
| 924 |
+
self.re_id = nn.Sequential(
|
| 925 |
+
conv2d(512,1024,3,1,1),
|
| 926 |
+
|
| 927 |
+
nn.MaxPool2d((1,2), stride=(1,2)), #[1, 1024, 1, 3]
|
| 928 |
+
conv2d(1024,1024,3,1,1),
|
| 929 |
+
|
| 930 |
+
conv2d(1024,2048,3,1,1),
|
| 931 |
+
|
| 932 |
+
nn.MaxPool2d((1,2), stride=(1,2)) #[1, 2048, 1, 1]
|
| 933 |
+
|
| 934 |
+
|
| 935 |
+
)
|
| 936 |
+
self.re_id_fc = nn.Sequential(
|
| 937 |
+
|
| 938 |
+
nn.Linear(2048,512),
|
| 939 |
+
nn.ReLU(True),
|
| 940 |
+
nn.Linear(512,128),
|
| 941 |
+
nn.ReLU(True),
|
| 942 |
+
)
|
| 943 |
+
|
| 944 |
+
|
| 945 |
+
def forward(self, mfcc):
|
| 946 |
+
# mfcc= torch.unsqueeze(mfcc, 1)
|
| 947 |
+
mfcc=torch.transpose(mfcc,2,3)
|
| 948 |
+
feature = self.emotion_eocder(mfcc)
|
| 949 |
+
|
| 950 |
+
# id_feature = feature.detach()
|
| 951 |
+
|
| 952 |
+
feature = feature.view(feature.size(0),-1)
|
| 953 |
+
x = self.emotion_eocder_fc(feature)
|
| 954 |
+
|
| 955 |
+
|
| 956 |
+
# remove_feature = self.re_id(id_feature)
|
| 957 |
+
# remove_feature = remove_feature.view(remove_feature.size(0),-1)
|
| 958 |
+
# y = self.re_id_fc(remove_feature)
|
| 959 |
+
|
| 960 |
+
return x
|
| 961 |
+
|
| 962 |
+
|
| 963 |
+
class AF2F(nn.Module):
|
| 964 |
+
def __init__(self):
|
| 965 |
+
super(AF2F, self).__init__()
|
| 966 |
+
self.decon = nn.Sequential(
|
| 967 |
+
nn.ConvTranspose2d(384, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 968 |
+
nn.BatchNorm2d(256),
|
| 969 |
+
nn.ReLU(True),
|
| 970 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 971 |
+
nn.BatchNorm2d(128),
|
| 972 |
+
nn.ReLU(True),
|
| 973 |
+
nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 974 |
+
nn.BatchNorm2d(64),
|
| 975 |
+
nn.ReLU(True),
|
| 976 |
+
nn.ConvTranspose2d(64, 64, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 977 |
+
nn.BatchNorm2d(64),
|
| 978 |
+
nn.ReLU(True),
|
| 979 |
+
nn.ConvTranspose2d(64, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 980 |
+
|
| 981 |
+
|
| 982 |
+
)
|
| 983 |
+
|
| 984 |
+
def forward(self, content,emotion):
|
| 985 |
+
features = torch.cat([content, emotion], 1) #connect tensors inputs and dimension
|
| 986 |
+
features = torch.unsqueeze(features,2)
|
| 987 |
+
features = torch.unsqueeze(features,3)
|
| 988 |
+
x = self.decon(features)
|
| 989 |
+
|
| 990 |
+
|
| 991 |
+
return x
|
| 992 |
+
|
| 993 |
+
class AF2F_s(nn.Module):
|
| 994 |
+
def __init__(self):
|
| 995 |
+
super(AF2F_s, self).__init__()
|
| 996 |
+
self.decon = nn.Sequential(
|
| 997 |
+
nn.ConvTranspose2d(256, 256, kernel_size=6, stride=2, padding=1, bias=True),#4,4
|
| 998 |
+
nn.BatchNorm2d(256),
|
| 999 |
+
nn.ReLU(True),
|
| 1000 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 1001 |
+
nn.BatchNorm2d(128),
|
| 1002 |
+
nn.ReLU(True),
|
| 1003 |
+
nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 1004 |
+
nn.BatchNorm2d(64),
|
| 1005 |
+
nn.ReLU(True),
|
| 1006 |
+
nn.ConvTranspose2d(64, 64, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 1007 |
+
nn.BatchNorm2d(64),
|
| 1008 |
+
nn.ReLU(True),
|
| 1009 |
+
nn.ConvTranspose2d(64, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 1010 |
+
|
| 1011 |
+
nn.ReLU(),
|
| 1012 |
+
)
|
| 1013 |
+
|
| 1014 |
+
def forward(self, content):
|
| 1015 |
+
# features = torch.cat([content, emotion], 1) #connect tensors inputs and dimension
|
| 1016 |
+
features = torch.unsqueeze(content,2)
|
| 1017 |
+
features = torch.unsqueeze(features,3)
|
| 1018 |
+
x = self.decon(features)
|
| 1019 |
+
|
| 1020 |
+
|
| 1021 |
+
return x
|
| 1022 |
+
|
| 1023 |
+
|
| 1024 |
+
class A2I(nn.Module):
|
| 1025 |
+
def __init__(self):
|
| 1026 |
+
super(A2I, self).__init__()
|
| 1027 |
+
self.audio_eocder = nn.Sequential(
|
| 1028 |
+
conv2d(1,64,3,1,1),
|
| 1029 |
+
conv2d(64,128,3,1,1),
|
| 1030 |
+
nn.MaxPool2d((1,5), stride=(1,2)),
|
| 1031 |
+
conv2d(128,256,3,1,1),
|
| 1032 |
+
conv2d(256,256,3,1,1),
|
| 1033 |
+
|
| 1034 |
+
nn.MaxPool2d((5,5), stride=(2,2))
|
| 1035 |
+
)
|
| 1036 |
+
self.decon = nn.Sequential(
|
| 1037 |
+
|
| 1038 |
+
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 1039 |
+
nn.BatchNorm2d(128),
|
| 1040 |
+
nn.ReLU(True),
|
| 1041 |
+
nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 1042 |
+
nn.BatchNorm2d(64),
|
| 1043 |
+
nn.ReLU(True),
|
| 1044 |
+
nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 1045 |
+
nn.BatchNorm2d(32),
|
| 1046 |
+
nn.ReLU(True),
|
| 1047 |
+
nn.ConvTranspose2d(32, 2, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 1048 |
+
|
| 1049 |
+
nn.ReLU(),
|
| 1050 |
+
)
|
| 1051 |
+
|
| 1052 |
+
def forward(self, mfcc):
|
| 1053 |
+
mfcc= torch.unsqueeze(mfcc, 1)
|
| 1054 |
+
mfcc=torch.transpose(mfcc,2,3)
|
| 1055 |
+
feature = self.audio_eocder(mfcc)
|
| 1056 |
+
|
| 1057 |
+
# id_feature = feature.detach()
|
| 1058 |
+
|
| 1059 |
+
x = self.decon(feature)
|
| 1060 |
+
|
| 1061 |
+
return x
|
| 1062 |
+
|
| 1063 |
+
def kp2gaussian(kp, spatial_size, kp_variance):
|
| 1064 |
+
"""
|
| 1065 |
+
Transform a keypoint into gaussian like representation
|
| 1066 |
+
"""
|
| 1067 |
+
mean = kp['value'] #[4,10,2]
|
| 1068 |
+
|
| 1069 |
+
coordinate_grid = make_coordinate_grid(spatial_size, mean.type()) #[h,w,2]
|
| 1070 |
+
number_of_leading_dimensions = len(mean.shape) - 1
|
| 1071 |
+
shape = (1,) * number_of_leading_dimensions + coordinate_grid.shape #5
|
| 1072 |
+
coordinate_grid = coordinate_grid.view(*shape) #[1,1,h,w,2]
|
| 1073 |
+
repeats = mean.shape[:number_of_leading_dimensions] + (1, 1, 1)
|
| 1074 |
+
coordinate_grid = coordinate_grid.repeat(*repeats) #[4,10,h,w,2]
|
| 1075 |
+
|
| 1076 |
+
# Preprocess kp shape
|
| 1077 |
+
shape = mean.shape[:number_of_leading_dimensions] + (1, 1, 2)
|
| 1078 |
+
mean = mean.view(*shape) #[4,10,1,1,2]
|
| 1079 |
+
|
| 1080 |
+
mean_sub = (coordinate_grid - mean)
|
| 1081 |
+
|
| 1082 |
+
out = torch.exp(-0.5 * (mean_sub ** 2).sum(-1) / kp_variance)
|
| 1083 |
+
|
| 1084 |
+
return out
|
| 1085 |
+
|
| 1086 |
+
|
| 1087 |
+
def make_coordinate_grid(spatial_size, type):
|
| 1088 |
+
"""
|
| 1089 |
+
Create a meshgrid [-1,1] x [-1,1] of given spatial_size.
|
| 1090 |
+
"""
|
| 1091 |
+
h, w = spatial_size
|
| 1092 |
+
x = torch.arange(w).type(type)
|
| 1093 |
+
y = torch.arange(h).type(type)
|
| 1094 |
+
|
| 1095 |
+
x = (2 * (x / (w - 1)) - 1)
|
| 1096 |
+
y = (2 * (y / (h - 1)) - 1)
|
| 1097 |
+
|
| 1098 |
+
yy = y.view(-1, 1).repeat(1, w)
|
| 1099 |
+
xx = x.view(1, -1).repeat(h, 1)
|
| 1100 |
+
|
| 1101 |
+
meshed = torch.cat([xx.unsqueeze_(2), yy.unsqueeze_(2)], 2)
|
| 1102 |
+
|
| 1103 |
+
return meshed
|
| 1104 |
+
|
| 1105 |
+
|
| 1106 |
+
class ResBlock2d(nn.Module):
|
| 1107 |
+
"""
|
| 1108 |
+
Res block, preserve spatial resolution.
|
| 1109 |
+
"""
|
| 1110 |
+
|
| 1111 |
+
def __init__(self, in_features, kernel_size, padding):
|
| 1112 |
+
super(ResBlock2d, self).__init__()
|
| 1113 |
+
self.conv1 = nn.Conv2d(in_channels=in_features, out_channels=in_features, kernel_size=kernel_size,
|
| 1114 |
+
padding=padding)
|
| 1115 |
+
self.conv2 = nn.Conv2d(in_channels=in_features, out_channels=in_features, kernel_size=kernel_size,
|
| 1116 |
+
padding=padding)
|
| 1117 |
+
self.norm1 = BatchNorm2d(in_features, affine=True)
|
| 1118 |
+
self.norm2 = BatchNorm2d(in_features, affine=True)
|
| 1119 |
+
|
| 1120 |
+
def forward(self, x):
|
| 1121 |
+
out = self.norm1(x)
|
| 1122 |
+
out = F.relu(out)
|
| 1123 |
+
out = self.conv1(out)
|
| 1124 |
+
out = self.norm2(out)
|
| 1125 |
+
out = F.relu(out)
|
| 1126 |
+
out = self.conv2(out)
|
| 1127 |
+
out += x
|
| 1128 |
+
return out
|
| 1129 |
+
|
| 1130 |
+
|
| 1131 |
+
class UpBlock2d(nn.Module):
|
| 1132 |
+
"""
|
| 1133 |
+
Upsampling block for use in decoder.
|
| 1134 |
+
"""
|
| 1135 |
+
|
| 1136 |
+
def __init__(self, in_features, out_features, kernel_size=3, padding=1, groups=1):
|
| 1137 |
+
super(UpBlock2d, self).__init__()
|
| 1138 |
+
|
| 1139 |
+
self.conv = nn.Conv2d(in_channels=in_features, out_channels=out_features, kernel_size=kernel_size,
|
| 1140 |
+
padding=padding, groups=groups)
|
| 1141 |
+
self.norm = BatchNorm2d(out_features, affine=True)
|
| 1142 |
+
|
| 1143 |
+
def forward(self, x):
|
| 1144 |
+
out = F.interpolate(x, scale_factor=2)
|
| 1145 |
+
out = self.conv(out)
|
| 1146 |
+
out = self.norm(out)
|
| 1147 |
+
out = F.relu(out)
|
| 1148 |
+
return out
|
| 1149 |
+
|
| 1150 |
+
|
| 1151 |
+
class DownBlock2d(nn.Module):
|
| 1152 |
+
"""
|
| 1153 |
+
Downsampling block for use in encoder.
|
| 1154 |
+
"""
|
| 1155 |
+
|
| 1156 |
+
def __init__(self, in_features, out_features, kernel_size=3, padding=1, groups=1):
|
| 1157 |
+
super(DownBlock2d, self).__init__()
|
| 1158 |
+
self.conv = nn.Conv2d(in_channels=in_features, out_channels=out_features, kernel_size=kernel_size,
|
| 1159 |
+
padding=padding, groups=groups)
|
| 1160 |
+
self.norm = BatchNorm2d(out_features, affine=True)
|
| 1161 |
+
self.pool = nn.AvgPool2d(kernel_size=(2, 2))
|
| 1162 |
+
|
| 1163 |
+
def forward(self, x):
|
| 1164 |
+
out = self.conv(x)
|
| 1165 |
+
out = self.norm(out)
|
| 1166 |
+
out = F.relu(out)
|
| 1167 |
+
out = self.pool(out)
|
| 1168 |
+
return out
|
| 1169 |
+
|
| 1170 |
+
|
| 1171 |
+
class SameBlock2d(nn.Module):
|
| 1172 |
+
"""
|
| 1173 |
+
Simple block, preserve spatial resolution.
|
| 1174 |
+
"""
|
| 1175 |
+
|
| 1176 |
+
def __init__(self, in_features, out_features, groups=1, kernel_size=3, padding=1):
|
| 1177 |
+
super(SameBlock2d, self).__init__()
|
| 1178 |
+
self.conv = nn.Conv2d(in_channels=in_features, out_channels=out_features,
|
| 1179 |
+
kernel_size=kernel_size, padding=padding, groups=groups)
|
| 1180 |
+
self.norm = BatchNorm2d(out_features, affine=True)
|
| 1181 |
+
|
| 1182 |
+
def forward(self, x):
|
| 1183 |
+
out = self.conv(x)
|
| 1184 |
+
out = self.norm(out)
|
| 1185 |
+
out = F.relu(out)
|
| 1186 |
+
return out
|
| 1187 |
+
|
| 1188 |
+
|
| 1189 |
+
class Encoder(nn.Module):
|
| 1190 |
+
"""
|
| 1191 |
+
Hourglass Encoder
|
| 1192 |
+
"""
|
| 1193 |
+
|
| 1194 |
+
def __init__(self, block_expansion, in_features, num_blocks=3, max_features=256):
|
| 1195 |
+
super(Encoder, self).__init__()
|
| 1196 |
+
|
| 1197 |
+
down_blocks = []
|
| 1198 |
+
for i in range(num_blocks):
|
| 1199 |
+
down_blocks.append(DownBlock2d(in_features if i == 0 else min(max_features, block_expansion * (2 ** i)),
|
| 1200 |
+
min(max_features, block_expansion * (2 ** (i + 1))),
|
| 1201 |
+
kernel_size=3, padding=1))
|
| 1202 |
+
self.down_blocks = nn.ModuleList(down_blocks)
|
| 1203 |
+
|
| 1204 |
+
def forward(self, x):
|
| 1205 |
+
outs = [x]
|
| 1206 |
+
for down_block in self.down_blocks:
|
| 1207 |
+
outs.append(down_block(outs[-1]))
|
| 1208 |
+
return outs
|
| 1209 |
+
|
| 1210 |
+
|
| 1211 |
+
class Decoder(nn.Module):
|
| 1212 |
+
"""
|
| 1213 |
+
Hourglass Decoder
|
| 1214 |
+
"""
|
| 1215 |
+
|
| 1216 |
+
def __init__(self, block_expansion, in_features, num_blocks=3, max_features=256):
|
| 1217 |
+
super(Decoder, self).__init__()
|
| 1218 |
+
|
| 1219 |
+
up_blocks = []
|
| 1220 |
+
|
| 1221 |
+
for i in range(num_blocks)[::-1]:
|
| 1222 |
+
in_filters = (1 if i == num_blocks - 1 else 2) * min(max_features, block_expansion * (2 ** (i + 1)))
|
| 1223 |
+
out_filters = min(max_features, block_expansion * (2 ** i))
|
| 1224 |
+
up_blocks.append(UpBlock2d(in_filters, out_filters, kernel_size=3, padding=1))
|
| 1225 |
+
|
| 1226 |
+
self.up_blocks = nn.ModuleList(up_blocks)
|
| 1227 |
+
self.out_filters = block_expansion + in_features
|
| 1228 |
+
|
| 1229 |
+
def forward(self, x):
|
| 1230 |
+
out = x.pop()
|
| 1231 |
+
for up_block in self.up_blocks:
|
| 1232 |
+
out = up_block(out)
|
| 1233 |
+
skip = x.pop()
|
| 1234 |
+
out = torch.cat([out, skip], dim=1)
|
| 1235 |
+
return out
|
| 1236 |
+
|
| 1237 |
+
|
| 1238 |
+
class Hourglass(nn.Module):
|
| 1239 |
+
"""
|
| 1240 |
+
Hourglass architecture.
|
| 1241 |
+
"""
|
| 1242 |
+
|
| 1243 |
+
def __init__(self, block_expansion, in_features, num_blocks=3, max_features=256):
|
| 1244 |
+
super(Hourglass, self).__init__()
|
| 1245 |
+
self.encoder = Encoder(block_expansion, in_features, num_blocks, max_features)
|
| 1246 |
+
self.decoder = Decoder(block_expansion, in_features, num_blocks, max_features)
|
| 1247 |
+
self.out_filters = self.decoder.out_filters
|
| 1248 |
+
|
| 1249 |
+
def forward(self, x):
|
| 1250 |
+
return self.decoder(self.encoder(x))
|
| 1251 |
+
|
| 1252 |
+
|
| 1253 |
+
class AntiAliasInterpolation2d(nn.Module):
|
| 1254 |
+
"""
|
| 1255 |
+
Band-limited downsampling, for better preservation of the input signal.
|
| 1256 |
+
"""
|
| 1257 |
+
def __init__(self, channels, scale):
|
| 1258 |
+
super(AntiAliasInterpolation2d, self).__init__()
|
| 1259 |
+
# sigma = (1 / scale - 1) / 2
|
| 1260 |
+
sigma = 1.5
|
| 1261 |
+
kernel_size = 2 * round(sigma * 4) + 1
|
| 1262 |
+
self.ka = kernel_size // 2
|
| 1263 |
+
self.kb = self.ka - 1 if kernel_size % 2 == 0 else self.ka
|
| 1264 |
+
|
| 1265 |
+
kernel_size = [kernel_size, kernel_size]
|
| 1266 |
+
sigma = [sigma, sigma]
|
| 1267 |
+
# The gaussian kernel is the product of the
|
| 1268 |
+
# gaussian function of each dimension.
|
| 1269 |
+
kernel = 1
|
| 1270 |
+
meshgrids = torch.meshgrid(
|
| 1271 |
+
[
|
| 1272 |
+
torch.arange(size, dtype=torch.float32)
|
| 1273 |
+
for size in kernel_size
|
| 1274 |
+
]
|
| 1275 |
+
)
|
| 1276 |
+
for size, std, mgrid in zip(kernel_size, sigma, meshgrids):
|
| 1277 |
+
mean = (size - 1) / 2
|
| 1278 |
+
kernel *= torch.exp(-(mgrid - mean) ** 2 / (2 * std ** 2))
|
| 1279 |
+
|
| 1280 |
+
# Make sure sum of values in gaussian kernel equals 1.
|
| 1281 |
+
kernel = kernel / torch.sum(kernel)
|
| 1282 |
+
# Reshape to depthwise convolutional weight
|
| 1283 |
+
kernel = kernel.view(1, 1, *kernel.size())
|
| 1284 |
+
kernel = kernel.repeat(channels, *[1] * (kernel.dim() - 1))
|
| 1285 |
+
|
| 1286 |
+
self.register_buffer('weight', kernel)
|
| 1287 |
+
self.groups = channels
|
| 1288 |
+
self.scale = scale
|
| 1289 |
+
inv_scale = 1 / scale
|
| 1290 |
+
self.int_inv_scale = int(inv_scale)
|
| 1291 |
+
|
| 1292 |
+
def forward(self, input):
|
| 1293 |
+
if self.scale == 1.0:
|
| 1294 |
+
return input
|
| 1295 |
+
|
| 1296 |
+
out = F.pad(input, (self.ka, self.kb, self.ka, self.kb))
|
| 1297 |
+
out = F.conv2d(out, weight=self.weight, groups=self.groups)
|
| 1298 |
+
out = out[:, :, ::self.int_inv_scale, ::self.int_inv_scale]
|
| 1299 |
+
|
| 1300 |
+
return out
|
| 1301 |
+
|
| 1302 |
+
def sigmoid(x):
|
| 1303 |
+
return 1 / (1 + math.exp(-x))
|
| 1304 |
+
|
| 1305 |
+
|
| 1306 |
+
def norm_angle(angle):
|
| 1307 |
+
norm_angle = sigmoid(10 * (abs(angle) / 0.7853975 - 1))
|
| 1308 |
+
return norm_angle
|
| 1309 |
+
|
| 1310 |
+
|
| 1311 |
+
def conv3x3(in_planes, out_planes, stride=1):
|
| 1312 |
+
"3x3 convolution with padding"
|
| 1313 |
+
return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
|
| 1314 |
+
padding=1, bias=False)
|
| 1315 |
+
|
| 1316 |
+
|
| 1317 |
+
class BasicBlock(nn.Module):
|
| 1318 |
+
expansion = 1
|
| 1319 |
+
|
| 1320 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
| 1321 |
+
super(BasicBlock, self).__init__()
|
| 1322 |
+
self.conv1 = conv3x3(inplanes, planes, stride)
|
| 1323 |
+
self.bn1 = nn.BatchNorm2d(planes)
|
| 1324 |
+
self.relu = nn.ReLU()
|
| 1325 |
+
self.conv2 = conv3x3(planes, planes)
|
| 1326 |
+
self.bn2 = nn.BatchNorm2d(planes)
|
| 1327 |
+
self.downsample = downsample
|
| 1328 |
+
self.stride = stride
|
| 1329 |
+
|
| 1330 |
+
def forward(self, x):
|
| 1331 |
+
residual = x
|
| 1332 |
+
|
| 1333 |
+
out = self.conv1(x)
|
| 1334 |
+
out = self.bn1(out)
|
| 1335 |
+
out = self.relu(out)
|
| 1336 |
+
|
| 1337 |
+
out = self.conv2(out)
|
| 1338 |
+
out = self.bn2(out)
|
| 1339 |
+
|
| 1340 |
+
if self.downsample is not None:
|
| 1341 |
+
residual = self.downsample(x)
|
| 1342 |
+
|
| 1343 |
+
out += residual
|
| 1344 |
+
out = self.relu(out)
|
| 1345 |
+
|
| 1346 |
+
return out
|
| 1347 |
+
|
| 1348 |
+
|
| 1349 |
+
class Bottleneck(nn.Module):
|
| 1350 |
+
expansion = 4
|
| 1351 |
+
|
| 1352 |
+
def __init__(self, inplanes, planes, stride=1, downsample=None):
|
| 1353 |
+
super(Bottleneck, self).__init__()
|
| 1354 |
+
self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)
|
| 1355 |
+
self.bn1 = nn.BatchNorm2d(planes)
|
| 1356 |
+
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,
|
| 1357 |
+
padding=1, bias=False)
|
| 1358 |
+
self.bn2 = nn.BatchNorm2d(planes)
|
| 1359 |
+
self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
|
| 1360 |
+
self.bn3 = nn.BatchNorm2d(planes * 4)
|
| 1361 |
+
self.relu = nn.ReLU()
|
| 1362 |
+
self.downsample = downsample
|
| 1363 |
+
self.stride = stride
|
| 1364 |
+
|
| 1365 |
+
def forward(self, x):
|
| 1366 |
+
residual = x
|
| 1367 |
+
|
| 1368 |
+
out = self.conv1(x)
|
| 1369 |
+
out = self.bn1(out)
|
| 1370 |
+
out = self.relu(out)
|
| 1371 |
+
|
| 1372 |
+
out = self.conv2(out)
|
| 1373 |
+
out = self.bn2(out)
|
| 1374 |
+
out = self.relu(out)
|
| 1375 |
+
|
| 1376 |
+
out = self.conv3(out)
|
| 1377 |
+
out = self.bn3(out)
|
| 1378 |
+
|
| 1379 |
+
if self.downsample is not None:
|
| 1380 |
+
residual = self.downsample(x)
|
| 1381 |
+
|
| 1382 |
+
out = out + residual
|
| 1383 |
+
out = self.relu(out)
|
| 1384 |
+
|
| 1385 |
+
return out
|
| 1386 |
+
|
| 1387 |
+
class EmDetector(nn.Module):
|
| 1388 |
+
"""
|
| 1389 |
+
Detecting a keypoints. Return keypoint position and jacobian near each keypoint.
|
| 1390 |
+
"""
|
| 1391 |
+
|
| 1392 |
+
def __init__(self, block_expansion, num_channels, max_features,
|
| 1393 |
+
num_blocks, scale_factor=1, num_classes=8):
|
| 1394 |
+
super(EmDetector, self).__init__()
|
| 1395 |
+
self.inplanes = 64
|
| 1396 |
+
self.predictor = Hourglass(block_expansion, in_features=num_channels,
|
| 1397 |
+
max_features=max_features, num_blocks=num_blocks)
|
| 1398 |
+
|
| 1399 |
+
|
| 1400 |
+
|
| 1401 |
+
|
| 1402 |
+
self.scale_factor = scale_factor
|
| 1403 |
+
if self.scale_factor != 1:
|
| 1404 |
+
self.down = AntiAliasInterpolation2d(num_channels, self.scale_factor)
|
| 1405 |
+
self.conv1 = nn.Conv2d(self.predictor.out_filters, 64, kernel_size=3, stride=1, padding=1,
|
| 1406 |
+
bias=False)
|
| 1407 |
+
self.bn1 = nn.BatchNorm2d(64)
|
| 1408 |
+
self.relu = nn.ReLU()
|
| 1409 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
| 1410 |
+
layers = [2,2,2,2]
|
| 1411 |
+
self.layer1 = self._make_layer(BasicBlock, 64, layers[0])
|
| 1412 |
+
self.layer2 = self._make_layer(BasicBlock, 128, layers[1], stride=2)
|
| 1413 |
+
self.layer3 = self._make_layer(BasicBlock, 256, layers[2], stride=2)
|
| 1414 |
+
self.layer4 = self._make_layer(BasicBlock, 512, layers[3], stride=2)
|
| 1415 |
+
self.avgpool = nn.AdaptiveAvgPool2d(1)
|
| 1416 |
+
self.fc = nn.Linear(512 * BasicBlock.expansion, num_classes)
|
| 1417 |
+
self.classify = Classify()
|
| 1418 |
+
def _make_layer(self, block, planes, blocks, stride=1):
|
| 1419 |
+
downsample = None
|
| 1420 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
| 1421 |
+
downsample = nn.Sequential(
|
| 1422 |
+
nn.Conv2d(self.inplanes, planes * block.expansion,
|
| 1423 |
+
kernel_size=1, stride=stride, bias=False),
|
| 1424 |
+
nn.BatchNorm2d(planes * block.expansion),
|
| 1425 |
+
)
|
| 1426 |
+
|
| 1427 |
+
layers = []
|
| 1428 |
+
layers.append(block(self.inplanes, planes, stride, downsample))
|
| 1429 |
+
self.inplanes = planes * block.expansion
|
| 1430 |
+
for i in range(1, blocks):
|
| 1431 |
+
layers.append(block(self.inplanes, planes))
|
| 1432 |
+
|
| 1433 |
+
return nn.Sequential(*layers)
|
| 1434 |
+
|
| 1435 |
+
def adain_feature(self, x): #torch.Size([4, 3, H, W])
|
| 1436 |
+
if self.scale_factor != 1:
|
| 1437 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1438 |
+
|
| 1439 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1440 |
+
|
| 1441 |
+
# out = self.fc(out)
|
| 1442 |
+
|
| 1443 |
+
return feature_map
|
| 1444 |
+
|
| 1445 |
+
def forward(self, x): #torch.Size([4, 3, H, W])
|
| 1446 |
+
if self.scale_factor != 1:
|
| 1447 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1448 |
+
|
| 1449 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1450 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1451 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1452 |
+
f = self.relu(f)
|
| 1453 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1454 |
+
|
| 1455 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1456 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1457 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1458 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1459 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1460 |
+
out = f.squeeze(3).squeeze(2)
|
| 1461 |
+
fake = self.classify(out)
|
| 1462 |
+
# out = self.fc(out)
|
| 1463 |
+
|
| 1464 |
+
return out, fake
|
| 1465 |
+
|
| 1466 |
+
|
| 1467 |
+
|
| 1468 |
+
|
| 1469 |
+
|
| 1470 |
+
|
| 1471 |
+
class Emotion_k(nn.Module):
|
| 1472 |
+
"""
|
| 1473 |
+
Detecting a keypoints. Return keypoint position and jacobian near each keypoint.
|
| 1474 |
+
"""
|
| 1475 |
+
|
| 1476 |
+
def __init__(self, block_expansion, num_channels, max_features,
|
| 1477 |
+
num_blocks, scale_factor=1, num_classes=8):
|
| 1478 |
+
super(Emotion_k, self).__init__()
|
| 1479 |
+
self.inplanes = 64
|
| 1480 |
+
self.predictor = Hourglass(block_expansion, in_features=num_channels,
|
| 1481 |
+
max_features=max_features, num_blocks=num_blocks)
|
| 1482 |
+
|
| 1483 |
+
|
| 1484 |
+
|
| 1485 |
+
|
| 1486 |
+
self.scale_factor = scale_factor
|
| 1487 |
+
if self.scale_factor != 1:
|
| 1488 |
+
self.down = AntiAliasInterpolation2d(num_channels, self.scale_factor)
|
| 1489 |
+
self.conv1 = nn.Conv2d(self.predictor.out_filters, 64, kernel_size=3, stride=1, padding=1,
|
| 1490 |
+
bias=False)
|
| 1491 |
+
self.bn1 = nn.BatchNorm2d(64)
|
| 1492 |
+
self.relu = nn.ReLU()
|
| 1493 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
| 1494 |
+
layers = [2,2,2,2]
|
| 1495 |
+
self.layer1 = self._make_layer(BasicBlock, 64, layers[0])
|
| 1496 |
+
self.layer2 = self._make_layer(BasicBlock, 128, layers[1], stride=2)
|
| 1497 |
+
self.layer3 = self._make_layer(BasicBlock, 256, layers[2], stride=2)
|
| 1498 |
+
self.layer4 = self._make_layer(BasicBlock, 512, layers[3], stride=2)
|
| 1499 |
+
self.avgpool = nn.AdaptiveAvgPool2d(1)
|
| 1500 |
+
self.fc = nn.Linear(512 * BasicBlock.expansion, num_classes)
|
| 1501 |
+
|
| 1502 |
+
self.embed_fn, self.input_ch = get_embedder(10, 0)
|
| 1503 |
+
|
| 1504 |
+
self.fc_p = nn.Sequential(
|
| 1505 |
+
nn.Linear(10 * 126,1024),
|
| 1506 |
+
nn.ReLU(True),
|
| 1507 |
+
nn.Linear(1024,512),
|
| 1508 |
+
nn.ReLU(True),
|
| 1509 |
+
|
| 1510 |
+
)
|
| 1511 |
+
self.fc_n = nn.Sequential(
|
| 1512 |
+
nn.Linear(10 * 6,128),
|
| 1513 |
+
nn.ReLU(True),
|
| 1514 |
+
nn.Linear(128,512),
|
| 1515 |
+
nn.ReLU(True),
|
| 1516 |
+
|
| 1517 |
+
)
|
| 1518 |
+
|
| 1519 |
+
self.fc_all = nn.Sequential(
|
| 1520 |
+
nn.Linear(1024,512),
|
| 1521 |
+
nn.ReLU(True),
|
| 1522 |
+
nn.Linear(512,256),
|
| 1523 |
+
nn.ReLU(True),
|
| 1524 |
+
nn.Linear(256,64),
|
| 1525 |
+
nn.ReLU(True),
|
| 1526 |
+
)
|
| 1527 |
+
|
| 1528 |
+
# self.fc_single = nn.Sequential(
|
| 1529 |
+
# nn.Linear(512,256),
|
| 1530 |
+
# nn.ReLU(True),
|
| 1531 |
+
# nn.Linear(256,64),
|
| 1532 |
+
# nn.ReLU(True),
|
| 1533 |
+
# )
|
| 1534 |
+
|
| 1535 |
+
self.final = nn.Sequential(
|
| 1536 |
+
nn.Conv1d(1,2,4,2,1),
|
| 1537 |
+
nn.MaxPool1d(2,stride=2),
|
| 1538 |
+
nn.ReLU(True),
|
| 1539 |
+
nn.Conv1d(2,4,4,2,1),
|
| 1540 |
+
nn.ReLU(True),
|
| 1541 |
+
nn.Conv1d(4,4,3),
|
| 1542 |
+
|
| 1543 |
+
)
|
| 1544 |
+
|
| 1545 |
+
self.final_4 = nn.Sequential(
|
| 1546 |
+
nn.Conv1d(4,4,3,1,1),
|
| 1547 |
+
nn.MaxPool1d(2,stride=2),
|
| 1548 |
+
nn.ReLU(True),
|
| 1549 |
+
nn.Conv1d(4,4,3,1)
|
| 1550 |
+
|
| 1551 |
+
)
|
| 1552 |
+
|
| 1553 |
+
self.final_10 = nn.Sequential(
|
| 1554 |
+
nn.Conv1d(4,8,3,1,1), #[B,8,16]
|
| 1555 |
+
nn.MaxPool1d(2,stride=2), #[B,8,8]
|
| 1556 |
+
nn.ReLU(True),
|
| 1557 |
+
nn.Conv1d(8,10,3,1), #[B,10,6]
|
| 1558 |
+
|
| 1559 |
+
|
| 1560 |
+
)
|
| 1561 |
+
|
| 1562 |
+
self.classify = Classify()
|
| 1563 |
+
|
| 1564 |
+
def _make_layer(self, block, planes, blocks, stride=1):
|
| 1565 |
+
downsample = None
|
| 1566 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
| 1567 |
+
downsample = nn.Sequential(
|
| 1568 |
+
nn.Conv2d(self.inplanes, planes * block.expansion,
|
| 1569 |
+
kernel_size=1, stride=stride, bias=False),
|
| 1570 |
+
nn.BatchNorm2d(planes * block.expansion),
|
| 1571 |
+
)
|
| 1572 |
+
|
| 1573 |
+
layers = []
|
| 1574 |
+
layers.append(block(self.inplanes, planes, stride, downsample))
|
| 1575 |
+
self.inplanes = planes * block.expansion
|
| 1576 |
+
for i in range(1, blocks):
|
| 1577 |
+
layers.append(block(self.inplanes, planes))
|
| 1578 |
+
|
| 1579 |
+
return nn.Sequential(*layers)
|
| 1580 |
+
|
| 1581 |
+
def linear_10(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1582 |
+
if self.scale_factor != 1:
|
| 1583 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1584 |
+
|
| 1585 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1586 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1587 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1588 |
+
f = self.relu(f)
|
| 1589 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1590 |
+
|
| 1591 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1592 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1593 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1594 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1595 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1596 |
+
out = f.squeeze(3).squeeze(2)
|
| 1597 |
+
fake = self.classify(out)
|
| 1598 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1599 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1600 |
+
posi_input = self.embed_fn(neu_input)
|
| 1601 |
+
posi_input =posi_input.reshape(posi_input.shape[0],-1)
|
| 1602 |
+
ner_feature = self.fc_p(posi_input)
|
| 1603 |
+
all_fc = self.fc_all(torch.cat((out,ner_feature),1)).reshape(-1,4,16)
|
| 1604 |
+
result = self.final_10(all_fc)
|
| 1605 |
+
e_value = result[:,:,:2]
|
| 1606 |
+
e_jacobian = result[:,:,2:].reshape(result.shape[0],10,2,2)
|
| 1607 |
+
kp = {'value': e_value,'jacobian': e_jacobian}
|
| 1608 |
+
|
| 1609 |
+
return kp, fake
|
| 1610 |
+
|
| 1611 |
+
|
| 1612 |
+
def linear_4(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1613 |
+
if self.scale_factor != 1:
|
| 1614 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1615 |
+
|
| 1616 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1617 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1618 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1619 |
+
f = self.relu(f)
|
| 1620 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1621 |
+
|
| 1622 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1623 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1624 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1625 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1626 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1627 |
+
out = f.squeeze(3).squeeze(2)
|
| 1628 |
+
fake = self.classify(out)
|
| 1629 |
+
# jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1630 |
+
# neu_input = torch.cat((value,jacobian),2)
|
| 1631 |
+
# posi_input = self.embed_fn(neu_input)
|
| 1632 |
+
# posi_input =posi_input.reshape(posi_input.shape[0],-1)
|
| 1633 |
+
# ner_feature = self.fc_p(posi_input)
|
| 1634 |
+
# all_fc = self.fc_all(torch.cat((out,ner_feature),1)).reshape(-1,4,16)
|
| 1635 |
+
all_fc = torch.unsqueeze(self.fc_single(out),1)
|
| 1636 |
+
result = self.final(all_fc)
|
| 1637 |
+
e_value = result[:,:,:2]
|
| 1638 |
+
e_jacobian = result[:,:,2:].reshape(result.shape[0],4,2,2)
|
| 1639 |
+
kp = {'value': e_value,'jacobian': e_jacobian}
|
| 1640 |
+
# out = self.fc(out)
|
| 1641 |
+
|
| 1642 |
+
return kp, fake
|
| 1643 |
+
|
| 1644 |
+
def linear_np_10(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1645 |
+
if self.scale_factor != 1:
|
| 1646 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1647 |
+
|
| 1648 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1649 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1650 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1651 |
+
f = self.relu(f)
|
| 1652 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1653 |
+
|
| 1654 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1655 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1656 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1657 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1658 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1659 |
+
out = f.squeeze(3).squeeze(2)
|
| 1660 |
+
fake = self.classify(out)
|
| 1661 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1662 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1663 |
+
|
| 1664 |
+
posi_input =neu_input.reshape(neu_input.shape[0],-1)
|
| 1665 |
+
ner_feature = self.fc_n(posi_input)
|
| 1666 |
+
all_fc = self.fc_all(torch.cat((out,ner_feature),1)).reshape(-1,4,16)
|
| 1667 |
+
result = self.final_10(all_fc)
|
| 1668 |
+
e_value = result[:,:,:2]
|
| 1669 |
+
e_jacobian = result[:,:,2:].reshape(result.shape[0],10,2,2)
|
| 1670 |
+
kp = {'value': e_value,'jacobian': e_jacobian}
|
| 1671 |
+
# out = self.fc(out)
|
| 1672 |
+
|
| 1673 |
+
return kp, fake
|
| 1674 |
+
|
| 1675 |
+
def linear_np_4(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1676 |
+
if self.scale_factor != 1:
|
| 1677 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1678 |
+
|
| 1679 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1680 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1681 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1682 |
+
f = self.relu(f)
|
| 1683 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1684 |
+
|
| 1685 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1686 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1687 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1688 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1689 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1690 |
+
out = f.squeeze(3).squeeze(2)
|
| 1691 |
+
fake = self.classify(out)
|
| 1692 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1693 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1694 |
+
|
| 1695 |
+
posi_input =neu_input.reshape(neu_input.shape[0],-1)
|
| 1696 |
+
ner_feature = self.fc_n(posi_input)
|
| 1697 |
+
all_fc = torch.unsqueeze(self.fc_all(torch.cat((out,ner_feature),1)),1)
|
| 1698 |
+
result = self.final(all_fc)
|
| 1699 |
+
e_value = result[:,:,:2]
|
| 1700 |
+
e_jacobian = result[:,:,2:].reshape(result.shape[0],4,2,2)
|
| 1701 |
+
kp = {'value': e_value,'jacobian': e_jacobian}
|
| 1702 |
+
# out = self.fc(out)
|
| 1703 |
+
|
| 1704 |
+
return kp, fake
|
| 1705 |
+
|
| 1706 |
+
|
| 1707 |
+
def emotion_feature(self, feature, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1708 |
+
|
| 1709 |
+
out = feature
|
| 1710 |
+
fake = self.classify(out)
|
| 1711 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1712 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1713 |
+
posi_input = self.embed_fn(neu_input)
|
| 1714 |
+
posi_input =posi_input.reshape(posi_input.shape[0],-1)
|
| 1715 |
+
ner_feature = self.fc_p(posi_input)
|
| 1716 |
+
all_fc = torch.unsqueeze(self.fc_all(torch.cat((out,ner_feature),1)),1)
|
| 1717 |
+
result = self.final(all_fc)
|
| 1718 |
+
e_value = result[:,:,:2]
|
| 1719 |
+
e_jacobian = result[:,:,2:].reshape(result.shape[0],4,2,2)
|
| 1720 |
+
kp = {'value': e_value,'jacobian': e_jacobian}
|
| 1721 |
+
# out = self.fc(out)
|
| 1722 |
+
|
| 1723 |
+
return kp, fake
|
| 1724 |
+
|
| 1725 |
+
def feature(self, x): #torch.Size([4, 3, H, W])
|
| 1726 |
+
if self.scale_factor != 1:
|
| 1727 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1728 |
+
|
| 1729 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1730 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1731 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1732 |
+
f = self.relu(f)
|
| 1733 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1734 |
+
|
| 1735 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1736 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1737 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1738 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1739 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1740 |
+
out = f.squeeze(3).squeeze(2)
|
| 1741 |
+
|
| 1742 |
+
# out = self.fc(out)
|
| 1743 |
+
|
| 1744 |
+
return out
|
| 1745 |
+
|
| 1746 |
+
def forward(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1747 |
+
if self.scale_factor != 1:
|
| 1748 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1749 |
+
|
| 1750 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1751 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1752 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1753 |
+
f = self.relu(f)
|
| 1754 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1755 |
+
|
| 1756 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1757 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1758 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1759 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1760 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1761 |
+
out = f.squeeze(3).squeeze(2)
|
| 1762 |
+
fake = self.classify(out)
|
| 1763 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1764 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1765 |
+
posi_input = self.embed_fn(neu_input)
|
| 1766 |
+
posi_input =posi_input.reshape(posi_input.shape[0],-1)
|
| 1767 |
+
ner_feature = self.fc_p(posi_input)
|
| 1768 |
+
all_fc = torch.unsqueeze(self.fc_all(torch.cat((out,ner_feature),1)),1)
|
| 1769 |
+
result = self.final(all_fc)
|
| 1770 |
+
e_value = result[:,:,:2]
|
| 1771 |
+
e_jacobian = result[:,:,2:].reshape(result.shape[0],4,2,2)
|
| 1772 |
+
kp = {'value': e_value,'jacobian': e_jacobian}
|
| 1773 |
+
# out = self.fc(out)
|
| 1774 |
+
|
| 1775 |
+
return kp, fake
|
| 1776 |
+
|
| 1777 |
+
class Emotion_map(nn.Module):
|
| 1778 |
+
"""
|
| 1779 |
+
Detecting a keypoints. Return keypoint position and jacobian near each keypoint.
|
| 1780 |
+
"""
|
| 1781 |
+
|
| 1782 |
+
def __init__(self, block_expansion, num_channels, max_features,
|
| 1783 |
+
num_blocks, scale_factor=1, num_classes=8):
|
| 1784 |
+
super(Emotion_map, self).__init__()
|
| 1785 |
+
self.inplanes = 64
|
| 1786 |
+
self.predictor = Hourglass(block_expansion, in_features=num_channels,
|
| 1787 |
+
max_features=max_features, num_blocks=num_blocks)
|
| 1788 |
+
|
| 1789 |
+
|
| 1790 |
+
|
| 1791 |
+
|
| 1792 |
+
self.scale_factor = scale_factor
|
| 1793 |
+
if self.scale_factor != 1:
|
| 1794 |
+
self.down = AntiAliasInterpolation2d(num_channels, self.scale_factor)
|
| 1795 |
+
self.conv1 = nn.Conv2d(self.predictor.out_filters, 64, kernel_size=3, stride=1, padding=1,
|
| 1796 |
+
bias=False)
|
| 1797 |
+
self.bn1 = nn.BatchNorm2d(64)
|
| 1798 |
+
self.relu = nn.ReLU()
|
| 1799 |
+
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
|
| 1800 |
+
layers = [2,2,2,2]
|
| 1801 |
+
self.layer1 = self._make_layer(BasicBlock, 64, layers[0])
|
| 1802 |
+
self.layer2 = self._make_layer(BasicBlock, 128, layers[1], stride=2)
|
| 1803 |
+
self.layer3 = self._make_layer(BasicBlock, 256, layers[2], stride=2)
|
| 1804 |
+
self.layer4 = self._make_layer(BasicBlock, 512, layers[3], stride=2)
|
| 1805 |
+
self.avgpool = nn.AdaptiveAvgPool2d(1)
|
| 1806 |
+
self.fc = nn.Linear(512 * BasicBlock.expansion, num_classes)
|
| 1807 |
+
|
| 1808 |
+
self.embed_fn, self.input_ch = get_embedder(10, 0)
|
| 1809 |
+
|
| 1810 |
+
self.fc_p = nn.Sequential(
|
| 1811 |
+
nn.Linear(10 * 126,1024),
|
| 1812 |
+
nn.ReLU(True),
|
| 1813 |
+
nn.Linear(1024,512),
|
| 1814 |
+
nn.ReLU(True),
|
| 1815 |
+
|
| 1816 |
+
)
|
| 1817 |
+
|
| 1818 |
+
self.fc_all = nn.Sequential(
|
| 1819 |
+
nn.Linear(1024,2048),
|
| 1820 |
+
nn.ReLU(True)
|
| 1821 |
+
)
|
| 1822 |
+
|
| 1823 |
+
self.final = nn.Sequential(
|
| 1824 |
+
nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1, bias=True),#8,8
|
| 1825 |
+
nn.BatchNorm2d(128),
|
| 1826 |
+
nn.ReLU(True),
|
| 1827 |
+
nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=True), #16,16
|
| 1828 |
+
nn.BatchNorm2d(64),
|
| 1829 |
+
nn.ReLU(True),
|
| 1830 |
+
nn.ConvTranspose2d(64, 64, kernel_size=4, stride=2, padding=1, bias=True),#32,32
|
| 1831 |
+
nn.BatchNorm2d(64),
|
| 1832 |
+
nn.ReLU(True),
|
| 1833 |
+
nn.ConvTranspose2d(64, 32+3, kernel_size=4, stride=2, padding=1, bias=True),#64,64
|
| 1834 |
+
|
| 1835 |
+
)
|
| 1836 |
+
|
| 1837 |
+
|
| 1838 |
+
self.classify = Classify()
|
| 1839 |
+
self.kp = nn.Conv2d(in_channels=35, out_channels=10, kernel_size=(7, 7),
|
| 1840 |
+
padding=0)
|
| 1841 |
+
self.jacobian = nn.Conv2d(in_channels=35,
|
| 1842 |
+
out_channels=4 * 10, kernel_size=(7, 7), padding=0)
|
| 1843 |
+
self.jacobian.weight.data.zero_()
|
| 1844 |
+
self.jacobian.bias.data.copy_(torch.tensor([1, 0, 0, 1] * 10, dtype=torch.float))
|
| 1845 |
+
self.temperature = 0.1
|
| 1846 |
+
|
| 1847 |
+
self.kp_4 = nn.Conv2d(in_channels=35, out_channels=4, kernel_size=(7, 7),
|
| 1848 |
+
padding=0)
|
| 1849 |
+
self.jacobian_4 = nn.Conv2d(in_channels=35,
|
| 1850 |
+
out_channels=4 * 4, kernel_size=(7, 7), padding=0)
|
| 1851 |
+
self.jacobian_4.weight.data.zero_()
|
| 1852 |
+
self.jacobian_4.bias.data.copy_(torch.tensor([1, 0, 0, 1] * 4, dtype=torch.float))
|
| 1853 |
+
|
| 1854 |
+
|
| 1855 |
+
def _make_layer(self, block, planes, blocks, stride=1):
|
| 1856 |
+
downsample = None
|
| 1857 |
+
if stride != 1 or self.inplanes != planes * block.expansion:
|
| 1858 |
+
downsample = nn.Sequential(
|
| 1859 |
+
nn.Conv2d(self.inplanes, planes * block.expansion,
|
| 1860 |
+
kernel_size=1, stride=stride, bias=False),
|
| 1861 |
+
nn.BatchNorm2d(planes * block.expansion),
|
| 1862 |
+
)
|
| 1863 |
+
|
| 1864 |
+
layers = []
|
| 1865 |
+
layers.append(block(self.inplanes, planes, stride, downsample))
|
| 1866 |
+
self.inplanes = planes * block.expansion
|
| 1867 |
+
for i in range(1, blocks):
|
| 1868 |
+
layers.append(block(self.inplanes, planes))
|
| 1869 |
+
|
| 1870 |
+
return nn.Sequential(*layers)
|
| 1871 |
+
|
| 1872 |
+
def gaussian2kp(self, heatmap):
|
| 1873 |
+
"""
|
| 1874 |
+
Extract the mean and from a heatmap
|
| 1875 |
+
"""
|
| 1876 |
+
shape = heatmap.shape
|
| 1877 |
+
heatmap = heatmap.unsqueeze(-1) #[4,10,58,58,1]
|
| 1878 |
+
grid = make_coordinate_grid(shape[2:], heatmap.type()).unsqueeze_(0).unsqueeze_(0) #[1,1,58,58,2]
|
| 1879 |
+
value = (heatmap * grid).sum(dim=(2, 3)) #[4,10,2]
|
| 1880 |
+
kp = {'value': value}
|
| 1881 |
+
|
| 1882 |
+
return kp
|
| 1883 |
+
|
| 1884 |
+
def map_4(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1885 |
+
if self.scale_factor != 1:
|
| 1886 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1887 |
+
|
| 1888 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1889 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1890 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1891 |
+
f = self.relu(f)
|
| 1892 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1893 |
+
|
| 1894 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1895 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1896 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1897 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1898 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1899 |
+
out = f.squeeze(3).squeeze(2)
|
| 1900 |
+
fake = self.classify(out)
|
| 1901 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1902 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1903 |
+
posi_input = self.embed_fn(neu_input)
|
| 1904 |
+
posi_input =posi_input.reshape(posi_input.shape[0],-1)
|
| 1905 |
+
ner_feature = self.fc_p(posi_input)
|
| 1906 |
+
all_fc = self.fc_all(torch.cat((out,ner_feature),1)).reshape(-1,128,4,4)
|
| 1907 |
+
feature_map = self.final(all_fc)
|
| 1908 |
+
prediction = self.kp_4(feature_map) #[4,10,H/4-6, W/4-6]
|
| 1909 |
+
|
| 1910 |
+
final_shape = prediction.shape
|
| 1911 |
+
|
| 1912 |
+
heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 1913 |
+
heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 1914 |
+
heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 1915 |
+
|
| 1916 |
+
out = self.gaussian2kp(heatmap)
|
| 1917 |
+
out['heatmap'] = heatmap
|
| 1918 |
+
|
| 1919 |
+
if self.jacobian is not None:
|
| 1920 |
+
jacobian_map = self.jacobian_4(feature_map) ##[4,40,H/4-6, W/4-6]
|
| 1921 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], 4, 4, final_shape[2],
|
| 1922 |
+
final_shape[3])
|
| 1923 |
+
heatmap = heatmap.unsqueeze(2)
|
| 1924 |
+
|
| 1925 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 1926 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 1927 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 1928 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 1929 |
+
out['jacobian'] = jacobian
|
| 1930 |
+
|
| 1931 |
+
|
| 1932 |
+
|
| 1933 |
+
return out, fake
|
| 1934 |
+
|
| 1935 |
+
def forward(self, x, value, jacobian): #torch.Size([4, 3, H, W])
|
| 1936 |
+
if self.scale_factor != 1:
|
| 1937 |
+
x = self.down(x) # 0.25 [4, 3, H/4, W/4]
|
| 1938 |
+
|
| 1939 |
+
feature_map = self.predictor(x) #[4,3+32,H/4, W/4]
|
| 1940 |
+
f = self.conv1(feature_map) #[16,64,64,64]
|
| 1941 |
+
f = self.bn1(f) #torch.Size([16, 64, 64, 64])
|
| 1942 |
+
f = self.relu(f)
|
| 1943 |
+
f = self.maxpool(f) #[16, 64, 32, 32]
|
| 1944 |
+
|
| 1945 |
+
f = self.layer1(f) #[16, 64, 32, 32]
|
| 1946 |
+
f = self.layer2(f) #[16, 128, 16, 16])
|
| 1947 |
+
f = self.layer3(f) #[16, 256, 8, 8]
|
| 1948 |
+
f = self.layer4(f) #[16, 512, 4, 4]
|
| 1949 |
+
f = self.avgpool(f) #[16, 512, 1, 1]
|
| 1950 |
+
out = f.squeeze(3).squeeze(2)
|
| 1951 |
+
fake = self.classify(out)
|
| 1952 |
+
jacobian = jacobian.reshape(jacobian.shape[0],jacobian.shape[1],4)
|
| 1953 |
+
neu_input = torch.cat((value,jacobian),2)
|
| 1954 |
+
posi_input = self.embed_fn(neu_input)
|
| 1955 |
+
posi_input =posi_input.reshape(posi_input.shape[0],-1)
|
| 1956 |
+
ner_feature = self.fc_p(posi_input)
|
| 1957 |
+
all_fc = self.fc_all(torch.cat((out,ner_feature),1)).reshape(-1,128,4,4)
|
| 1958 |
+
feature_map = self.final(all_fc)
|
| 1959 |
+
|
| 1960 |
+
prediction = self.kp(feature_map) #[4,10,H/4-6, W/4-6]
|
| 1961 |
+
|
| 1962 |
+
final_shape = prediction.shape
|
| 1963 |
+
|
| 1964 |
+
heatmap = prediction.view(final_shape[0], final_shape[1], -1) #[4, 10, 58*58]
|
| 1965 |
+
heatmap = F.softmax(heatmap / self.temperature, dim=2)
|
| 1966 |
+
heatmap = heatmap.view(*final_shape) #[4,10,58,58]
|
| 1967 |
+
|
| 1968 |
+
out = self.gaussian2kp(heatmap)
|
| 1969 |
+
out['heatmap'] = heatmap
|
| 1970 |
+
|
| 1971 |
+
if self.jacobian is not None:
|
| 1972 |
+
jacobian_map = self.jacobian(feature_map) ##[4,40,H/4-6, W/4-6]
|
| 1973 |
+
jacobian_map = jacobian_map.reshape(final_shape[0], 10, 4, final_shape[2],
|
| 1974 |
+
final_shape[3])
|
| 1975 |
+
heatmap = heatmap.unsqueeze(2)
|
| 1976 |
+
|
| 1977 |
+
jacobian = heatmap * jacobian_map #[4,10,4,H/4-6, W/4-6]
|
| 1978 |
+
jacobian = jacobian.view(final_shape[0], final_shape[1], 4, -1)
|
| 1979 |
+
jacobian = jacobian.sum(dim=-1) #[4,10,4]
|
| 1980 |
+
jacobian = jacobian.view(jacobian.shape[0], jacobian.shape[1], 2, 2) #[4,10,2,2]
|
| 1981 |
+
out['jacobian'] = jacobian
|
| 1982 |
+
|
| 1983 |
+
|
| 1984 |
+
|
| 1985 |
+
return out, fake
|
| 1986 |
+
|
| 1987 |
+
|
| 1988 |
+
def conv2d(channel_in, channel_out,
|
| 1989 |
+
ksize=3, stride=1, padding=1,
|
| 1990 |
+
activation=nn.ReLU,
|
| 1991 |
+
normalizer=nn.BatchNorm2d):
|
| 1992 |
+
layer = list()
|
| 1993 |
+
bias = True if not normalizer else False
|
| 1994 |
+
|
| 1995 |
+
layer.append(nn.Conv2d(channel_in, channel_out,
|
| 1996 |
+
ksize, stride, padding,
|
| 1997 |
+
bias=bias))
|
| 1998 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 1999 |
+
# init.kaiming_normal(layer[0].weight)
|
| 2000 |
+
|
| 2001 |
+
return nn.Sequential(*layer)
|
| 2002 |
+
|
| 2003 |
+
def _apply(layer, activation, normalizer, channel_out=None):
|
| 2004 |
+
if normalizer:
|
| 2005 |
+
layer.append(normalizer(channel_out))
|
| 2006 |
+
if activation:
|
| 2007 |
+
layer.append(activation())
|
| 2008 |
+
return layer
|
FONT/ops.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
import torchvision
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.init as init
|
| 5 |
+
from torch.autograd import Variable
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class ResidualBlock(nn.Module):
|
| 9 |
+
def __init__(self, channel_in, channel_out):
|
| 10 |
+
super(ResidualBlock, self).__init__()
|
| 11 |
+
|
| 12 |
+
self.block = nn.Sequential(
|
| 13 |
+
conv3d(channel_in, channel_out, 3, 1, 1),
|
| 14 |
+
conv3d(channel_out, channel_out, 3, 1, 1, activation=None)
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
self.lrelu = nn.ReLU(0.2)
|
| 18 |
+
|
| 19 |
+
def forward(self, x):
|
| 20 |
+
residual = x
|
| 21 |
+
out = self.block(x)
|
| 22 |
+
|
| 23 |
+
out += residual
|
| 24 |
+
out = self.lrelu(out)
|
| 25 |
+
return out
|
| 26 |
+
|
| 27 |
+
def linear(channel_in, channel_out,
|
| 28 |
+
activation=nn.ReLU,
|
| 29 |
+
normalizer=nn.BatchNorm1d):
|
| 30 |
+
layer = list()
|
| 31 |
+
bias = True if not normalizer else False
|
| 32 |
+
|
| 33 |
+
layer.append(nn.Linear(channel_in, channel_out, bias=bias))
|
| 34 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 35 |
+
# init.kaiming_normal(layer[0].weight)
|
| 36 |
+
|
| 37 |
+
return nn.Sequential(*layer)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def conv2d(channel_in, channel_out,
|
| 41 |
+
ksize=3, stride=1, padding=1,
|
| 42 |
+
activation=nn.ReLU,
|
| 43 |
+
normalizer=nn.BatchNorm2d):
|
| 44 |
+
layer = list()
|
| 45 |
+
bias = True if not normalizer else False
|
| 46 |
+
|
| 47 |
+
layer.append(nn.Conv2d(channel_in, channel_out,
|
| 48 |
+
ksize, stride, padding,
|
| 49 |
+
bias=bias))
|
| 50 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 51 |
+
# init.kaiming_normal(layer[0].weight)
|
| 52 |
+
|
| 53 |
+
return nn.Sequential(*layer)
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def conv_transpose2d(channel_in, channel_out,
|
| 57 |
+
ksize=4, stride=2, padding=1,
|
| 58 |
+
activation=nn.ReLU,
|
| 59 |
+
normalizer=nn.BatchNorm2d):
|
| 60 |
+
layer = list()
|
| 61 |
+
bias = True if not normalizer else False
|
| 62 |
+
|
| 63 |
+
layer.append(nn.ConvTranspose2d(channel_in, channel_out,
|
| 64 |
+
ksize, stride, padding,
|
| 65 |
+
bias=bias))
|
| 66 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 67 |
+
# init.kaiming_normal(layer[0].weight)
|
| 68 |
+
|
| 69 |
+
return nn.Sequential(*layer)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def nn_conv2d(channel_in, channel_out,
|
| 73 |
+
ksize=3, stride=1, padding=1,
|
| 74 |
+
scale_factor=2,
|
| 75 |
+
activation=nn.ReLU,
|
| 76 |
+
normalizer=nn.BatchNorm2d):
|
| 77 |
+
layer = list()
|
| 78 |
+
bias = True if not normalizer else False
|
| 79 |
+
|
| 80 |
+
layer.append(nn.UpsamplingNearest2d(scale_factor=scale_factor))
|
| 81 |
+
layer.append(nn.Conv2d(channel_in, channel_out,
|
| 82 |
+
ksize, stride, padding,
|
| 83 |
+
bias=bias))
|
| 84 |
+
_apply(layer, activation, normalizer, channel_out)
|
| 85 |
+
# init.kaiming_normal(layer[1].weight)
|
| 86 |
+
|
| 87 |
+
return nn.Sequential(*layer)
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _apply(layer, activation, normalizer, channel_out=None):
|
| 91 |
+
if normalizer:
|
| 92 |
+
layer.append(normalizer(channel_out))
|
| 93 |
+
if activation:
|
| 94 |
+
layer.append(activation())
|
| 95 |
+
return layer
|
| 96 |
+
|
FONT/process_data.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
Created on Thu Jun 24 11:36:01 2021
|
| 4 |
+
|
| 5 |
+
@author: Xinya
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import glob
|
| 10 |
+
import time
|
| 11 |
+
import numpy as np
|
| 12 |
+
import csv
|
| 13 |
+
import cv2
|
| 14 |
+
import dlib
|
| 15 |
+
|
| 16 |
+
from skimage import transform as tf
|
| 17 |
+
|
| 18 |
+
detector = dlib.get_frontal_face_detector()
|
| 19 |
+
predictor = dlib.shape_predictor('./shape_predictor_68_face_landmarks.dat')
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
import imageio
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def save(path, frames, format):
|
| 27 |
+
if format == '.mp4':
|
| 28 |
+
imageio.mimsave(path, frames)
|
| 29 |
+
elif format == '.png':
|
| 30 |
+
if not os.path.exists(path):
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
os.makedirs(path)
|
| 34 |
+
for j, frame in enumerate(frames):
|
| 35 |
+
cv2.imwrite(path+'/'+str(j)+'.png',frame)
|
| 36 |
+
# imageio.imsave(os.path.join(path, str(j) + '.png'), frames[j])
|
| 37 |
+
else:
|
| 38 |
+
print ("Unknown format %s" % format)
|
| 39 |
+
exit()
|
| 40 |
+
|
| 41 |
+
def crop_image(image_path, out_path):
|
| 42 |
+
template = np.load('./M003_template.npy')
|
| 43 |
+
image = cv2.imread(image_path)
|
| 44 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| 45 |
+
rects = detector(gray, 1) #detect human face
|
| 46 |
+
if len(rects) != 1:
|
| 47 |
+
return 0
|
| 48 |
+
for (j, rect) in enumerate(rects):
|
| 49 |
+
shape = predictor(gray, rect) #detect 68 points
|
| 50 |
+
shape = shape_to_np(shape)
|
| 51 |
+
|
| 52 |
+
pts2 = np.float32(template[:47,:])
|
| 53 |
+
# pts2 = np.float32(template[17:35,:])
|
| 54 |
+
# pts1 = np.vstack((landmark[27:36,:], landmark[39,:],landmark[42,:],landmark[45,:]))
|
| 55 |
+
pts1 = np.float32(shape[:47,:]) #eye and nose
|
| 56 |
+
# pts1 = np.float32(landmark[17:35,:])
|
| 57 |
+
tform = tf.SimilarityTransform()
|
| 58 |
+
tform.estimate( pts2, pts1) #Set the transformation matrix with the explicit parameters.
|
| 59 |
+
|
| 60 |
+
dst = tf.warp(image, tform, output_shape=(256, 256))
|
| 61 |
+
|
| 62 |
+
dst = np.array(dst * 255, dtype=np.uint8)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
cv2.imwrite(out_path,dst)
|
| 66 |
+
|
| 67 |
+
def shape_to_np(shape, dtype="int"):
|
| 68 |
+
# initialize the list of (x, y)-coordinates
|
| 69 |
+
coords = np.zeros((shape.num_parts, 2), dtype=dtype)
|
| 70 |
+
|
| 71 |
+
# loop over all facial landmarks and convert them
|
| 72 |
+
# to a 2-tuple of (x, y)-coordinates
|
| 73 |
+
for i in range(0, shape.num_parts):
|
| 74 |
+
coords[i] = (shape.part(i).x, shape.part(i).y)
|
| 75 |
+
|
| 76 |
+
# return the list of (x, y)-coordinates
|
| 77 |
+
return coords
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def crop_image_tem(video_path, out_path):
|
| 81 |
+
image_all = []
|
| 82 |
+
videoCapture = cv2.VideoCapture(video_path)
|
| 83 |
+
success, frame = videoCapture.read()
|
| 84 |
+
n = 0
|
| 85 |
+
while success :
|
| 86 |
+
image_all.append(frame)
|
| 87 |
+
n = n + 1
|
| 88 |
+
success, frame = videoCapture.read()
|
| 89 |
+
|
| 90 |
+
if len(image_all)!=0 :
|
| 91 |
+
template = np.load('./M003_template.npy')
|
| 92 |
+
image=image_all[0]
|
| 93 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
| 94 |
+
rects = detector(gray, 1) #detect human face
|
| 95 |
+
if len(rects) != 1:
|
| 96 |
+
return 0
|
| 97 |
+
for (j, rect) in enumerate(rects):
|
| 98 |
+
shape = predictor(gray, rect) #detect 68 points
|
| 99 |
+
shape = shape_to_np(shape)
|
| 100 |
+
|
| 101 |
+
pts2 = np.float32(template[:47,:])
|
| 102 |
+
# pts2 = np.float32(template[17:35,:])
|
| 103 |
+
# pts1 = np.vstack((landmark[27:36,:], landmark[39,:],landmark[42,:],landmark[45,:]))
|
| 104 |
+
pts1 = np.float32(shape[:47,:]) #eye and nose
|
| 105 |
+
# pts1 = np.float32(landmark[17:35,:])
|
| 106 |
+
tform = tf.SimilarityTransform()
|
| 107 |
+
tform.estimate( pts2, pts1) #Set the transformation matrix with the explicit parameters.
|
| 108 |
+
out = []
|
| 109 |
+
for i in range(len(image_all)):
|
| 110 |
+
image = image_all[i]
|
| 111 |
+
dst = tf.warp(image, tform, output_shape=(256, 256))
|
| 112 |
+
|
| 113 |
+
dst = np.array(dst * 255, dtype=np.uint8)
|
| 114 |
+
out.append(dst)
|
| 115 |
+
if not os.path.exists(out_path):
|
| 116 |
+
os.makedirs(out_path)
|
| 117 |
+
save(out_path,out,'.png')
|
| 118 |
+
|
| 119 |
+
def proc_audio(src_mouth_path, dst_audio_path):
|
| 120 |
+
audio_command = 'ffmpeg -i \"{}\" -loglevel error -y -f wav -acodec pcm_s16le ' \
|
| 121 |
+
'-ar 16000 \"{}\"'.format(src_mouth_path, dst_audio_path)
|
| 122 |
+
os.system(audio_command)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__":
|
| 127 |
+
#video alignment
|
| 128 |
+
video_path = './test/crop/M030_sad_3_001.mp4'
|
| 129 |
+
out_path = './test/crop/M030_sad_3_001'
|
| 130 |
+
crop_image_tem(video_path, out_path)
|
| 131 |
+
|
| 132 |
+
#image alignment
|
| 133 |
+
image_path = './test/raw_image/brade2.jpg'
|
| 134 |
+
out_path = './test/image/brade2.jpg'
|
| 135 |
+
crop_image(image_path, out_path)
|
| 136 |
+
|
| 137 |
+
#change_audio_sample_rate
|
| 138 |
+
src_mouth_path = './test/audio/00015.mp3'
|
| 139 |
+
dst_audio_path = './test/audio/00015.mov'
|
| 140 |
+
proc_audio(src_mouth_path, dst_audio_path)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
|
FONT/requirements.txt
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch==1.10.1
|
| 2 |
+
torchvision==0.11.2
|
| 3 |
+
numpy
|
| 4 |
+
librosa
|
| 5 |
+
opencv-python
|
| 6 |
+
python_speech_features
|
| 7 |
+
pickle
|
| 8 |
+
matplotlib
|
| 9 |
+
scikit-image
|
| 10 |
+
Pillow
|
| 11 |
+
tqdm
|
| 12 |
+
dlib
|
| 13 |
+
scipy
|
| 14 |
+
yaml
|
| 15 |
+
imageio
|
| 16 |
+
csv
|
FONT/result/all.mov
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:dece5be32b925f419a781d3b931ceadfde409a3e89629161afbe61e8bf6cc42d
|
| 3 |
+
size 966293
|
FONT/result/all_with_pose.mov
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac55c4f07db9444a5291bb619865e04c8361f276fdb4da8f39f95f49b8daed60
|
| 3 |
+
size 1018871
|
FONT/run.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import matplotlib
|
| 2 |
+
|
| 3 |
+
matplotlib.use('Agg')
|
| 4 |
+
|
| 5 |
+
import os, sys
|
| 6 |
+
import yaml
|
| 7 |
+
from argparse import ArgumentParser
|
| 8 |
+
from time import gmtime, strftime
|
| 9 |
+
from shutil import copy
|
| 10 |
+
|
| 11 |
+
# from frames_dataset import MeadDataset, AudioDataset, VoxDataset
|
| 12 |
+
from frames_dataset_liujin import MeadDataset, AudioDataset, VoxDataset, HDTFDataset
|
| 13 |
+
|
| 14 |
+
from modules.generator import OcclusionAwareGenerator
|
| 15 |
+
from modules.discriminator import MultiScaleDiscriminator
|
| 16 |
+
from modules.keypoint_detector import KPDetector, Audio_Feature, KPDetector_a
|
| 17 |
+
from modules.util import AT_net,Emotion_k
|
| 18 |
+
# from modules.util import get_logger
|
| 19 |
+
import torch
|
| 20 |
+
|
| 21 |
+
from train import train_part1, train_part1_fine_tune, train_part2
|
| 22 |
+
# from reconstruction import reconstruction
|
| 23 |
+
# from animate import animate
|
| 24 |
+
|
| 25 |
+
import warnings
|
| 26 |
+
warnings.filterwarnings("ignore")
|
| 27 |
+
|
| 28 |
+
if __name__ == "__main__":
|
| 29 |
+
|
| 30 |
+
if sys.version_info[0] < 3:
|
| 31 |
+
raise Exception("You must use Python 3 or higher. Recommended version is Python 3.7")
|
| 32 |
+
|
| 33 |
+
parser = ArgumentParser()
|
| 34 |
+
parser.add_argument("--config", default="config/train_part1.yaml", help="path to config")# required=True
|
| 35 |
+
parser.add_argument("--mode", default="train_part1", choices=["train_part1", "train_part1_fine_tune", "train_part2"])
|
| 36 |
+
parser.add_argument("--log_dir", default='log', help="path to log into")
|
| 37 |
+
parser.add_argument("--checkpoint", default='124_52000.pth.tar', help="path to checkpoint to restore")
|
| 38 |
+
parser.add_argument("--audio_checkpoint", default=None, help="path to audio_checkpoint to restore")
|
| 39 |
+
parser.add_argument("--emo_checkpoint", default=None, help="path to audio_checkpoint to restore")
|
| 40 |
+
parser.add_argument("--device_ids", default="0", type=lambda x: list(map(int, x.split(','))),
|
| 41 |
+
help="Names of the devices comma separated.")
|
| 42 |
+
parser.add_argument("--verbose", dest="verbose", action="store_true", help="Print model architecture")
|
| 43 |
+
parser.set_defaults(verbose=False)
|
| 44 |
+
parser.add_argument("--comment", default='comment', help="comment about experiment")
|
| 45 |
+
|
| 46 |
+
opt = parser.parse_args()
|
| 47 |
+
with open(opt.config) as f:
|
| 48 |
+
config = yaml.load(f)
|
| 49 |
+
|
| 50 |
+
name = os.path.basename(opt.config).split('.')[0]
|
| 51 |
+
if opt.checkpoint is not None:
|
| 52 |
+
|
| 53 |
+
log_dir = os.path.join(opt.log_dir, os.path.basename(opt.config).split('.')[0])
|
| 54 |
+
# log_dir += ' ' + strftime("%d_%m_%y_%H.%M.%S", gmtime())
|
| 55 |
+
log_dir += '_' + opt.comment
|
| 56 |
+
else:
|
| 57 |
+
log_dir = os.path.join(opt.log_dir, os.path.basename(opt.config).split('.')[0])
|
| 58 |
+
# log_dir += ' ' + strftime("%d_%m_%y_%H.%M.%S", gmtime())
|
| 59 |
+
log_dir += '_' + opt.comment
|
| 60 |
+
|
| 61 |
+
if not os.path.exists(log_dir):
|
| 62 |
+
os.makedirs(log_dir)
|
| 63 |
+
if not os.path.exists(os.path.join(log_dir, os.path.basename(opt.config))):
|
| 64 |
+
copy(opt.config, log_dir)
|
| 65 |
+
|
| 66 |
+
# logger = get_logger(os.path.join(log_dir, "log.txt"))
|
| 67 |
+
|
| 68 |
+
generator = OcclusionAwareGenerator(**config['model_params']['generator_params'],
|
| 69 |
+
**config['model_params']['common_params'])
|
| 70 |
+
|
| 71 |
+
if torch.cuda.is_available():
|
| 72 |
+
generator.to(opt.device_ids[0])
|
| 73 |
+
|
| 74 |
+
if opt.verbose:
|
| 75 |
+
print(generator)
|
| 76 |
+
|
| 77 |
+
discriminator = MultiScaleDiscriminator(**config['model_params']['discriminator_params'],
|
| 78 |
+
**config['model_params']['common_params'])
|
| 79 |
+
if torch.cuda.is_available():
|
| 80 |
+
discriminator.to(opt.device_ids[0])
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
if opt.verbose:
|
| 85 |
+
print(discriminator)
|
| 86 |
+
|
| 87 |
+
kp_detector = KPDetector(**config['model_params']['kp_detector_params'],
|
| 88 |
+
**config['model_params']['common_params'])
|
| 89 |
+
|
| 90 |
+
kp_detector_a = KPDetector_a(**config['model_params']['kp_detector_params'],
|
| 91 |
+
**config['model_params']['audio_params'])
|
| 92 |
+
|
| 93 |
+
if torch.cuda.is_available():
|
| 94 |
+
kp_detector.to(opt.device_ids[0])
|
| 95 |
+
kp_detector_a.to(opt.device_ids[0])
|
| 96 |
+
|
| 97 |
+
audio_feature = AT_net()
|
| 98 |
+
emo_feature = Emotion_k(block_expansion=32, num_channels=3, max_features=1024,
|
| 99 |
+
num_blocks=5, scale_factor=0.25, num_classes=8)
|
| 100 |
+
|
| 101 |
+
if torch.cuda.is_available():
|
| 102 |
+
audio_feature.to(opt.device_ids[0])
|
| 103 |
+
emo_feature.to(opt.device_ids[0])
|
| 104 |
+
|
| 105 |
+
if opt.verbose:
|
| 106 |
+
print(kp_detector)
|
| 107 |
+
print(kp_detector_a)
|
| 108 |
+
print(audio_feature)
|
| 109 |
+
print(emo_feature)
|
| 110 |
+
|
| 111 |
+
# logger.info("Successfully load models.")
|
| 112 |
+
|
| 113 |
+
if config['dataset_params']['name'] == 'Vox':
|
| 114 |
+
dataset = VoxDataset(is_train=True, **config['dataset_params'])
|
| 115 |
+
test_dataset = VoxDataset(is_train=False, **config['dataset_params'])
|
| 116 |
+
elif config['dataset_params']['name'] == 'Lrw':
|
| 117 |
+
dataset = AudioDataset(is_train=True, **config['dataset_params'])
|
| 118 |
+
test_dataset = AudioDataset(is_train=False, **config['dataset_params'])
|
| 119 |
+
elif config['dataset_params']['name'] == 'MEAD':
|
| 120 |
+
dataset = MeadDataset(is_train=True, **config['dataset_params'])
|
| 121 |
+
test_dataset = MeadDataset(is_train=False, **config['dataset_params'])
|
| 122 |
+
elif config['dataset_params']['name'] == 'hdtf':
|
| 123 |
+
dataset = HDTFDataset(is_train=True, **config['dataset_params'])
|
| 124 |
+
test_dataset = HDTFDataset(is_train=False, **config['dataset_params'])
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
if opt.mode == 'train_part1':
|
| 130 |
+
print("Training part1...")
|
| 131 |
+
train_part1(config, generator, discriminator, kp_detector, kp_detector_a,audio_feature, opt.checkpoint, opt.audio_checkpoint, log_dir, dataset, test_dataset,opt.device_ids, name)
|
| 132 |
+
elif opt.mode == 'train_part1_fine_tune':
|
| 133 |
+
print("Finetune part1...")
|
| 134 |
+
train_part1_fine_tune(config, generator, discriminator, kp_detector, kp_detector_a,audio_feature, opt.checkpoint, opt.audio_checkpoint, log_dir, dataset, test_dataset,opt.device_ids, name)
|
| 135 |
+
elif opt.mode == 'train_part2':
|
| 136 |
+
print("Training part2...")
|
| 137 |
+
train_part2(config, generator, discriminator, kp_detector, emo_feature,kp_detector_a,audio_feature, opt.checkpoint, opt.audio_checkpoint, opt.emo_checkpoint, log_dir, dataset,test_dataset,opt.device_ids, name)
|
FONT/script_backup.sh
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
--pose_given "/data/liujin/dataset/preprocess/LRW/data_file/test_video_pose.npy" \
|
| 2 |
+
--pose_given "/data/liujin/_new_idea_211030_ICME/audio2pose_vae/result/test_pose_long_vae.npy" \
|
| 3 |
+
--audio_checkpoint "./log/train_part1_wav2lip_pretrain_3dmm/294-00010000-checkpoint.pth.tar" \
|
| 4 |
+
--checkpoint "./log/train_part1_fine_tune_wav2lip_pretrain_3dmm/392-00080000-checkpoint.pth.tar" \
|
| 5 |
+
--audio_checkpoint "/data/liujin/EAMM-main/log/train_part1_hdtf_wav2lip_pretrain_3dmm_hdtf/3124-00022500-checkpoint.pth.tar" \
|
| 6 |
+
--checkpoint "/data/liujin/EAMM-main/log/train_part1_fine_tune_hdtf_wav2lip_pretrain_3dmm_hdtf/3341-00005000-checkpoint.pth.tar" \
|
| 7 |
+
--pose_long TRUE \
|
| 8 |
+
--pose_file "/data/liujin/dataset/preprocess/LRW/data_file/test_first_frame_pose.npy" \
|
| 9 |
+
--pose_given "/data/liujin/dataset/preprocess/LRW/data_file/test_video_pose.npy" \
|
| 10 |
+
--source_image ./test/image/RD_Radio10_000.png \
|
| 11 |
+
--source_image "./test/image/ABOUT_00994.jpg" \
|
| 12 |
+
--source_image "/data/liujin/dataset/LRW/lipread_frames/ABOUT/train/ABOUT_00994/000000.jpg" \
|
| 13 |
+
--in_file "/data/liujin/dataset/LRW/lipread_wav/ABOUT/train/ABOUT_00994.wav" \
|
| 14 |
+
--pose_file "/data/liujin/dataset/HDTF/pose_3DDFA_256/RD_Radio20_000/000000.npy" \
|
| 15 |
+
--pose_given "/data/liujin/dataset/preprocess/LRW/data_file/test_pose_long_3ddfa.npy" \
|
| 16 |
+
--source_image "/data/liujin/dataset/HDTF/frames_256/RD_Radio26_000/000000.png" \
|
| 17 |
+
--pose_given "./test/pose_long/50IAfJCypFI_Alex_Kingston_50IAfJCypFI_0001.npy" \
|
FONT/script_finetune_liujin.sh
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
CUDA_VISIBLE_DEVICES=3 nohup python -u run.py --config config/train_part1_fine_tune.yaml --mode train_part1_fine_tune --checkpoint ckpt/124_52000.pth.tar --audio_checkpoint log/train_part1_wav2lip_pretrain_3dmm/294-00010000-checkpoint.pth.tar --comment wav2lip_pretrain_3dmm > out_liujin_finetune_wav2lip_pretrain_3dmm.log 2>&1 &
|