mirror of
https://github.com/iperov/DeepFaceLab.git
synced 2025-08-22 22:34:25 -07:00
Merge branch 'iperov-master' into build/update-from-upstream/2019-10-24
# Conflicts: # facelib/S3FDExtractor.py # samplelib/SampleGeneratorFace.py
This commit is contained in:
commit
a066cf081f
13 changed files with 234 additions and 89 deletions
|
@ -684,6 +684,9 @@ def draw_rect_landmarks(image, rect, image_landmarks, face_size, face_type, tran
|
||||||
image_to_face_mat, True)
|
image_to_face_mat, True)
|
||||||
imagelib.draw_polygon (image, points, (0,0,255), 2)
|
imagelib.draw_polygon (image, points, (0,0,255), 2)
|
||||||
|
|
||||||
|
points = transform_points ( [ ( int(face_size*0.05), 0), ( int(face_size*0.1), int(face_size*0.1) ), ( 0, int(face_size*0.1) ) ], image_to_face_mat, True)
|
||||||
|
imagelib.draw_polygon (image, points, (0,0,255), 2)
|
||||||
|
|
||||||
|
|
||||||
def calc_face_pitch(landmarks):
|
def calc_face_pitch(landmarks):
|
||||||
if not isinstance(landmarks, np.ndarray):
|
if not isinstance(landmarks, np.ndarray):
|
||||||
|
|
|
@ -12,8 +12,8 @@ class S3FDExtractor(object):
|
||||||
S3FD: Single Shot Scale-invariant Face Detector
|
S3FD: Single Shot Scale-invariant Face Detector
|
||||||
https://arxiv.org/pdf/1708.05237.pdf
|
https://arxiv.org/pdf/1708.05237.pdf
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, do_dummy_predict=False):
|
||||||
exec(nnlib.import_all(), locals(), globals())
|
exec( nnlib.import_all(), locals(), globals() )
|
||||||
|
|
||||||
model_path = Path(__file__).parent / "S3FD.h5"
|
model_path = Path(__file__).parent / "S3FD.h5"
|
||||||
if not model_path.exists():
|
if not model_path.exists():
|
||||||
|
@ -21,13 +21,14 @@ class S3FDExtractor(object):
|
||||||
|
|
||||||
self.model = nnlib.keras.models.load_model ( str(model_path) )
|
self.model = nnlib.keras.models.load_model ( str(model_path) )
|
||||||
|
|
||||||
self.extract ( np.zeros( (1080,1920,3), dtype=np.uint8) )
|
if do_dummy_predict:
|
||||||
|
self.extract ( np.zeros( (640,640,3), dtype=np.uint8) )
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||||
return False # pass exception between __enter__ and __exit__ to outter level
|
return False #pass exception between __enter__ and __exit__ to outter level
|
||||||
|
|
||||||
def extract(self, input_image, is_bgr=True, is_remove_intersects=False, nms_thresh=0.3):
|
def extract(self, input_image, is_bgr=True, is_remove_intersects=False, nms_thresh=0.3):
|
||||||
"""
|
"""
|
||||||
|
@ -40,7 +41,7 @@ class S3FDExtractor(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if is_bgr:
|
if is_bgr:
|
||||||
input_image = input_image[:, :, ::-1]
|
input_image = input_image[:,:,::-1]
|
||||||
is_bgr = False
|
is_bgr = False
|
||||||
|
|
||||||
(h, w, ch) = input_image.shape
|
(h, w, ch) = input_image.shape
|
||||||
|
@ -53,16 +54,16 @@ class S3FDExtractor(object):
|
||||||
input_image = cv2.resize(input_image, (int(w / input_scale), int(h / input_scale)),
|
input_image = cv2.resize(input_image, (int(w / input_scale), int(h / input_scale)),
|
||||||
interpolation=cv2.INTER_LINEAR)
|
interpolation=cv2.INTER_LINEAR)
|
||||||
|
|
||||||
olist = self.model.predict(np.expand_dims(input_image, 0))
|
olist = self.model.predict( np.expand_dims(input_image,0) )
|
||||||
|
|
||||||
detected_faces = []
|
detected_faces = []
|
||||||
for ltrb in self._refine(olist, nms_thresh):
|
for ltrb in self._refine(olist, nms_thresh):
|
||||||
l, t, r, b = [x * input_scale for x in ltrb]
|
l,t,r,b = [ x*input_scale for x in ltrb]
|
||||||
bt = b - t
|
bt = b-t
|
||||||
if min(r - l, bt) < 40: # filtering faces < 40pix by any side
|
if min(r-l,bt) < 40: #filtering faces < 40pix by any side
|
||||||
continue
|
continue
|
||||||
b += bt * 0.1 # enlarging bottom line a bit for 2DFAN-4, because default is not enough covering a chin
|
b += bt*0.1 #enlarging bottom line a bit for 2DFAN-4, because default is not enough covering a chin
|
||||||
detected_faces.append([int(x) for x in (l, t, r, b)])
|
detected_faces.append ( [int(x) for x in (l,t,r,b) ] )
|
||||||
|
|
||||||
#sort by largest area first
|
#sort by largest area first
|
||||||
detected_faces = [ [(l,t,r,b), (r-l)*(b-t) ] for (l,t,r,b) in detected_faces ]
|
detected_faces = [ [(l,t,r,b), (r-l)*(b-t) ] for (l,t,r,b) in detected_faces ]
|
||||||
|
@ -83,18 +84,18 @@ class S3FDExtractor(object):
|
||||||
|
|
||||||
def _refine(self, olist, thresh):
|
def _refine(self, olist, thresh):
|
||||||
bboxlist = []
|
bboxlist = []
|
||||||
for i, ((ocls,), (oreg,)) in enumerate(zip(olist[::2], olist[1::2])):
|
for i, ((ocls,), (oreg,)) in enumerate ( zip ( olist[::2], olist[1::2] ) ):
|
||||||
stride = 2 ** (i + 2) # 4,8,16,32,64,128
|
stride = 2**(i + 2) # 4,8,16,32,64,128
|
||||||
s_d2 = stride / 2
|
s_d2 = stride / 2
|
||||||
s_m4 = stride * 4
|
s_m4 = stride * 4
|
||||||
|
|
||||||
for hindex, windex in zip(*np.where(ocls > 0.05)):
|
for hindex, windex in zip(*np.where(ocls > 0.05)):
|
||||||
score = ocls[hindex, windex]
|
score = ocls[hindex, windex]
|
||||||
loc = oreg[hindex, windex, :]
|
loc = oreg[hindex, windex, :]
|
||||||
priors = np.array([windex * stride + s_d2, hindex * stride + s_d2, s_m4, s_m4])
|
priors = np.array([windex * stride + s_d2, hindex * stride + s_d2, s_m4, s_m4])
|
||||||
priors_2p = priors[2:]
|
priors_2p = priors[2:]
|
||||||
box = np.concatenate((priors[:2] + loc[:2] * 0.1 * priors_2p,
|
box = np.concatenate((priors[:2] + loc[:2] * 0.1 * priors_2p,
|
||||||
priors_2p * np.exp(loc[2:] * 0.2)))
|
priors_2p * np.exp(loc[2:] * 0.2)) )
|
||||||
box[:2] -= box[2:] / 2
|
box[:2] -= box[2:] / 2
|
||||||
box[2:] += box[:2]
|
box[2:] += box[:2]
|
||||||
|
|
||||||
|
@ -104,7 +105,7 @@ class S3FDExtractor(object):
|
||||||
if len(bboxlist) == 0:
|
if len(bboxlist) == 0:
|
||||||
bboxlist = np.zeros((1, 5))
|
bboxlist = np.zeros((1, 5))
|
||||||
bboxlist = bboxlist[self._refine_nms(bboxlist, thresh), :]
|
bboxlist = bboxlist[self._refine_nms(bboxlist, thresh), :]
|
||||||
bboxlist = [x[:-1].astype(np.int) for x in bboxlist if x[-1] >= 0.5]
|
bboxlist = [ x[:-1].astype(np.int) for x in bboxlist if x[-1] >= 0.5]
|
||||||
return bboxlist
|
return bboxlist
|
||||||
|
|
||||||
def _refine_nms(self, dets, nms_thresh):
|
def _refine_nms(self, dets, nms_thresh):
|
||||||
|
|
2
main.py
2
main.py
|
@ -112,7 +112,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
p = subparsers.add_parser( "sort", help="Sort faces in a directory.")
|
p = subparsers.add_parser( "sort", help="Sort faces in a directory.")
|
||||||
p.add_argument('--input-dir', required=True, action=fixPathAction, dest="input_dir", help="Input directory. A directory containing the files you wish to process.")
|
p.add_argument('--input-dir', required=True, action=fixPathAction, dest="input_dir", help="Input directory. A directory containing the files you wish to process.")
|
||||||
p.add_argument('--by', required=True, dest="sort_by_method", choices=("blur", "face", "face-dissim", "face-yaw", "face-pitch", "hist", "hist-dissim", "brightness", "hue", "black", "origname", "oneface", "final", "final-no-blur", "test"), help="Method of sorting. 'origname' sort by original filename to recover original sequence." )
|
p.add_argument('--by', required=True, dest="sort_by_method", choices=("blur", "face", "face-dissim", "face-yaw", "face-pitch", "hist", "hist-dissim", "brightness", "hue", "black", "origname", "oneface", "final", "final-no-blur", "vggface", "test"), help="Method of sorting. 'origname' sort by original filename to recover original sequence." )
|
||||||
p.set_defaults (func=process_sort)
|
p.set_defaults (func=process_sort)
|
||||||
|
|
||||||
def process_util(arguments):
|
def process_util(arguments):
|
||||||
|
|
|
@ -77,7 +77,7 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.e = facelib.DLIBExtractor(nnlib.dlib)
|
self.e = facelib.DLIBExtractor(nnlib.dlib)
|
||||||
elif self.type == 'rects-s3fd':
|
elif self.type == 'rects-s3fd':
|
||||||
nnlib.import_all (device_config)
|
nnlib.import_all (device_config)
|
||||||
self.e = facelib.S3FDExtractor()
|
self.e = facelib.S3FDExtractor(do_dummy_predict=True)
|
||||||
else:
|
else:
|
||||||
raise ValueError ("Wrong type.")
|
raise ValueError ("Wrong type.")
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ class ExtractSubprocessor(Subprocessor):
|
||||||
self.e = facelib.FANExtractor()
|
self.e = facelib.FANExtractor()
|
||||||
self.e.__enter__()
|
self.e.__enter__()
|
||||||
if self.device_vram >= 2:
|
if self.device_vram >= 2:
|
||||||
self.second_pass_e = facelib.S3FDExtractor()
|
self.second_pass_e = facelib.S3FDExtractor(do_dummy_predict=False)
|
||||||
self.second_pass_e.__enter__()
|
self.second_pass_e.__enter__()
|
||||||
else:
|
else:
|
||||||
self.second_pass_e = None
|
self.second_pass_e = None
|
||||||
|
|
|
@ -1,19 +1,26 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import operator
|
|
||||||
import numpy as np
|
|
||||||
import cv2
|
|
||||||
from shutil import copyfile
|
|
||||||
from pathlib import Path
|
|
||||||
from utils import Path_utils
|
|
||||||
from utils.DFLPNG import DFLPNG
|
|
||||||
from utils.DFLJPG import DFLJPG
|
|
||||||
from utils.cv2_utils import *
|
|
||||||
from facelib import LandmarksProcessor
|
|
||||||
from joblib import Subprocessor
|
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
from interact import interact as io
|
import operator
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import copyfile
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from numpy import linalg as npla
|
||||||
|
|
||||||
|
import imagelib
|
||||||
|
from facelib import LandmarksProcessor
|
||||||
|
from functools import cmp_to_key
|
||||||
from imagelib import estimate_sharpness
|
from imagelib import estimate_sharpness
|
||||||
|
from interact import interact as io
|
||||||
|
from joblib import Subprocessor
|
||||||
|
from nnlib import VGGFace
|
||||||
|
from utils import Path_utils
|
||||||
|
from utils.cv2_utils import *
|
||||||
|
from utils.DFLJPG import DFLJPG
|
||||||
|
from utils.DFLPNG import DFLPNG
|
||||||
|
|
||||||
|
|
||||||
class BlurEstimatorSubprocessor(Subprocessor):
|
class BlurEstimatorSubprocessor(Subprocessor):
|
||||||
class Cli(Subprocessor.Cli):
|
class Cli(Subprocessor.Cli):
|
||||||
|
@ -790,6 +797,79 @@ def sort_final(input_path, include_by_blur=True):
|
||||||
|
|
||||||
return final_img_list, trash_img_list
|
return final_img_list, trash_img_list
|
||||||
|
|
||||||
|
|
||||||
|
def sort_by_vggface(input_path):
|
||||||
|
io.log_info ("Sorting by face similarity using VGGFace model...")
|
||||||
|
|
||||||
|
model = VGGFace()
|
||||||
|
|
||||||
|
final_img_list = []
|
||||||
|
trash_img_list = []
|
||||||
|
|
||||||
|
image_paths = Path_utils.get_image_paths(input_path)
|
||||||
|
img_list = [ (x,) for x in image_paths ]
|
||||||
|
img_list_len = len(img_list)
|
||||||
|
img_list_range = [*range(img_list_len)]
|
||||||
|
|
||||||
|
feats = [None]*img_list_len
|
||||||
|
for i in io.progress_bar_generator(img_list_range, "Loading"):
|
||||||
|
img = cv2_imread( img_list[i][0] ).astype(np.float32)
|
||||||
|
img = imagelib.normalize_channels (img, 3)
|
||||||
|
img = cv2.resize (img, (224,224) )
|
||||||
|
img = img[..., ::-1]
|
||||||
|
img[..., 0] -= 93.5940
|
||||||
|
img[..., 1] -= 104.7624
|
||||||
|
img[..., 2] -= 129.1863
|
||||||
|
feats[i] = model.predict( img[None,...] )[0]
|
||||||
|
|
||||||
|
tmp = np.zeros( (img_list_len,) )
|
||||||
|
float_inf = float("inf")
|
||||||
|
for i in io.progress_bar_generator ( range(img_list_len-1), "Sorting" ):
|
||||||
|
i_feat = feats[i]
|
||||||
|
|
||||||
|
for j in img_list_range:
|
||||||
|
tmp[j] = npla.norm(i_feat-feats[j]) if j >= i+1 else float_inf
|
||||||
|
|
||||||
|
idx = np.argmin(tmp)
|
||||||
|
|
||||||
|
img_list[i+1], img_list[idx] = img_list[idx], img_list[i+1]
|
||||||
|
feats[i+1], feats[idx] = feats[idx], feats[i+1]
|
||||||
|
|
||||||
|
return img_list, trash_img_list
|
||||||
|
|
||||||
|
"""
|
||||||
|
img_list_len = len(img_list)
|
||||||
|
|
||||||
|
for i in io.progress_bar_generator ( range(img_list_len-1), "Sorting" ):
|
||||||
|
a = []
|
||||||
|
i_1 = img_list[i][1]
|
||||||
|
|
||||||
|
|
||||||
|
for j in range(i+1, img_list_len):
|
||||||
|
a.append ( [ j, np.linalg.norm(i_1-img_list[j][1]) ] )
|
||||||
|
|
||||||
|
x = sorted(a, key=operator.itemgetter(1) )[0][0]
|
||||||
|
saved = img_list[i+1]
|
||||||
|
img_list[i+1] = img_list[x]
|
||||||
|
img_list[x] = saved
|
||||||
|
|
||||||
|
|
||||||
|
q = np.array ( [ x[1] for x in img_list ] )
|
||||||
|
|
||||||
|
for i in io.progress_bar_generator ( range(img_list_len-1), "Sorting" ):
|
||||||
|
|
||||||
|
a = np.linalg.norm( q[i] - q[i+1:], axis=1 )
|
||||||
|
a = i+1+np.argmin(a)
|
||||||
|
|
||||||
|
saved = img_list[i+1]
|
||||||
|
img_list[i+1] = img_list[a]
|
||||||
|
img_list[a] = saved
|
||||||
|
|
||||||
|
saved = q[i+1]
|
||||||
|
q[i+1] = q[a]
|
||||||
|
q[a] = saved
|
||||||
|
"""
|
||||||
|
|
||||||
def final_process(input_path, img_list, trash_img_list):
|
def final_process(input_path, img_list, trash_img_list):
|
||||||
if len(trash_img_list) != 0:
|
if len(trash_img_list) != 0:
|
||||||
parent_input_path = input_path.parent
|
parent_input_path = input_path.parent
|
||||||
|
@ -851,6 +931,7 @@ def main (input_path, sort_by_method):
|
||||||
elif sort_by_method == 'black': img_list = sort_by_black (input_path)
|
elif sort_by_method == 'black': img_list = sort_by_black (input_path)
|
||||||
elif sort_by_method == 'origname': img_list, trash_img_list = sort_by_origname (input_path)
|
elif sort_by_method == 'origname': img_list, trash_img_list = sort_by_origname (input_path)
|
||||||
elif sort_by_method == 'oneface': img_list, trash_img_list = sort_by_oneface_in_image (input_path)
|
elif sort_by_method == 'oneface': img_list, trash_img_list = sort_by_oneface_in_image (input_path)
|
||||||
|
elif sort_by_method == 'vggface': img_list, trash_img_list = sort_by_vggface (input_path)
|
||||||
elif sort_by_method == 'final': img_list, trash_img_list = sort_final (input_path)
|
elif sort_by_method == 'final': img_list, trash_img_list = sort_final (input_path)
|
||||||
elif sort_by_method == 'final-no-blur': img_list, trash_img_list = sort_final (input_path, include_by_blur=False)
|
elif sort_by_method == 'final-no-blur': img_list, trash_img_list = sort_final (input_path, include_by_blur=False)
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,10 @@ def extract_vggface2_dataset(input_dir, device_args={} ):
|
||||||
cur_input_path = input_path / dir_name
|
cur_input_path = input_path / dir_name
|
||||||
cur_output_path = output_path / dir_name
|
cur_output_path = output_path / dir_name
|
||||||
|
|
||||||
|
l = len(Path_utils.get_image_paths(cur_input_path))
|
||||||
|
if l < 250 or l > 350:
|
||||||
|
continue
|
||||||
|
|
||||||
io.log_info (f"Processing: {str(cur_input_path)} ")
|
io.log_info (f"Processing: {str(cur_input_path)} ")
|
||||||
|
|
||||||
if not cur_output_path.exists():
|
if not cur_output_path.exists():
|
||||||
|
|
|
@ -57,7 +57,7 @@ def umeyama(src, dst, estimate_scale):
|
||||||
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
|
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
|
||||||
d[dim - 1] = s
|
d[dim - 1] = s
|
||||||
else:
|
else:
|
||||||
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V.T))
|
T[:dim, :dim] = np.dot(U, np.dot(np.diag(d), V))
|
||||||
|
|
||||||
if estimate_scale:
|
if estimate_scale:
|
||||||
# Eq. (41) and (42).
|
# Eq. (41) and (42).
|
||||||
|
|
|
@ -42,7 +42,7 @@ class FUNITModel(ModelBase):
|
||||||
#override
|
#override
|
||||||
def onInitialize(self, batch_size=-1, **in_options):
|
def onInitialize(self, batch_size=-1, **in_options):
|
||||||
exec(nnlib.code_import_all, locals(), globals())
|
exec(nnlib.code_import_all, locals(), globals())
|
||||||
self.set_vram_batch_requirements({4:16})
|
self.set_vram_batch_requirements({4:16,11:24})
|
||||||
|
|
||||||
resolution = self.options['resolution']
|
resolution = self.options['resolution']
|
||||||
face_type = FaceType.FULL if self.options['face_type'] == 'f' else FaceType.HALF
|
face_type = FaceType.FULL if self.options['face_type'] == 'f' else FaceType.HALF
|
||||||
|
@ -75,6 +75,7 @@ class FUNITModel(ModelBase):
|
||||||
face_type = t.FACE_TYPE_FULL if self.options['face_type'] == 'f' else t.FACE_TYPE_HALF
|
face_type = t.FACE_TYPE_FULL if self.options['face_type'] == 'f' else t.FACE_TYPE_HALF
|
||||||
|
|
||||||
output_sample_types=[ {'types': (t.IMG_TRANSFORMED, face_type, t.MODE_BGR), 'resolution':128, 'normalize_tanh':True} ]
|
output_sample_types=[ {'types': (t.IMG_TRANSFORMED, face_type, t.MODE_BGR), 'resolution':128, 'normalize_tanh':True} ]
|
||||||
|
output_sample_types1=[ {'types': (t.IMG_SOURCE, face_type, t.MODE_BGR), 'resolution':128, 'normalize_tanh':True} ]
|
||||||
|
|
||||||
self.set_training_data_generators ([
|
self.set_training_data_generators ([
|
||||||
SampleGeneratorFace(self.training_data_src_path, debug=self.is_debug(), batch_size=self.batch_size,
|
SampleGeneratorFace(self.training_data_src_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||||
|
@ -87,11 +88,11 @@ class FUNITModel(ModelBase):
|
||||||
|
|
||||||
SampleGeneratorFace(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
SampleGeneratorFace(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||||
sample_process_options=SampleProcessor.Options(random_flip=True),
|
sample_process_options=SampleProcessor.Options(random_flip=True),
|
||||||
output_sample_types=output_sample_types, person_id_mode=True ),
|
output_sample_types=output_sample_types1, person_id_mode=True ),
|
||||||
|
|
||||||
SampleGeneratorFace(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
SampleGeneratorFace(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||||
sample_process_options=SampleProcessor.Options(random_flip=True),
|
sample_process_options=SampleProcessor.Options(random_flip=True),
|
||||||
output_sample_types=output_sample_types, person_id_mode=True ),
|
output_sample_types=output_sample_types1, person_id_mode=True ),
|
||||||
])
|
])
|
||||||
|
|
||||||
#override
|
#override
|
||||||
|
|
|
@ -162,10 +162,6 @@ class FUNIT(object):
|
||||||
for w in weights_list:
|
for w in weights_list:
|
||||||
K.set_value( w, K.get_value(initer(K.int_shape(w))) )
|
K.set_value( w, K.get_value(initer(K.int_shape(w))) )
|
||||||
|
|
||||||
#if not self.is_first_run():
|
|
||||||
# self.load_weights_safe(self.get_model_filename_list())
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if load_weights_locally:
|
if load_weights_locally:
|
||||||
pass
|
pass
|
||||||
|
@ -188,9 +184,6 @@ class FUNIT(object):
|
||||||
[self.D_opt, 'D_opt.h5'],
|
[self.D_opt, 'D_opt.h5'],
|
||||||
]
|
]
|
||||||
|
|
||||||
#def save_weights(self):
|
|
||||||
# self.model.save_weights (str(self.weights_path))
|
|
||||||
|
|
||||||
def train(self, xa,la,xb,lb):
|
def train(self, xa,la,xb,lb):
|
||||||
D_loss, = self.D_train ([xa,la,xb,lb])
|
D_loss, = self.D_train ([xa,la,xb,lb])
|
||||||
G_loss, = self.G_train ([xa,la,xb,lb])
|
G_loss, = self.G_train ([xa,la,xb,lb])
|
||||||
|
@ -209,17 +202,17 @@ class FUNIT(object):
|
||||||
def ResBlock(dim):
|
def ResBlock(dim):
|
||||||
def func(input):
|
def func(input):
|
||||||
x = input
|
x = input
|
||||||
x = Conv2D(dim, 3, strides=1, padding='valid')(ZeroPadding2D(1)(x))
|
x = Conv2D(dim, 3, strides=1, padding='same')(x)
|
||||||
x = InstanceNormalization()(x)
|
x = InstanceNormalization()(x)
|
||||||
x = ReLU()(x)
|
x = ReLU()(x)
|
||||||
x = Conv2D(dim, 3, strides=1, padding='valid')(ZeroPadding2D(1)(x))
|
x = Conv2D(dim, 3, strides=1, padding='same')(x)
|
||||||
x = InstanceNormalization()(x)
|
x = InstanceNormalization()(x)
|
||||||
|
|
||||||
return Add()([x,input])
|
return Add()([x,input])
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def func(x):
|
def func(x):
|
||||||
x = Conv2D (nf, kernel_size=7, strides=1, padding='valid')(ZeroPadding2D(3)(x))
|
x = Conv2D (nf, kernel_size=7, strides=1, padding='same')(x)
|
||||||
x = InstanceNormalization()(x)
|
x = InstanceNormalization()(x)
|
||||||
x = ReLU()(x)
|
x = ReLU()(x)
|
||||||
for i in range(downs):
|
for i in range(downs):
|
||||||
|
@ -237,11 +230,11 @@ class FUNIT(object):
|
||||||
exec (nnlib.import_all(), locals(), globals())
|
exec (nnlib.import_all(), locals(), globals())
|
||||||
|
|
||||||
def func(x):
|
def func(x):
|
||||||
x = Conv2D (nf, kernel_size=7, strides=1, padding='valid', activation='relu')(ZeroPadding2D(3)(x))
|
x = Conv2D (nf, kernel_size=7, strides=1, padding='same', activation='relu')(x)
|
||||||
for i in range(downs):
|
for i in range(downs):
|
||||||
x = Conv2D (nf * min ( 4, 2**(i+1) ), kernel_size=4, strides=2, padding='valid', activation='relu')(ZeroPadding2D(1)(x))
|
x = Conv2D (nf * min ( 4, 2**(i+1) ), kernel_size=4, strides=2, padding='valid', activation='relu')(ZeroPadding2D(1)(x))
|
||||||
x = GlobalAveragePooling2D()(x)
|
x = GlobalAveragePooling2D()(x)
|
||||||
x = Dense(nf)(x)
|
x = Dense(latent_dim)(x)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
@ -250,16 +243,14 @@ class FUNIT(object):
|
||||||
def DecoderFlow(ups, n_res_blks=2, mlp_blks=2, subpixel_decoder=False ):
|
def DecoderFlow(ups, n_res_blks=2, mlp_blks=2, subpixel_decoder=False ):
|
||||||
exec (nnlib.import_all(), locals(), globals())
|
exec (nnlib.import_all(), locals(), globals())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def ResBlock(dim):
|
def ResBlock(dim):
|
||||||
def func(input):
|
def func(input):
|
||||||
inp, mlp = input
|
inp, mlp = input
|
||||||
x = inp
|
x = inp
|
||||||
x = Conv2D(dim, 3, strides=1, padding='valid')(ZeroPadding2D(1)(x))
|
x = Conv2D(dim, 3, strides=1, padding='same')(x)
|
||||||
x = FUNITAdain(kernel_initializer='he_normal')([x,mlp])
|
x = FUNITAdain(kernel_initializer='he_normal')([x,mlp])
|
||||||
x = ReLU()(x)
|
x = ReLU()(x)
|
||||||
x = Conv2D(dim, 3, strides=1, padding='valid')(ZeroPadding2D(1)(x))
|
x = Conv2D(dim, 3, strides=1, padding='same')(x)
|
||||||
x = FUNITAdain(kernel_initializer='he_normal')([x,mlp])
|
x = FUNITAdain(kernel_initializer='he_normal')([x,mlp])
|
||||||
return Add()([x,inp])
|
return Add()([x,inp])
|
||||||
return func
|
return func
|
||||||
|
@ -280,16 +271,16 @@ class FUNIT(object):
|
||||||
for i in range(ups):
|
for i in range(ups):
|
||||||
|
|
||||||
if subpixel_decoder:
|
if subpixel_decoder:
|
||||||
x = Conv2D (4* (nf // 2**(i+1)), kernel_size=3, strides=1, padding='valid')(ZeroPadding2D(1)(x))
|
x = Conv2D (4* (nf // 2**(i+1)), kernel_size=3, strides=1, padding='same')(x)
|
||||||
x = SubpixelUpscaler()(x)
|
x = SubpixelUpscaler()(x)
|
||||||
else:
|
else:
|
||||||
x = UpSampling2D()(x)
|
x = UpSampling2D()(x)
|
||||||
x = Conv2D (nf // 2**(i+1), kernel_size=5, strides=1, padding='valid')(ZeroPadding2D(2)(x))
|
x = Conv2D (nf // 2**(i+1), kernel_size=5, strides=1, padding='same')(x)
|
||||||
|
|
||||||
x = InstanceNormalization()(x)
|
x = InstanceNormalization()(x)
|
||||||
x = ReLU()(x)
|
x = ReLU()(x)
|
||||||
|
|
||||||
rgb = Conv2D (3, kernel_size=7, strides=1, padding='valid', activation='tanh')(ZeroPadding2D(3)(x))
|
rgb = Conv2D (3, kernel_size=7, strides=1, padding='same', activation='tanh')(x)
|
||||||
return rgb
|
return rgb
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
64
nnlib/VGGFace.py
Normal file
64
nnlib/VGGFace.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
from nnlib import nnlib
|
||||||
|
|
||||||
|
def VGGFace():
|
||||||
|
exec(nnlib.import_all(), locals(), globals())
|
||||||
|
|
||||||
|
img_input = Input(shape=(224,224,3) )
|
||||||
|
|
||||||
|
# Block 1
|
||||||
|
x = Conv2D(64, (3, 3), activation='relu', padding='same', name='conv1_1')(
|
||||||
|
img_input)
|
||||||
|
x = Conv2D(64, (3, 3), activation='relu', padding='same', name='conv1_2')(x)
|
||||||
|
x = MaxPooling2D((2, 2), strides=(2, 2), name='pool1')(x)
|
||||||
|
|
||||||
|
# Block 2
|
||||||
|
x = Conv2D(128, (3, 3), activation='relu', padding='same', name='conv2_1')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(128, (3, 3), activation='relu', padding='same', name='conv2_2')(
|
||||||
|
x)
|
||||||
|
x = MaxPooling2D((2, 2), strides=(2, 2), name='pool2')(x)
|
||||||
|
|
||||||
|
# Block 3
|
||||||
|
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_1')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_2')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(256, (3, 3), activation='relu', padding='same', name='conv3_3')(
|
||||||
|
x)
|
||||||
|
x = MaxPooling2D((2, 2), strides=(2, 2), name='pool3')(x)
|
||||||
|
|
||||||
|
# Block 4
|
||||||
|
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_1')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_2')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv4_3')(
|
||||||
|
x)
|
||||||
|
x = MaxPooling2D((2, 2), strides=(2, 2), name='pool4')(x)
|
||||||
|
|
||||||
|
# Block 5
|
||||||
|
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_1')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_2')(
|
||||||
|
x)
|
||||||
|
x = Conv2D(512, (3, 3), activation='relu', padding='same', name='conv5_3')(
|
||||||
|
x)
|
||||||
|
x = MaxPooling2D((2, 2), strides=(2, 2), name='pool5')(x)
|
||||||
|
|
||||||
|
|
||||||
|
# Classification block
|
||||||
|
x = Flatten(name='flatten')(x)
|
||||||
|
x = Dense(4096, name='fc6')(x)
|
||||||
|
x = Activation('relu', name='fc6/relu')(x)
|
||||||
|
x = Dense(4096, name='fc7')(x)
|
||||||
|
x = Activation('relu', name='fc7/relu')(x)
|
||||||
|
x = Dense(2622, name='fc8')(x)
|
||||||
|
x = Activation('softmax', name='fc8/softmax')(x)
|
||||||
|
|
||||||
|
model = Model(img_input, x, name='vggface_vgg16')
|
||||||
|
weights_path = keras.utils.data_utils.get_file('rcmalli_vggface_tf_vgg16.h5',
|
||||||
|
'https://github.com/rcmalli/keras-vggface/releases/download/v2.0/rcmalli_vggface_tf_vgg16.h5')
|
||||||
|
|
||||||
|
model.load_weights(weights_path, by_name=True)
|
||||||
|
|
||||||
|
return model
|
|
@ -1,3 +1,4 @@
|
||||||
from .nnlib import nnlib
|
from .nnlib import nnlib
|
||||||
from .FUNIT import FUNIT
|
from .FUNIT import FUNIT
|
||||||
from .TernausNet import TernausNet
|
from .TernausNet import TernausNet
|
||||||
|
from .VGGFace import VGGFace
|
|
@ -63,6 +63,7 @@ UpSampling2D = KL.UpSampling2D
|
||||||
BatchNormalization = KL.BatchNormalization
|
BatchNormalization = KL.BatchNormalization
|
||||||
PixelNormalization = nnlib.PixelNormalization
|
PixelNormalization = nnlib.PixelNormalization
|
||||||
|
|
||||||
|
Activation = KL.Activation
|
||||||
LeakyReLU = KL.LeakyReLU
|
LeakyReLU = KL.LeakyReLU
|
||||||
ELU = KL.ELU
|
ELU = KL.ELU
|
||||||
ReLU = KL.ReLU
|
ReLU = KL.ReLU
|
||||||
|
|
|
@ -53,58 +53,56 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
self.generators_random_seed = generators_random_seed
|
self.generators_random_seed = generators_random_seed
|
||||||
|
|
||||||
samples = SampleLoader.load (self.sample_type, self.samples_path, sort_by_yaw_target_samples_path, person_id_mode=person_id_mode)
|
samples = SampleLoader.load (self.sample_type, self.samples_path, sort_by_yaw_target_samples_path, person_id_mode=person_id_mode)
|
||||||
self.total_samples_count = len(samples)
|
self.samples_len = len(samples)
|
||||||
|
|
||||||
|
if self.samples_len == 0:
|
||||||
|
raise ValueError('No training data provided.')
|
||||||
|
|
||||||
ct_samples = SampleLoader.load (SampleType.FACE, random_ct_samples_path) if random_ct_samples_path is not None else None
|
ct_samples = SampleLoader.load (SampleType.FACE, random_ct_samples_path) if random_ct_samples_path is not None else None
|
||||||
self.random_ct_sample_chance = 100
|
self.random_ct_sample_chance = 100
|
||||||
|
|
||||||
if self.debug:
|
if self.debug:
|
||||||
self.generators_count = 1
|
self.generators_count = 1
|
||||||
self.generators = [iter_utils.ThisThreadGenerator(self.batch_func, (0, samples, ct_samples))]
|
self.generators = [iter_utils.ThisThreadGenerator ( self.batch_func, (0, samples, ct_samples) )]
|
||||||
else:
|
else:
|
||||||
self.generators_count = min(generators_count, len(samples))
|
self.generators_count = min ( generators_count, self.samples_len )
|
||||||
self.generators = [
|
self.generators = [iter_utils.SubprocessGenerator ( self.batch_func, (i, samples[i::self.generators_count], ct_samples ) ) for i in range(self.generators_count) ]
|
||||||
iter_utils.SubprocessGenerator(self.batch_func, (i, samples[i::self.generators_count], ct_samples)) for
|
|
||||||
i in range(self.generators_count)]
|
|
||||||
|
|
||||||
self.generator_counter = -1
|
self.generator_counter = -1
|
||||||
|
|
||||||
#overridable
|
#overridable
|
||||||
def get_total_sample_count(self):
|
def get_total_sample_count(self):
|
||||||
return self.total_samples_count
|
return self.samples_len
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __next__(self):
|
def __next__(self):
|
||||||
self.generator_counter += 1
|
self.generator_counter += 1
|
||||||
generator = self.generators[self.generator_counter % len(self.generators)]
|
generator = self.generators[self.generator_counter % len(self.generators) ]
|
||||||
super().__next__()
|
super().__next__()
|
||||||
return next(generator)
|
return next(generator)
|
||||||
|
|
||||||
def batch_func(self, param):
|
def batch_func(self, param ):
|
||||||
generator_id, samples, ct_samples = param
|
generator_id, samples, ct_samples = param
|
||||||
|
|
||||||
if self.generators_random_seed is not None:
|
if self.generators_random_seed is not None:
|
||||||
np.random.seed(self.generators_random_seed[generator_id])
|
np.random.seed ( self.generators_random_seed[generator_id] )
|
||||||
|
|
||||||
samples_len = len(samples)
|
samples_len = len(samples)
|
||||||
samples_idxs = [*range(samples_len)]
|
samples_idxs = [*range(samples_len)]
|
||||||
|
|
||||||
ct_samples_len = len(ct_samples) if ct_samples is not None else 0
|
ct_samples_len = len(ct_samples) if ct_samples is not None else 0
|
||||||
|
|
||||||
if len(samples_idxs) == 0:
|
|
||||||
raise ValueError('No training data provided.')
|
|
||||||
|
|
||||||
if self.sample_type == SampleType.FACE_YAW_SORTED or self.sample_type == SampleType.FACE_YAW_SORTED_AS_TARGET:
|
if self.sample_type == SampleType.FACE_YAW_SORTED or self.sample_type == SampleType.FACE_YAW_SORTED_AS_TARGET:
|
||||||
if all([samples[idx] == None for idx in samples_idxs]):
|
if all ( [ samples[idx] == None for idx in samples_idxs] ):
|
||||||
raise ValueError('Not enough training data. Gather more faces!')
|
raise ValueError('Not enough training data. Gather more faces!')
|
||||||
|
|
||||||
if self.sample_type == SampleType.FACE:
|
if self.sample_type == SampleType.FACE:
|
||||||
shuffle_idxs = []
|
shuffle_idxs = []
|
||||||
elif self.sample_type == SampleType.FACE_YAW_SORTED or self.sample_type == SampleType.FACE_YAW_SORTED_AS_TARGET:
|
elif self.sample_type == SampleType.FACE_YAW_SORTED or self.sample_type == SampleType.FACE_YAW_SORTED_AS_TARGET:
|
||||||
shuffle_idxs = []
|
shuffle_idxs = []
|
||||||
shuffle_idxs_2D = [[]] * samples_len
|
shuffle_idxs_2D = [[]]*samples_len
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
batches = None
|
batches = None
|
||||||
|
@ -118,7 +116,7 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
np.random.shuffle(shuffle_idxs)
|
np.random.shuffle(shuffle_idxs)
|
||||||
|
|
||||||
idx = shuffle_idxs.pop()
|
idx = shuffle_idxs.pop()
|
||||||
sample = samples[idx]
|
sample = samples[ idx ]
|
||||||
|
|
||||||
elif self.sample_type == SampleType.FACE_YAW_SORTED or self.sample_type == SampleType.FACE_YAW_SORTED_AS_TARGET:
|
elif self.sample_type == SampleType.FACE_YAW_SORTED or self.sample_type == SampleType.FACE_YAW_SORTED_AS_TARGET:
|
||||||
if len(shuffle_idxs) == 0:
|
if len(shuffle_idxs) == 0:
|
||||||
|
@ -128,8 +126,8 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
idx = shuffle_idxs.pop()
|
idx = shuffle_idxs.pop()
|
||||||
if samples[idx] != None:
|
if samples[idx] != None:
|
||||||
if len(shuffle_idxs_2D[idx]) == 0:
|
if len(shuffle_idxs_2D[idx]) == 0:
|
||||||
a = shuffle_idxs_2D[idx] = [*range(len(samples[idx]))]
|
a = shuffle_idxs_2D[idx] = [ *range(len(samples[idx])) ]
|
||||||
np.random.shuffle(a)
|
np.random.shuffle (a)
|
||||||
|
|
||||||
idx2 = shuffle_idxs_2D[idx].pop()
|
idx2 = shuffle_idxs_2D[idx].pop()
|
||||||
sample = samples[idx][idx2]
|
sample = samples[idx][idx2]
|
||||||
|
@ -138,10 +136,10 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
|
|
||||||
if sample is not None:
|
if sample is not None:
|
||||||
try:
|
try:
|
||||||
ct_sample = None
|
ct_sample=None
|
||||||
if ct_samples is not None:
|
if ct_samples is not None:
|
||||||
if np.random.randint(100) < self.random_ct_sample_chance:
|
if np.random.randint(100) < self.random_ct_sample_chance:
|
||||||
ct_sample = ct_samples[np.random.randint(ct_samples_len)]
|
ct_sample=ct_samples[np.random.randint(ct_samples_len)]
|
||||||
|
|
||||||
x = SampleProcessor.process(sample, self.sample_process_options, self.output_sample_types,
|
x = SampleProcessor.process(sample, self.sample_process_options, self.output_sample_types,
|
||||||
self.debug, ct_sample=ct_sample)
|
self.debug, ct_sample=ct_sample)
|
||||||
|
@ -153,7 +151,7 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
raise Exception('SampleProcessor.process returns NOT tuple/list')
|
raise Exception('SampleProcessor.process returns NOT tuple/list')
|
||||||
|
|
||||||
if batches is None:
|
if batches is None:
|
||||||
batches = [[] for _ in range(len(x))]
|
batches = [ [] for _ in range(len(x)) ]
|
||||||
if self.add_sample_idx:
|
if self.add_sample_idx:
|
||||||
batches += [ [] ]
|
batches += [ [] ]
|
||||||
i_sample_idx = len(batches)-1
|
i_sample_idx = len(batches)-1
|
||||||
|
@ -163,7 +161,7 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
i_person_id = len(batches)-1
|
i_person_id = len(batches)-1
|
||||||
|
|
||||||
for i in range(len(x)):
|
for i in range(len(x)):
|
||||||
batches[i].append(x[i])
|
batches[i].append ( x[i] )
|
||||||
|
|
||||||
if self.add_sample_idx:
|
if self.add_sample_idx:
|
||||||
batches[i_sample_idx].append (idx)
|
batches[i_sample_idx].append (idx)
|
||||||
|
@ -172,7 +170,7 @@ class SampleGeneratorFace(SampleGeneratorPingPong):
|
||||||
batches[i_person_id].append ( np.array([sample.person_id]) )
|
batches[i_person_id].append ( np.array([sample.person_id]) )
|
||||||
|
|
||||||
break
|
break
|
||||||
yield [np.array(batch) for batch in batches]
|
yield [ np.array(batch) for batch in batches]
|
||||||
|
|
||||||
def update_batch(self, batch_size):
|
def update_batch(self, batch_size):
|
||||||
self.batch_size = batch_size
|
self.batch_size = batch_size
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue