mirror of
https://github.com/iperov/DeepFaceLab.git
synced 2025-08-22 06:23:20 -07:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
d278a443bb
47 changed files with 2403 additions and 1663 deletions
35
converters/ConvertAvatar.py
Normal file
35
converters/ConvertAvatar.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import cv2
|
||||
import numpy as np
|
||||
|
||||
import imagelib
|
||||
from facelib import FaceType, LandmarksProcessor
|
||||
from utils.cv2_utils import *
|
||||
|
||||
def process_frame_info(frame_info, inp_sh):
|
||||
img_uint8 = cv2_imread (frame_info.filename)
|
||||
img_uint8 = imagelib.normalize_channels (img_uint8, 3)
|
||||
img = img_uint8.astype(np.float32) / 255.0
|
||||
|
||||
img_mat = LandmarksProcessor.get_transform_mat (frame_info.landmarks_list[0], inp_sh[0], face_type=FaceType.FULL_NO_ALIGN)
|
||||
img = cv2.warpAffine( img, img_mat, inp_sh[0:2], flags=cv2.INTER_CUBIC )
|
||||
return img
|
||||
|
||||
def ConvertFaceAvatar (cfg, prev_temporal_frame_infos, frame_info, next_temporal_frame_infos):
|
||||
inp_sh = cfg.predictor_input_shape
|
||||
|
||||
prev_imgs=[]
|
||||
next_imgs=[]
|
||||
for i in range(cfg.temporal_face_count):
|
||||
prev_imgs.append( process_frame_info(prev_temporal_frame_infos[i], inp_sh) )
|
||||
next_imgs.append( process_frame_info(next_temporal_frame_infos[i], inp_sh) )
|
||||
img = process_frame_info(frame_info, inp_sh)
|
||||
|
||||
prd_f = cfg.predictor_func ( prev_imgs, img, next_imgs )
|
||||
|
||||
out_img = np.clip(prd_f, 0.0, 1.0)
|
||||
|
||||
if cfg.add_source_image:
|
||||
out_img = np.concatenate ( [cv2.resize ( img, (prd_f.shape[1], prd_f.shape[0]) ),
|
||||
out_img], axis=1 )
|
||||
|
||||
return (out_img*255).astype(np.uint8)
|
392
converters/ConvertMasked.py
Normal file
392
converters/ConvertMasked.py
Normal file
|
@ -0,0 +1,392 @@
|
|||
import traceback
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
import imagelib
|
||||
from facelib import FaceType, LandmarksProcessor
|
||||
from interact import interact as io
|
||||
from utils.cv2_utils import *
|
||||
|
||||
|
||||
def ConvertMaskedFace (cfg, frame_info, img_bgr_uint8, img_bgr, img_face_landmarks):
|
||||
|
||||
#if debug:
|
||||
# debugs = [img_bgr.copy()]
|
||||
|
||||
img_size = img_bgr.shape[1], img_bgr.shape[0]
|
||||
|
||||
img_face_mask_a = LandmarksProcessor.get_image_hull_mask (img_bgr.shape, img_face_landmarks)
|
||||
|
||||
if cfg.mode == 'original':
|
||||
if cfg.export_mask_alpha:
|
||||
img_bgr = np.concatenate ( [img_bgr, img_face_mask_a], -1 )
|
||||
return img_bgr, img_face_mask_a
|
||||
|
||||
out_img = img_bgr.copy()
|
||||
out_merging_mask = None
|
||||
|
||||
output_size = cfg.predictor_input_shape[0]
|
||||
if cfg.super_resolution:
|
||||
output_size *= 2
|
||||
|
||||
face_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, output_size, face_type=cfg.face_type)
|
||||
face_output_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, output_size, face_type=cfg.face_type, scale= 1.0 + 0.01*cfg.output_face_scale )
|
||||
|
||||
dst_face_bgr = cv2.warpAffine( img_bgr , face_mat, (output_size, output_size), flags=cv2.INTER_CUBIC )
|
||||
dst_face_mask_a_0 = cv2.warpAffine( img_face_mask_a, face_mat, (output_size, output_size), flags=cv2.INTER_CUBIC )
|
||||
|
||||
predictor_input_bgr = cv2.resize (dst_face_bgr, cfg.predictor_input_shape[0:2] )
|
||||
|
||||
if cfg.predictor_masked:
|
||||
prd_face_bgr, prd_face_mask_a_0 = cfg.predictor_func (predictor_input_bgr)
|
||||
|
||||
prd_face_bgr = np.clip (prd_face_bgr, 0, 1.0 )
|
||||
prd_face_mask_a_0 = np.clip (prd_face_mask_a_0, 0.0, 1.0)
|
||||
else:
|
||||
predicted = cfg.predictor_func (predictor_input_bgr)
|
||||
prd_face_bgr = np.clip (predicted, 0, 1.0 )
|
||||
prd_face_mask_a_0 = cv2.resize (dst_face_mask_a_0, cfg.predictor_input_shape[0:2] )
|
||||
|
||||
if cfg.super_resolution:
|
||||
#if debug:
|
||||
# tmp = cv2.resize (prd_face_bgr, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
# debugs += [ np.clip( cv2.warpAffine( tmp, face_output_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
prd_face_bgr = cfg.dcscn_upscale_func(prd_face_bgr)
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
if cfg.predictor_masked:
|
||||
prd_face_mask_a_0 = cv2.resize (prd_face_mask_a_0, (output_size, output_size), cv2.INTER_CUBIC)
|
||||
else:
|
||||
prd_face_mask_a_0 = cv2.resize (dst_face_mask_a_0, (output_size, output_size), cv2.INTER_CUBIC)
|
||||
|
||||
if cfg.mask_mode == 2: #dst
|
||||
prd_face_mask_a_0 = cv2.resize (dst_face_mask_a_0, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
elif cfg.mask_mode >= 3 and cfg.mask_mode <= 7:
|
||||
|
||||
if cfg.mask_mode == 3 or cfg.mask_mode == 5 or cfg.mask_mode == 6:
|
||||
prd_face_fanseg_bgr = cv2.resize (prd_face_bgr, (cfg.fanseg_input_size,)*2 )
|
||||
prd_face_fanseg_mask = cfg.fanseg_extract_func(FaceType.FULL, prd_face_fanseg_bgr)
|
||||
FAN_prd_face_mask_a_0 = cv2.resize ( prd_face_fanseg_mask, (output_size, output_size), cv2.INTER_CUBIC)
|
||||
|
||||
if cfg.mask_mode >= 4 or cfg.mask_mode <= 7:
|
||||
|
||||
full_face_fanseg_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, cfg.fanseg_input_size, face_type=FaceType.FULL)
|
||||
dst_face_fanseg_bgr = cv2.warpAffine(img_bgr, full_face_fanseg_mat, (cfg.fanseg_input_size,)*2, flags=cv2.INTER_CUBIC )
|
||||
dst_face_fanseg_mask = cfg.fanseg_extract_func( FaceType.FULL, dst_face_fanseg_bgr )
|
||||
|
||||
if cfg.face_type == FaceType.FULL:
|
||||
FAN_dst_face_mask_a_0 = cv2.resize (dst_face_fanseg_mask, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
elif cfg.face_type == FaceType.HALF:
|
||||
half_face_fanseg_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, cfg.fanseg_input_size, face_type=FaceType.HALF)
|
||||
|
||||
fanseg_rect_corner_pts = np.array ( [ [0,0], [cfg.fanseg_input_size-1,0], [0,cfg.fanseg_input_size-1] ], dtype=np.float32 )
|
||||
a = LandmarksProcessor.transform_points (fanseg_rect_corner_pts, half_face_fanseg_mat, invert=True )
|
||||
b = LandmarksProcessor.transform_points (a, full_face_fanseg_mat )
|
||||
m = cv2.getAffineTransform(b, fanseg_rect_corner_pts)
|
||||
FAN_dst_face_mask_a_0 = cv2.warpAffine(dst_face_fanseg_mask, m, (cfg.fanseg_input_size,)*2, flags=cv2.INTER_CUBIC )
|
||||
FAN_dst_face_mask_a_0 = cv2.resize (FAN_dst_face_mask_a_0, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
else:
|
||||
raise ValueError ("cfg.face_type unsupported")
|
||||
|
||||
if cfg.mask_mode == 3: #FAN-prd
|
||||
prd_face_mask_a_0 = FAN_prd_face_mask_a_0
|
||||
elif cfg.mask_mode == 4: #FAN-dst
|
||||
prd_face_mask_a_0 = FAN_dst_face_mask_a_0
|
||||
elif cfg.mask_mode == 5:
|
||||
prd_face_mask_a_0 = FAN_prd_face_mask_a_0 * FAN_dst_face_mask_a_0
|
||||
elif cfg.mask_mode == 6:
|
||||
prd_face_mask_a_0 = prd_face_mask_a_0 * FAN_prd_face_mask_a_0 * FAN_dst_face_mask_a_0
|
||||
elif cfg.mask_mode == 7:
|
||||
prd_face_mask_a_0 = prd_face_mask_a_0 * FAN_dst_face_mask_a_0
|
||||
|
||||
prd_face_mask_a_0[ prd_face_mask_a_0 < 0.001 ] = 0.0
|
||||
|
||||
prd_face_mask_a = prd_face_mask_a_0[...,np.newaxis]
|
||||
prd_face_mask_aaa = np.repeat (prd_face_mask_a, (3,), axis=-1)
|
||||
|
||||
img_face_mask_aaa = cv2.warpAffine( prd_face_mask_aaa, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC )
|
||||
img_face_mask_aaa = np.clip (img_face_mask_aaa, 0.0, 1.0)
|
||||
img_face_mask_aaa [ img_face_mask_aaa <= 0.1 ] = 0.0 #get rid of noise
|
||||
|
||||
#if debug:
|
||||
# debugs += [img_face_mask_aaa.copy()]
|
||||
|
||||
if 'raw' in cfg.mode:
|
||||
face_corner_pts = np.array ([ [0,0], [output_size-1,0], [output_size-1,output_size-1], [0,output_size-1] ], dtype=np.float32)
|
||||
square_mask = np.zeros(img_bgr.shape, dtype=np.float32)
|
||||
cv2.fillConvexPoly(square_mask, \
|
||||
LandmarksProcessor.transform_points (face_corner_pts, face_output_mat, invert=True ).astype(np.int), \
|
||||
(1,1,1) )
|
||||
|
||||
if cfg.mode == 'raw-rgb':
|
||||
out_merging_mask = square_mask
|
||||
|
||||
if cfg.mode == 'raw-rgb' or cfg.mode == 'raw-rgb-mask':
|
||||
out_img = cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, out_img, cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT )
|
||||
|
||||
if cfg.mode == 'raw-rgb-mask':
|
||||
out_img = np.concatenate ( [out_img, np.expand_dims (img_face_mask_aaa[:,:,0],-1)], -1 )
|
||||
out_merging_mask = square_mask
|
||||
|
||||
elif cfg.mode == 'raw-mask-only':
|
||||
out_img = img_face_mask_aaa
|
||||
out_merging_mask = img_face_mask_aaa
|
||||
elif cfg.mode == 'raw-predicted-only':
|
||||
out_img = cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT )
|
||||
out_merging_mask = square_mask
|
||||
|
||||
out_img = np.clip (out_img, 0.0, 1.0 )
|
||||
else:
|
||||
#averaging [lenx, leny, maskx, masky] by grayscale gradients of upscaled mask
|
||||
ar = []
|
||||
for i in range(1, 10):
|
||||
maxregion = np.argwhere( img_face_mask_aaa > i / 10.0 )
|
||||
if maxregion.size != 0:
|
||||
miny,minx = maxregion.min(axis=0)[:2]
|
||||
maxy,maxx = maxregion.max(axis=0)[:2]
|
||||
lenx = maxx - minx
|
||||
leny = maxy - miny
|
||||
if min(lenx,leny) >= 4:
|
||||
ar += [ [ lenx, leny] ]
|
||||
|
||||
if len(ar) > 0:
|
||||
lenx, leny = np.mean ( ar, axis=0 )
|
||||
lowest_len = min (lenx, leny)
|
||||
#if debug:
|
||||
# io.log_info ("lenx/leny:(%d/%d) " % (lenx, leny ) )
|
||||
# io.log_info ("lowest_len = %f" % (lowest_len) )
|
||||
|
||||
if cfg.erode_mask_modifier != 0:
|
||||
ero = int( lowest_len * ( 0.126 - lowest_len * 0.00004551365 ) * 0.01*cfg.erode_mask_modifier )
|
||||
#if debug:
|
||||
# io.log_info ("erode_size = %d" % (ero) )
|
||||
if ero > 0:
|
||||
img_face_mask_aaa = cv2.erode(img_face_mask_aaa, cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(ero,ero)), iterations = 1 )
|
||||
elif ero < 0:
|
||||
img_face_mask_aaa = cv2.dilate(img_face_mask_aaa, cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(-ero,-ero)), iterations = 1 )
|
||||
|
||||
if cfg.clip_hborder_mask_per > 0: #clip hborder before blur
|
||||
prd_hborder_rect_mask_a = np.ones ( prd_face_mask_a.shape, dtype=np.float32)
|
||||
prd_border_size = int ( prd_hborder_rect_mask_a.shape[1] * cfg.clip_hborder_mask_per )
|
||||
prd_hborder_rect_mask_a[:,0:prd_border_size,:] = 0
|
||||
prd_hborder_rect_mask_a[:,-prd_border_size:,:] = 0
|
||||
prd_hborder_rect_mask_a[-prd_border_size:,:,:] = 0
|
||||
prd_hborder_rect_mask_a = np.expand_dims(cv2.blur(prd_hborder_rect_mask_a, (prd_border_size, prd_border_size) ),-1)
|
||||
|
||||
img_prd_hborder_rect_mask_a = cv2.warpAffine( prd_hborder_rect_mask_a, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC )
|
||||
img_prd_hborder_rect_mask_a = np.expand_dims (img_prd_hborder_rect_mask_a, -1)
|
||||
img_face_mask_aaa *= img_prd_hborder_rect_mask_a
|
||||
img_face_mask_aaa = np.clip( img_face_mask_aaa, 0, 1.0 )
|
||||
|
||||
#if debug:
|
||||
# debugs += [img_face_mask_aaa.copy()]
|
||||
|
||||
if cfg.blur_mask_modifier > 0:
|
||||
blur = int( lowest_len * 0.10 * 0.01*cfg.blur_mask_modifier )
|
||||
#if debug:
|
||||
# io.log_info ("blur_size = %d" % (blur) )
|
||||
if blur > 0:
|
||||
img_face_mask_aaa = cv2.blur(img_face_mask_aaa, (blur, blur) )
|
||||
|
||||
img_face_mask_aaa = np.clip( img_face_mask_aaa, 0, 1.0 )
|
||||
|
||||
|
||||
#if debug:
|
||||
# debugs += [img_face_mask_aaa.copy()]
|
||||
|
||||
if 'seamless' not in cfg.mode and cfg.color_transfer_mode != 0:
|
||||
if cfg.color_transfer_mode == 1:
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
prd_face_bgr = imagelib.reinhard_color_transfer ( np.clip( (prd_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
np.clip( (dst_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
source_mask=prd_face_mask_a, target_mask=prd_face_mask_a)
|
||||
prd_face_bgr = np.clip( prd_face_bgr.astype(np.float32) / 255.0, 0.0, 1.0)
|
||||
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
|
||||
elif cfg.color_transfer_mode == 2:
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
prd_face_bgr = imagelib.linear_color_transfer (prd_face_bgr, dst_face_bgr)
|
||||
prd_face_bgr = np.clip( prd_face_bgr, 0.0, 1.0)
|
||||
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
if cfg.mode == 'hist-match-bw':
|
||||
prd_face_bgr = cv2.cvtColor(prd_face_bgr, cv2.COLOR_BGR2GRAY)
|
||||
prd_face_bgr = np.repeat( np.expand_dims (prd_face_bgr, -1), (3,), -1 )
|
||||
|
||||
if cfg.mode == 'hist-match' or cfg.mode == 'hist-match-bw':
|
||||
#if debug:
|
||||
# debugs += [ cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ) ]
|
||||
|
||||
hist_mask_a = np.ones ( prd_face_bgr.shape[:2] + (1,) , dtype=np.float32)
|
||||
|
||||
if cfg.masked_hist_match:
|
||||
hist_mask_a *= prd_face_mask_a
|
||||
|
||||
white = (1.0-hist_mask_a)* np.ones ( prd_face_bgr.shape[:2] + (1,) , dtype=np.float32)
|
||||
|
||||
hist_match_1 = prd_face_bgr*hist_mask_a + white
|
||||
hist_match_1[ hist_match_1 > 1.0 ] = 1.0
|
||||
|
||||
hist_match_2 = dst_face_bgr*hist_mask_a + white
|
||||
hist_match_2[ hist_match_1 > 1.0 ] = 1.0
|
||||
|
||||
prd_face_bgr = imagelib.color_hist_match(hist_match_1, hist_match_2, cfg.hist_match_threshold )
|
||||
|
||||
#if cfg.masked_hist_match:
|
||||
# prd_face_bgr -= white
|
||||
|
||||
if cfg.mode == 'hist-match-bw':
|
||||
prd_face_bgr = prd_face_bgr.astype(dtype=np.float32)
|
||||
|
||||
out_img = cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, out_img, cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip(out_img, 0.0, 1.0)
|
||||
|
||||
#if debug:
|
||||
# debugs += [out_img.copy()]
|
||||
|
||||
if cfg.mode == 'overlay':
|
||||
pass
|
||||
|
||||
if 'seamless' in cfg.mode:
|
||||
#mask used for cv2.seamlessClone
|
||||
img_face_seamless_mask_a = None
|
||||
img_face_mask_a = img_face_mask_aaa[...,0:1]
|
||||
for i in range(1,10):
|
||||
a = img_face_mask_a > i / 10.0
|
||||
if len(np.argwhere(a)) == 0:
|
||||
continue
|
||||
img_face_seamless_mask_a = img_face_mask_aaa[...,0:1].copy()
|
||||
img_face_seamless_mask_a[a] = 1.0
|
||||
img_face_seamless_mask_a[img_face_seamless_mask_a <= i / 10.0] = 0.0
|
||||
break
|
||||
|
||||
try:
|
||||
#calc same bounding rect and center point as in cv2.seamlessClone to prevent jittering (not flickering)
|
||||
l,t,w,h = cv2.boundingRect( (img_face_seamless_mask_a*255).astype(np.uint8) )
|
||||
s_maskx, s_masky = int(l+w/2), int(t+h/2)
|
||||
|
||||
out_img = cv2.seamlessClone( (out_img*255).astype(np.uint8), img_bgr_uint8, (img_face_seamless_mask_a*255).astype(np.uint8), (s_maskx,s_masky) , cv2.NORMAL_CLONE )
|
||||
out_img = out_img.astype(dtype=np.float32) / 255.0
|
||||
except Exception as e:
|
||||
#seamlessClone may fail in some cases
|
||||
e_str = traceback.format_exc()
|
||||
|
||||
if 'MemoryError' in e_str:
|
||||
raise Exception("Seamless fail: " + e_str) #reraise MemoryError in order to reprocess this data by other processes
|
||||
else:
|
||||
print ("Seamless fail: " + e_str)
|
||||
|
||||
#if debug:
|
||||
# debugs += [out_img.copy()]
|
||||
|
||||
out_img = img_bgr*(1-img_face_mask_aaa) + (out_img*img_face_mask_aaa)
|
||||
|
||||
if 'seamless' in cfg.mode and cfg.color_transfer_mode != 0:
|
||||
out_face_bgr = cv2.warpAffine( out_img, face_mat, (output_size, output_size) )
|
||||
|
||||
if cfg.color_transfer_mode == 1:
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
face_mask_aaa = cv2.warpAffine( img_face_mask_aaa, face_mat, (output_size, output_size) )
|
||||
|
||||
new_out_face_bgr = imagelib.reinhard_color_transfer ( np.clip( (out_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
np.clip( (dst_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
source_mask=face_mask_aaa, target_mask=face_mask_aaa)
|
||||
new_out_face_bgr = np.clip( new_out_face_bgr.astype(np.float32) / 255.0, 0.0, 1.0)
|
||||
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( new_out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
|
||||
elif cfg.color_transfer_mode == 2:
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
new_out_face_bgr = imagelib.linear_color_transfer (out_face_bgr, dst_face_bgr)
|
||||
new_out_face_bgr = np.clip( new_out_face_bgr, 0.0, 1.0)
|
||||
|
||||
#if debug:
|
||||
# debugs += [ np.clip( cv2.warpAffine( new_out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
new_out = cv2.warpAffine( new_out_face_bgr, face_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip( img_bgr*(1-img_face_mask_aaa) + (new_out*img_face_mask_aaa) , 0, 1.0 )
|
||||
|
||||
if cfg.mode == 'seamless-hist-match':
|
||||
out_face_bgr = cv2.warpAffine( out_img, face_mat, (output_size, output_size) )
|
||||
new_out_face_bgr = imagelib.color_hist_match(out_face_bgr, dst_face_bgr, cfg.hist_match_threshold)
|
||||
new_out = cv2.warpAffine( new_out_face_bgr, face_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip( img_bgr*(1-img_face_mask_aaa) + (new_out*img_face_mask_aaa) , 0, 1.0 )
|
||||
|
||||
|
||||
cfg_mp = cfg.motion_blur_power / 100.0
|
||||
if cfg_mp != 0:
|
||||
k_size = int(frame_info.motion_power*cfg_mp)
|
||||
if k_size >= 1:
|
||||
k_size = np.clip (k_size+1, 2, 50)
|
||||
if cfg.super_resolution:
|
||||
k_size *= 2
|
||||
out_face_bgr = cv2.warpAffine( out_img, face_mat, (output_size, output_size) )
|
||||
new_out_face_bgr = imagelib.LinearMotionBlur (out_face_bgr, k_size , frame_info.motion_deg)
|
||||
new_out = cv2.warpAffine( new_out_face_bgr, face_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_CUBIC, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip( img_bgr*(1-img_face_mask_aaa) + (new_out*img_face_mask_aaa) , 0, 1.0 )
|
||||
|
||||
if cfg.color_degrade_power != 0:
|
||||
#if debug:
|
||||
# debugs += [out_img.copy()]
|
||||
out_img_reduced = imagelib.reduce_colors(out_img, 256)
|
||||
if cfg.color_degrade_power == 100:
|
||||
out_img = out_img_reduced
|
||||
else:
|
||||
alpha = cfg.color_degrade_power / 100.0
|
||||
out_img = (out_img*(1.0-alpha) + out_img_reduced*alpha)
|
||||
|
||||
if cfg.export_mask_alpha:
|
||||
out_img = np.concatenate ( [out_img, img_face_mask_aaa[:,:,0:1]], -1 )
|
||||
out_merging_mask = img_face_mask_aaa
|
||||
|
||||
|
||||
#if debug:
|
||||
# debugs += [out_img.copy()]
|
||||
|
||||
return out_img, out_merging_mask
|
||||
|
||||
|
||||
def ConvertMasked (cfg, frame_info):
|
||||
img_bgr_uint8 = cv2_imread(frame_info.filename)
|
||||
img_bgr_uint8 = imagelib.normalize_channels (img_bgr_uint8, 3)
|
||||
img_bgr = img_bgr_uint8.astype(np.float32) / 255.0
|
||||
|
||||
outs = []
|
||||
for face_num, img_landmarks in enumerate( frame_info.landmarks_list ):
|
||||
out_img, out_img_merging_mask = ConvertMaskedFace (cfg, frame_info, img_bgr_uint8, img_bgr, img_landmarks)
|
||||
outs += [ (out_img, out_img_merging_mask) ]
|
||||
|
||||
#Combining multiple face outputs
|
||||
final_img = None
|
||||
for img, merging_mask in outs:
|
||||
h,w,c = img.shape
|
||||
|
||||
if final_img is None:
|
||||
final_img = img
|
||||
else:
|
||||
merging_mask = merging_mask[...,0:1]
|
||||
if c == 3:
|
||||
final_img = final_img*(1-merging_mask) + img*merging_mask
|
||||
elif c == 4:
|
||||
final_img_bgr = final_img[...,0:3]*(1-merging_mask) + img[...,0:3]*merging_mask
|
||||
final_img_mask = np.clip ( final_img[...,3:4] + img[...,3:4], 0, 1 )
|
||||
final_img = np.concatenate ( [final_img_bgr, final_img_mask], -1 )
|
||||
|
||||
return (final_img*255).astype(np.uint8)
|
|
@ -1,50 +0,0 @@
|
|||
import copy
|
||||
'''
|
||||
You can implement your own Converter, check example ConverterMasked.py
|
||||
'''
|
||||
|
||||
class Converter(object):
|
||||
TYPE_FACE = 0 #calls convert_face
|
||||
TYPE_FACE_AVATAR = 1 #calls convert_face with avatar_operator_face
|
||||
TYPE_IMAGE = 2 #calls convert_image without landmarks
|
||||
TYPE_IMAGE_WITH_LANDMARKS = 3 #calls convert_image with landmarks
|
||||
|
||||
#overridable
|
||||
def __init__(self, predictor_func, type):
|
||||
self.predictor_func = predictor_func
|
||||
self.type = type
|
||||
|
||||
#overridable
|
||||
def on_cli_initialize(self):
|
||||
#cli initialization
|
||||
pass
|
||||
|
||||
#overridable
|
||||
def on_host_tick(self):
|
||||
pass
|
||||
|
||||
#overridable
|
||||
def cli_convert_face (self, img_bgr, img_face_landmarks, debug, avaperator_face_bgr=None, **kwargs):
|
||||
#return float32 image
|
||||
#if debug , return tuple ( images of any size and channels, ...)
|
||||
return image
|
||||
|
||||
#overridable
|
||||
def cli_convert_image (self, img_bgr, img_landmarks, debug):
|
||||
#img_landmarks not None, if input image is png with embedded data
|
||||
#return float32 image
|
||||
#if debug , return tuple ( images of any size and channels, ...)
|
||||
return image
|
||||
|
||||
#overridable
|
||||
def dummy_predict(self):
|
||||
#do dummy predict here
|
||||
pass
|
||||
|
||||
def copy(self):
|
||||
return copy.copy(self)
|
||||
|
||||
def copy_and_set_predictor(self, predictor_func):
|
||||
result = self.copy()
|
||||
result.predictor_func = predictor_func
|
||||
return result
|
|
@ -1,61 +0,0 @@
|
|||
import time
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from facelib import FaceType, LandmarksProcessor
|
||||
from joblib import SubprocessFunctionCaller
|
||||
from utils.pickle_utils import AntiPickler
|
||||
|
||||
from .Converter import Converter
|
||||
|
||||
class ConverterAvatar(Converter):
|
||||
|
||||
#override
|
||||
def __init__(self, predictor_func,
|
||||
predictor_input_size=0):
|
||||
|
||||
super().__init__(predictor_func, Converter.TYPE_FACE_AVATAR)
|
||||
|
||||
self.predictor_input_size = predictor_input_size
|
||||
|
||||
#dummy predict and sleep, tensorflow caching kernels. If remove it, conversion speed will be x2 slower
|
||||
predictor_func ( np.zeros ( (predictor_input_size,predictor_input_size,3), dtype=np.float32 ),
|
||||
np.zeros ( (predictor_input_size,predictor_input_size,3), dtype=np.float32 ),
|
||||
np.zeros ( (predictor_input_size,predictor_input_size,3), dtype=np.float32 ) )
|
||||
time.sleep(2)
|
||||
|
||||
predictor_func_host, predictor_func = SubprocessFunctionCaller.make_pair(predictor_func)
|
||||
self.predictor_func_host = AntiPickler(predictor_func_host)
|
||||
self.predictor_func = predictor_func
|
||||
|
||||
#overridable
|
||||
def on_host_tick(self):
|
||||
self.predictor_func_host.obj.process_messages()
|
||||
|
||||
#override
|
||||
def cli_convert_face (self, f0, f0_lmrk, f1, f1_lmrk, f2, f2_lmrk, debug, **kwargs):
|
||||
if debug:
|
||||
debugs = []
|
||||
|
||||
inp_size = self.predictor_input_size
|
||||
|
||||
f0_mat = LandmarksProcessor.get_transform_mat (f0_lmrk, inp_size, face_type=FaceType.FULL_NO_ALIGN)
|
||||
f1_mat = LandmarksProcessor.get_transform_mat (f1_lmrk, inp_size, face_type=FaceType.FULL_NO_ALIGN)
|
||||
f2_mat = LandmarksProcessor.get_transform_mat (f2_lmrk, inp_size, face_type=FaceType.FULL_NO_ALIGN)
|
||||
|
||||
inp_f0 = cv2.warpAffine( f0, f0_mat, (inp_size, inp_size), flags=cv2.INTER_CUBIC )
|
||||
inp_f1 = cv2.warpAffine( f1, f1_mat, (inp_size, inp_size), flags=cv2.INTER_CUBIC )
|
||||
inp_f2 = cv2.warpAffine( f2, f2_mat, (inp_size, inp_size), flags=cv2.INTER_CUBIC )
|
||||
|
||||
prd_f = self.predictor_func ( inp_f0, inp_f1, inp_f2 )
|
||||
|
||||
out_img = np.clip(prd_f, 0.0, 1.0)
|
||||
|
||||
out_img = np.concatenate ( [cv2.resize ( inp_f1, (prd_f.shape[1], prd_f.shape[0]) ),
|
||||
out_img], axis=1 )
|
||||
|
||||
if debug:
|
||||
debugs += [out_img.copy()]
|
||||
|
||||
return debugs if debug else out_img
|
317
converters/ConverterConfig.py
Normal file
317
converters/ConverterConfig.py
Normal file
|
@ -0,0 +1,317 @@
|
|||
import numpy as np
|
||||
import copy
|
||||
|
||||
from facelib import FaceType
|
||||
from interact import interact as io
|
||||
|
||||
|
||||
class ConverterConfig(object):
|
||||
TYPE_NONE = 0
|
||||
TYPE_MASKED = 1
|
||||
TYPE_FACE_AVATAR = 2
|
||||
####
|
||||
|
||||
TYPE_IMAGE = 3
|
||||
TYPE_IMAGE_WITH_LANDMARKS = 4
|
||||
|
||||
def __init__(self, type=0, predictor_func=None,
|
||||
predictor_input_shape=None):
|
||||
self.type = type
|
||||
self.predictor_func = predictor_func
|
||||
self.predictor_input_shape = predictor_input_shape
|
||||
|
||||
self.dcscn_upscale_func = None
|
||||
self.fanseg_input_size = None
|
||||
self.fanseg_extract_func = None
|
||||
|
||||
def copy(self):
|
||||
return copy.copy(self)
|
||||
|
||||
#overridable
|
||||
def ask_settings(self):
|
||||
pass
|
||||
|
||||
#overridable
|
||||
def __eq__(self, other):
|
||||
#check equality of changeable params
|
||||
|
||||
if isinstance(other, ConverterConfig):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
#overridable
|
||||
def __str__(self):
|
||||
return "ConverterConfig: ."
|
||||
|
||||
class ConverterConfigMasked(ConverterConfig):
|
||||
|
||||
def __init__(self, predictor_func=None,
|
||||
predictor_input_shape=None,
|
||||
predictor_masked=True,
|
||||
face_type=FaceType.FULL,
|
||||
default_mode = 4,
|
||||
base_erode_mask_modifier = 0,
|
||||
base_blur_mask_modifier = 0,
|
||||
default_erode_mask_modifier = 0,
|
||||
default_blur_mask_modifier = 0,
|
||||
clip_hborder_mask_per = 0,
|
||||
):
|
||||
|
||||
super().__init__(type=ConverterConfig.TYPE_MASKED,
|
||||
predictor_func=predictor_func,
|
||||
predictor_input_shape=predictor_input_shape,
|
||||
)
|
||||
if len(predictor_input_shape) != 3:
|
||||
raise ValueError("ConverterConfigMasked: predictor_input_shape must be rank 3.")
|
||||
|
||||
if predictor_input_shape[0] != predictor_input_shape[1]:
|
||||
raise ValueError("ConverterConfigMasked: predictor_input_shape must be a square.")
|
||||
|
||||
self.predictor_masked = predictor_masked
|
||||
self.face_type = face_type
|
||||
if self.face_type not in [FaceType.FULL, FaceType.HALF]:
|
||||
raise ValueError("ConverterConfigMasked supports only full or half face masks.")
|
||||
|
||||
self.default_mode = default_mode
|
||||
self.base_erode_mask_modifier = base_erode_mask_modifier
|
||||
self.base_blur_mask_modifier = base_blur_mask_modifier
|
||||
self.default_erode_mask_modifier = default_erode_mask_modifier
|
||||
self.default_blur_mask_modifier = default_blur_mask_modifier
|
||||
self.clip_hborder_mask_per = clip_hborder_mask_per
|
||||
|
||||
#default changeable params
|
||||
|
||||
|
||||
self.mode = 'overlay'
|
||||
self.masked_hist_match = True
|
||||
self.hist_match_threshold = 238
|
||||
self.mask_mode = 1
|
||||
self.erode_mask_modifier = 0
|
||||
self.blur_mask_modifier = 0
|
||||
self.motion_blur_power = 0
|
||||
self.output_face_scale = 0
|
||||
self.color_transfer_mode = 0
|
||||
self.super_resolution = False
|
||||
self.color_degrade_power = 0
|
||||
self.export_mask_alpha = False
|
||||
|
||||
self.mode_dict = {0:'original',
|
||||
1:'overlay',
|
||||
2:'hist-match',
|
||||
3:'hist-match-bw',
|
||||
4:'seamless',
|
||||
5:'seamless-hist-match',
|
||||
6:'raw-rgb',
|
||||
7:'raw-rgb-mask',
|
||||
8:'raw-mask-only',
|
||||
9:'raw-predicted-only'}
|
||||
|
||||
self.full_face_mask_mode_dict = {1:'learned',
|
||||
2:'dst',
|
||||
3:'FAN-prd',
|
||||
4:'FAN-dst',
|
||||
5:'FAN-prd*FAN-dst',
|
||||
6:'learned*FAN-prd*FAN-dst'}
|
||||
|
||||
self.half_face_mask_mode_dict = {1:'learned',
|
||||
2:'dst',
|
||||
4:'FAN-dst',
|
||||
7:'learned*FAN-dst'}
|
||||
|
||||
self.ctm_dict = { 0: "None", 1:"rct", 2:"lct" }
|
||||
self.ctm_str_dict = {None:0, "rct":1, "lct": 2 }
|
||||
|
||||
def copy(self):
|
||||
return copy.copy(self)
|
||||
|
||||
def set_mode (self, mode):
|
||||
self.mode = self.mode_dict.get (mode, self.mode_dict[self.default_mode] )
|
||||
|
||||
def toggle_masked_hist_match(self):
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw':
|
||||
self.masked_hist_match = not self.masked_hist_match
|
||||
|
||||
def add_hist_match_threshold(self, diff):
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw' or self.mode == 'seamless-hist-match':
|
||||
self.hist_match_threshold = np.clip ( self.hist_match_threshold+diff , 0, 255)
|
||||
|
||||
def toggle_mask_mode(self):
|
||||
if self.face_type == FaceType.FULL:
|
||||
a = list( self.full_face_mask_mode_dict.keys() )
|
||||
else:
|
||||
a = list( self.half_face_mask_mode_dict.keys() )
|
||||
self.mask_mode = a[ (a.index(self.mask_mode)+1) % len(a) ]
|
||||
|
||||
def add_erode_mask_modifier(self, diff):
|
||||
self.erode_mask_modifier = np.clip ( self.erode_mask_modifier+diff , -200, 200)
|
||||
|
||||
def add_blur_mask_modifier(self, diff):
|
||||
self.blur_mask_modifier = np.clip ( self.blur_mask_modifier+diff , -200, 200)
|
||||
|
||||
def add_motion_blur_power(self, diff):
|
||||
self.motion_blur_power = np.clip ( self.motion_blur_power+diff, 0, 100)
|
||||
|
||||
def add_output_face_scale(self, diff):
|
||||
self.output_face_scale = np.clip ( self.output_face_scale+diff , -50, 50)
|
||||
|
||||
def toggle_color_transfer_mode(self):
|
||||
self.color_transfer_mode = (self.color_transfer_mode+1) % 3
|
||||
|
||||
def toggle_super_resolution(self):
|
||||
self.super_resolution = not self.super_resolution
|
||||
|
||||
def add_color_degrade_power(self, diff):
|
||||
self.color_degrade_power = np.clip ( self.color_degrade_power+diff , 0, 100)
|
||||
|
||||
def toggle_export_mask_alpha(self):
|
||||
self.export_mask_alpha = not self.export_mask_alpha
|
||||
|
||||
def ask_settings(self):
|
||||
|
||||
s = """Choose mode: \n"""
|
||||
for key in self.mode_dict.keys():
|
||||
s += f"""({key}) {self.mode_dict[key]}\n"""
|
||||
s += f"""Default: {self.default_mode} : """
|
||||
|
||||
mode = io.input_int (s, self.default_mode)
|
||||
|
||||
self.mode = self.mode_dict.get (mode, self.mode_dict[self.default_mode] )
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw':
|
||||
self.masked_hist_match = io.input_bool("Masked hist match? (y/n skip:y) : ", True)
|
||||
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw' or self.mode == 'seamless-hist-match':
|
||||
self.hist_match_threshold = np.clip ( io.input_int("Hist match threshold [0..255] (skip:255) : ", 255), 0, 255)
|
||||
|
||||
if self.face_type == FaceType.FULL:
|
||||
s = """Choose mask mode: \n"""
|
||||
for key in self.full_face_mask_mode_dict.keys():
|
||||
s += f"""({key}) {self.full_face_mask_mode_dict[key]}\n"""
|
||||
s += f"""?:help Default: 1 : """
|
||||
|
||||
self.mask_mode = io.input_int (s, 1, valid_list=self.full_face_mask_mode_dict.keys(), help_message="If you learned the mask, then option 1 should be choosed. 'dst' mask is raw shaky mask from dst aligned images. 'FAN-prd' - using super smooth mask by pretrained FAN-model from predicted face. 'FAN-dst' - using super smooth mask by pretrained FAN-model from dst face. 'FAN-prd*FAN-dst' or 'learned*FAN-prd*FAN-dst' - using multiplied masks.")
|
||||
else:
|
||||
s = """Choose mask mode: \n"""
|
||||
for key in self.half_face_mask_mode_dict.keys():
|
||||
s += f"""({key}) {self.half_face_mask_mode_dict[key]}\n"""
|
||||
s += f"""?:help , Default: 1 : """
|
||||
self.mask_mode = io.input_int (s, 1, valid_list=self.half_face_mask_mode_dict.keys(), help_message="If you learned the mask, then option 1 should be choosed. 'dst' mask is raw shaky mask from dst aligned images.")
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
self.erode_mask_modifier = self.base_erode_mask_modifier + np.clip ( io.input_int ("Choose erode mask modifier [-200..200] (skip:%d) : " % (self.default_erode_mask_modifier), self.default_erode_mask_modifier), -200, 200)
|
||||
self.blur_mask_modifier = self.base_blur_mask_modifier + np.clip ( io.input_int ("Choose blur mask modifier [-200..200] (skip:%d) : " % (self.default_blur_mask_modifier), self.default_blur_mask_modifier), -200, 200)
|
||||
self.motion_blur_power = np.clip ( io.input_int ("Choose motion blur power [0..100] (skip:%d) : " % (0), 0), 0, 100)
|
||||
|
||||
self.output_face_scale = np.clip (io.input_int ("Choose output face scale modifier [-50..50] (skip:0) : ", 0), -50, 50)
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
self.color_transfer_mode = io.input_str ("Apply color transfer to predicted face? Choose mode ( rct/lct skip:None ) : ", None, ['rct','lct'])
|
||||
self.color_transfer_mode = self.ctm_str_dict[self.color_transfer_mode]
|
||||
|
||||
self.super_resolution = io.input_bool("Apply super resolution? (y/n ?:help skip:n) : ", False, help_message="Enhance details by applying DCSCN network.")
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
self.color_degrade_power = np.clip ( io.input_int ("Degrade color power of final image [0..100] (skip:0) : ", 0), 0, 100)
|
||||
self.export_mask_alpha = io.input_bool("Export png with alpha channel of the mask? (y/n skip:n) : ", False)
|
||||
|
||||
io.log_info ("")
|
||||
|
||||
def __eq__(self, other):
|
||||
#check equality of changeable params
|
||||
|
||||
if isinstance(other, ConverterConfigMasked):
|
||||
return self.mode == other.mode and \
|
||||
self.masked_hist_match == other.masked_hist_match and \
|
||||
self.hist_match_threshold == other.hist_match_threshold and \
|
||||
self.mask_mode == other.mask_mode and \
|
||||
self.erode_mask_modifier == other.erode_mask_modifier and \
|
||||
self.blur_mask_modifier == other.blur_mask_modifier and \
|
||||
self.motion_blur_power == other.motion_blur_power and \
|
||||
self.output_face_scale == other.output_face_scale and \
|
||||
self.color_transfer_mode == other.color_transfer_mode and \
|
||||
self.super_resolution == other.super_resolution and \
|
||||
self.color_degrade_power == other.color_degrade_power and \
|
||||
self.export_mask_alpha == other.export_mask_alpha
|
||||
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
r = (
|
||||
"""ConverterConfig:\n"""
|
||||
f"""Mode: {self.mode}\n"""
|
||||
)
|
||||
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw':
|
||||
r += f"""masked_hist_match: {self.masked_hist_match}\n"""
|
||||
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw' or self.mode == 'seamless-hist-match':
|
||||
r += f"""hist_match_threshold: {self.hist_match_threshold}\n"""
|
||||
|
||||
if self.face_type == FaceType.FULL:
|
||||
r += f"""mask_mode: { self.full_face_mask_mode_dict[self.mask_mode] }\n"""
|
||||
else:
|
||||
r += f"""mask_mode: { self.half_face_mask_mode_dict[self.mask_mode] }\n"""
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
r += (f"""erode_mask_modifier: {self.erode_mask_modifier}\n"""
|
||||
f"""blur_mask_modifier: {self.blur_mask_modifier}\n"""
|
||||
f"""motion_blur_power: {self.motion_blur_power}\n""")
|
||||
|
||||
r += f"""output_face_scale: {self.output_face_scale}\n"""
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
r += f"""color_transfer_mode: { self.ctm_dict[self.color_transfer_mode]}\n"""
|
||||
|
||||
r += f"""super_resolution: {self.super_resolution}\n"""
|
||||
|
||||
if 'raw' not in self.mode:
|
||||
r += (f"""color_degrade_power: {self.color_degrade_power}\n"""
|
||||
f"""export_mask_alpha: {self.export_mask_alpha}\n""")
|
||||
|
||||
r += "================"
|
||||
|
||||
return r
|
||||
|
||||
|
||||
class ConverterConfigFaceAvatar(ConverterConfig):
|
||||
|
||||
def __init__(self, predictor_func=None,
|
||||
predictor_input_shape=None,
|
||||
temporal_face_count=0
|
||||
):
|
||||
super().__init__(type=ConverterConfig.TYPE_FACE_AVATAR,
|
||||
predictor_func=predictor_func,
|
||||
predictor_input_shape=predictor_input_shape
|
||||
)
|
||||
self.temporal_face_count = temporal_face_count
|
||||
|
||||
#changeable params
|
||||
self.add_source_image = False
|
||||
|
||||
def copy(self):
|
||||
return copy.copy(self)
|
||||
|
||||
#override
|
||||
def ask_settings(self):
|
||||
self.add_source_image = io.input_bool("Add source image? (y/n ?:help skip:n) : ", False, help_message="Add source image for comparison.")
|
||||
|
||||
def toggle_add_source_image(self):
|
||||
self.add_source_image = not self.add_source_image
|
||||
|
||||
#override
|
||||
def __eq__(self, other):
|
||||
#check equality of changeable params
|
||||
|
||||
if isinstance(other, ConverterConfigFaceAvatar):
|
||||
return self.add_source_image == other.add_source_image
|
||||
|
||||
return False
|
||||
|
||||
#override
|
||||
def __str__(self):
|
||||
return ("ConverterConfig: \n"
|
||||
f"add_source_image : {self.add_source_image}\n"
|
||||
"================"
|
||||
)
|
|
@ -1,50 +0,0 @@
|
|||
import time
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from facelib import FaceType, LandmarksProcessor
|
||||
from joblib import SubprocessFunctionCaller
|
||||
from utils.pickle_utils import AntiPickler
|
||||
|
||||
from .Converter import Converter
|
||||
|
||||
class ConverterImage(Converter):
|
||||
|
||||
#override
|
||||
def __init__(self, predictor_func,
|
||||
predictor_input_size=0):
|
||||
|
||||
super().__init__(predictor_func, Converter.TYPE_IMAGE)
|
||||
|
||||
self.predictor_input_size = predictor_input_size
|
||||
|
||||
#dummy predict and sleep, tensorflow caching kernels. If remove it, conversion speed will be x2 slower
|
||||
predictor_func ( np.zeros ( (predictor_input_size,predictor_input_size,3), dtype=np.float32 ) )
|
||||
time.sleep(2)
|
||||
|
||||
predictor_func_host, predictor_func = SubprocessFunctionCaller.make_pair(predictor_func)
|
||||
self.predictor_func_host = AntiPickler(predictor_func_host)
|
||||
self.predictor_func = predictor_func
|
||||
|
||||
#overridable
|
||||
def on_host_tick(self):
|
||||
self.predictor_func_host.obj.process_messages()
|
||||
|
||||
#override
|
||||
def cli_convert_image (self, img_bgr, img_landmarks, debug):
|
||||
img_size = img_bgr.shape[1], img_bgr.shape[0]
|
||||
|
||||
predictor_input_bgr = cv2.resize ( img_bgr, (self.predictor_input_size, self.predictor_input_size), cv2.INTER_LANCZOS4 )
|
||||
|
||||
if debug:
|
||||
debugs = [predictor_input_bgr]
|
||||
|
||||
output = self.predictor_func ( predictor_input_bgr )
|
||||
|
||||
if debug:
|
||||
return (predictor_input_bgr,output,)
|
||||
if debug:
|
||||
debugs += [out_img.copy()]
|
||||
|
||||
return debugs if debug else output
|
|
@ -1,436 +0,0 @@
|
|||
import time
|
||||
import traceback
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
import imagelib
|
||||
from facelib import FaceType, FANSegmentator, LandmarksProcessor
|
||||
from interact import interact as io
|
||||
from joblib import SubprocessFunctionCaller
|
||||
from utils.pickle_utils import AntiPickler
|
||||
|
||||
from .Converter import Converter
|
||||
|
||||
|
||||
'''
|
||||
default_mode = {1:'overlay',
|
||||
2:'hist-match',
|
||||
3:'hist-match-bw',
|
||||
4:'seamless',
|
||||
5:'seamless-hist-match',
|
||||
6:'raw'}
|
||||
'''
|
||||
class ConverterMasked(Converter):
|
||||
|
||||
#override
|
||||
def __init__(self, predictor_func,
|
||||
predictor_input_size=0,
|
||||
predictor_masked=True,
|
||||
face_type=FaceType.FULL,
|
||||
default_mode = 4,
|
||||
base_erode_mask_modifier = 0,
|
||||
base_blur_mask_modifier = 0,
|
||||
default_erode_mask_modifier = 0,
|
||||
default_blur_mask_modifier = 0,
|
||||
clip_hborder_mask_per = 0,
|
||||
force_mask_mode=-1):
|
||||
|
||||
super().__init__(predictor_func, Converter.TYPE_FACE)
|
||||
|
||||
#dummy predict and sleep, tensorflow caching kernels. If remove it, conversion speed will be x2 slower
|
||||
predictor_func ( np.zeros ( (predictor_input_size,predictor_input_size,3), dtype=np.float32 ) )
|
||||
time.sleep(2)
|
||||
|
||||
predictor_func_host, predictor_func = SubprocessFunctionCaller.make_pair(predictor_func)
|
||||
self.predictor_func_host = AntiPickler(predictor_func_host)
|
||||
self.predictor_func = predictor_func
|
||||
|
||||
self.predictor_masked = predictor_masked
|
||||
self.predictor_input_size = predictor_input_size
|
||||
self.face_type = face_type
|
||||
self.clip_hborder_mask_per = clip_hborder_mask_per
|
||||
|
||||
mode = io.input_int ("Choose mode: (1) overlay, (2) hist match, (3) hist match bw, (4) seamless, (5) raw. Default - %d : " % (default_mode) , default_mode)
|
||||
|
||||
mode_dict = {1:'overlay',
|
||||
2:'hist-match',
|
||||
3:'hist-match-bw',
|
||||
4:'seamless',
|
||||
5:'raw'}
|
||||
|
||||
self.mode = mode_dict.get (mode, mode_dict[default_mode] )
|
||||
|
||||
if self.mode == 'raw':
|
||||
mode = io.input_int ("Choose raw mode: (1) rgb, (2) rgb+mask (default), (3) mask only, (4) predicted only : ", 2)
|
||||
self.raw_mode = {1:'rgb',
|
||||
2:'rgb-mask',
|
||||
3:'mask-only',
|
||||
4:'predicted-only'}.get (mode, 'rgb-mask')
|
||||
|
||||
if self.mode != 'raw':
|
||||
|
||||
if self.mode == 'seamless':
|
||||
if io.input_bool("Seamless hist match? (y/n skip:n) : ", False):
|
||||
self.mode = 'seamless-hist-match'
|
||||
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw':
|
||||
self.masked_hist_match = io.input_bool("Masked hist match? (y/n skip:y) : ", True)
|
||||
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw' or self.mode == 'seamless-hist-match':
|
||||
self.hist_match_threshold = np.clip ( io.input_int("Hist match threshold [0..255] (skip:255) : ", 255), 0, 255)
|
||||
|
||||
if force_mask_mode != -1:
|
||||
self.mask_mode = force_mask_mode
|
||||
else:
|
||||
if face_type == FaceType.FULL:
|
||||
self.mask_mode = np.clip ( io.input_int ("Mask mode: (1) learned, (2) dst, (3) FAN-prd, (4) FAN-dst , (5) FAN-prd*FAN-dst (6) learned*FAN-prd*FAN-dst (?) help. Default - %d : " % (1) , 1, help_message="If you learned mask, then option 1 should be choosed. 'dst' mask is raw shaky mask from dst aligned images. 'FAN-prd' - using super smooth mask by pretrained FAN-model from predicted face. 'FAN-dst' - using super smooth mask by pretrained FAN-model from dst face. 'FAN-prd*FAN-dst' or 'learned*FAN-prd*FAN-dst' - using multiplied masks."), 1, 6 )
|
||||
else:
|
||||
self.mask_mode = np.clip ( io.input_int ("Mask mode: (1) learned, (2) dst . Default - %d : " % (1) , 1), 1, 2 )
|
||||
|
||||
if self.mask_mode >= 3 and self.mask_mode <= 6:
|
||||
self.fan_seg = None
|
||||
|
||||
if self.mode != 'raw':
|
||||
self.erode_mask_modifier = base_erode_mask_modifier + np.clip ( io.input_int ("Choose erode mask modifier [-200..200] (skip:%d) : " % (default_erode_mask_modifier), default_erode_mask_modifier), -200, 200)
|
||||
self.blur_mask_modifier = base_blur_mask_modifier + np.clip ( io.input_int ("Choose blur mask modifier [-200..200] (skip:%d) : " % (default_blur_mask_modifier), default_blur_mask_modifier), -200, 200)
|
||||
|
||||
self.output_face_scale = np.clip ( 1.0 + io.input_int ("Choose output face scale modifier [-50..50] (skip:0) : ", 0)*0.01, 0.5, 1.5)
|
||||
|
||||
if self.mode != 'raw':
|
||||
self.color_transfer_mode = io.input_str ("Apply color transfer to predicted face? Choose mode ( rct/lct skip:None ) : ", None, ['rct','lct'])
|
||||
|
||||
self.super_resolution = io.input_bool("Apply super resolution? (y/n ?:help skip:n) : ", False, help_message="Enhance details by applying DCSCN network.")
|
||||
|
||||
if self.mode != 'raw':
|
||||
self.final_image_color_degrade_power = np.clip ( io.input_int ("Degrade color power of final image [0..100] (skip:0) : ", 0), 0, 100)
|
||||
self.alpha = io.input_bool("Export png with alpha channel? (y/n skip:n) : ", False)
|
||||
|
||||
io.log_info ("")
|
||||
|
||||
if self.super_resolution:
|
||||
host_proc, dc_upscale = SubprocessFunctionCaller.make_pair( imagelib.DCSCN().upscale )
|
||||
self.dc_host = AntiPickler(host_proc)
|
||||
self.dc_upscale = dc_upscale
|
||||
else:
|
||||
self.dc_host = None
|
||||
|
||||
#overridable
|
||||
def on_host_tick(self):
|
||||
self.predictor_func_host.obj.process_messages()
|
||||
|
||||
if self.dc_host is not None:
|
||||
self.dc_host.obj.process_messages()
|
||||
|
||||
#overridable
|
||||
def on_cli_initialize(self):
|
||||
if (self.mask_mode >= 3 and self.mask_mode <= 6) and self.fan_seg == None:
|
||||
self.fan_seg = FANSegmentator(256, FaceType.toString( self.face_type ) )
|
||||
|
||||
#override
|
||||
def cli_convert_face (self, img_bgr, img_face_landmarks, debug, **kwargs):
|
||||
if debug:
|
||||
debugs = [img_bgr.copy()]
|
||||
|
||||
img_size = img_bgr.shape[1], img_bgr.shape[0]
|
||||
|
||||
img_face_mask_a = LandmarksProcessor.get_image_hull_mask (img_bgr.shape, img_face_landmarks)
|
||||
|
||||
output_size = self.predictor_input_size
|
||||
if self.super_resolution:
|
||||
output_size *= 2
|
||||
|
||||
face_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, output_size, face_type=self.face_type)
|
||||
face_output_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, output_size, face_type=self.face_type, scale=self.output_face_scale)
|
||||
|
||||
dst_face_bgr = cv2.warpAffine( img_bgr , face_mat, (output_size, output_size), flags=cv2.INTER_LANCZOS4 )
|
||||
dst_face_mask_a_0 = cv2.warpAffine( img_face_mask_a, face_mat, (output_size, output_size), flags=cv2.INTER_LANCZOS4 )
|
||||
|
||||
predictor_input_bgr = cv2.resize (dst_face_bgr, (self.predictor_input_size,self.predictor_input_size))
|
||||
|
||||
if self.predictor_masked:
|
||||
prd_face_bgr, prd_face_mask_a_0 = self.predictor_func (predictor_input_bgr)
|
||||
|
||||
prd_face_bgr = np.clip (prd_face_bgr, 0, 1.0 )
|
||||
prd_face_mask_a_0 = np.clip (prd_face_mask_a_0, 0.0, 1.0)
|
||||
else:
|
||||
predicted = self.predictor_func (predictor_input_bgr)
|
||||
prd_face_bgr = np.clip (predicted, 0, 1.0 )
|
||||
prd_face_mask_a_0 = cv2.resize (dst_face_mask_a_0, (self.predictor_input_size,self.predictor_input_size))
|
||||
|
||||
if self.super_resolution:
|
||||
if debug:
|
||||
tmp = cv2.resize (prd_face_bgr, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
debugs += [ np.clip( cv2.warpAffine( tmp, face_output_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
prd_face_bgr = self.dc_upscale(prd_face_bgr)
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
if self.predictor_masked:
|
||||
prd_face_mask_a_0 = cv2.resize (prd_face_mask_a_0, (output_size, output_size), cv2.INTER_CUBIC)
|
||||
else:
|
||||
prd_face_mask_a_0 = cv2.resize (dst_face_mask_a_0, (output_size, output_size), cv2.INTER_CUBIC)
|
||||
|
||||
if self.mask_mode == 2: #dst
|
||||
prd_face_mask_a_0 = cv2.resize (dst_face_mask_a_0, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
elif self.mask_mode >= 3 and self.mask_mode <= 6:
|
||||
|
||||
if self.mask_mode == 3 or self.mask_mode == 5 or self.mask_mode == 6:
|
||||
prd_face_bgr_256 = cv2.resize (prd_face_bgr, (256,256) )
|
||||
prd_face_bgr_256_mask = self.fan_seg.extract( prd_face_bgr_256 )
|
||||
FAN_prd_face_mask_a_0 = cv2.resize (prd_face_bgr_256_mask, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
|
||||
if self.mask_mode == 4 or self.mask_mode == 5 or self.mask_mode == 6:
|
||||
face_256_mat = LandmarksProcessor.get_transform_mat (img_face_landmarks, 256, face_type=FaceType.FULL)
|
||||
dst_face_256_bgr = cv2.warpAffine(img_bgr, face_256_mat, (256, 256), flags=cv2.INTER_LANCZOS4 )
|
||||
dst_face_256_mask = self.fan_seg.extract( dst_face_256_bgr )
|
||||
FAN_dst_face_mask_a_0 = cv2.resize (dst_face_256_mask, (output_size,output_size), cv2.INTER_CUBIC)
|
||||
|
||||
if self.mask_mode == 3: #FAN-prd
|
||||
prd_face_mask_a_0 = FAN_prd_face_mask_a_0
|
||||
elif self.mask_mode == 4: #FAN-dst
|
||||
prd_face_mask_a_0 = FAN_dst_face_mask_a_0
|
||||
elif self.mask_mode == 5:
|
||||
prd_face_mask_a_0 = FAN_prd_face_mask_a_0 * FAN_dst_face_mask_a_0
|
||||
elif self.mask_mode == 6:
|
||||
prd_face_mask_a_0 = prd_face_mask_a_0 * FAN_prd_face_mask_a_0 * FAN_dst_face_mask_a_0
|
||||
|
||||
prd_face_mask_a_0[ prd_face_mask_a_0 < 0.001 ] = 0.0
|
||||
|
||||
prd_face_mask_a = prd_face_mask_a_0[...,np.newaxis]
|
||||
prd_face_mask_aaa = np.repeat (prd_face_mask_a, (3,), axis=-1)
|
||||
|
||||
img_face_mask_aaa = cv2.warpAffine( prd_face_mask_aaa, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4 )
|
||||
img_face_mask_aaa = np.clip (img_face_mask_aaa, 0.0, 1.0)
|
||||
img_face_mask_aaa [ img_face_mask_aaa <= 0.1 ] = 0.0 #get rid of noise
|
||||
|
||||
if debug:
|
||||
debugs += [img_face_mask_aaa.copy()]
|
||||
|
||||
|
||||
out_img = img_bgr.copy()
|
||||
|
||||
if self.mode == 'raw':
|
||||
if self.raw_mode == 'rgb' or self.raw_mode == 'rgb-mask':
|
||||
out_img = cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, out_img, cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT )
|
||||
|
||||
if self.raw_mode == 'rgb-mask':
|
||||
out_img = np.concatenate ( [out_img, np.expand_dims (img_face_mask_aaa[:,:,0],-1)], -1 )
|
||||
|
||||
if self.raw_mode == 'mask-only':
|
||||
out_img = img_face_mask_aaa
|
||||
|
||||
if self.raw_mode == 'predicted-only':
|
||||
out_img = cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(out_img.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT )
|
||||
|
||||
else:
|
||||
#averaging [lenx, leny, maskx, masky] by grayscale gradients of upscaled mask
|
||||
ar = []
|
||||
for i in range(1, 10):
|
||||
maxregion = np.argwhere( img_face_mask_aaa > i / 10.0 )
|
||||
if maxregion.size != 0:
|
||||
miny,minx = maxregion.min(axis=0)[:2]
|
||||
maxy,maxx = maxregion.max(axis=0)[:2]
|
||||
lenx = maxx - minx
|
||||
leny = maxy - miny
|
||||
if min(lenx,leny) >= 4:
|
||||
ar += [ [ lenx, leny] ]
|
||||
|
||||
if len(ar) > 0:
|
||||
lenx, leny = np.mean ( ar, axis=0 )
|
||||
lowest_len = min (lenx, leny)
|
||||
if debug:
|
||||
io.log_info ("lenx/leny:(%d/%d) " % (lenx, leny ) )
|
||||
io.log_info ("lowest_len = %f" % (lowest_len) )
|
||||
|
||||
if self.erode_mask_modifier != 0:
|
||||
ero = int( lowest_len * ( 0.126 - lowest_len * 0.00004551365 ) * 0.01*self.erode_mask_modifier )
|
||||
if debug:
|
||||
io.log_info ("erode_size = %d" % (ero) )
|
||||
if ero > 0:
|
||||
img_face_mask_aaa = cv2.erode(img_face_mask_aaa, cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(ero,ero)), iterations = 1 )
|
||||
elif ero < 0:
|
||||
img_face_mask_aaa = cv2.dilate(img_face_mask_aaa, cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(-ero,-ero)), iterations = 1 )
|
||||
|
||||
img_mask_blurry_aaa = img_face_mask_aaa
|
||||
|
||||
if self.clip_hborder_mask_per > 0: #clip hborder before blur
|
||||
prd_hborder_rect_mask_a = np.ones ( prd_face_mask_a.shape, dtype=np.float32)
|
||||
prd_border_size = int ( prd_hborder_rect_mask_a.shape[1] * self.clip_hborder_mask_per )
|
||||
prd_hborder_rect_mask_a[:,0:prd_border_size,:] = 0
|
||||
prd_hborder_rect_mask_a[:,-prd_border_size:,:] = 0
|
||||
prd_hborder_rect_mask_a[-prd_border_size:,:,:] = 0
|
||||
prd_hborder_rect_mask_a = np.expand_dims(cv2.blur(prd_hborder_rect_mask_a, (prd_border_size, prd_border_size) ),-1)
|
||||
|
||||
img_prd_hborder_rect_mask_a = cv2.warpAffine( prd_hborder_rect_mask_a, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4 )
|
||||
img_prd_hborder_rect_mask_a = np.expand_dims (img_prd_hborder_rect_mask_a, -1)
|
||||
img_mask_blurry_aaa *= img_prd_hborder_rect_mask_a
|
||||
img_mask_blurry_aaa = np.clip( img_mask_blurry_aaa, 0, 1.0 )
|
||||
|
||||
if debug:
|
||||
debugs += [img_mask_blurry_aaa.copy()]
|
||||
|
||||
if self.blur_mask_modifier > 0:
|
||||
blur = int( lowest_len * 0.10 * 0.01*self.blur_mask_modifier )
|
||||
if debug:
|
||||
io.log_info ("blur_size = %d" % (blur) )
|
||||
if blur > 0:
|
||||
img_mask_blurry_aaa = cv2.blur(img_mask_blurry_aaa, (blur, blur) )
|
||||
|
||||
img_mask_blurry_aaa = np.clip( img_mask_blurry_aaa, 0, 1.0 )
|
||||
face_mask_blurry_aaa = cv2.warpAffine( img_mask_blurry_aaa, face_mat, (output_size, output_size) )
|
||||
|
||||
if debug:
|
||||
debugs += [img_mask_blurry_aaa.copy()]
|
||||
|
||||
if 'seamless' not in self.mode and self.color_transfer_mode is not None:
|
||||
if self.color_transfer_mode == 'rct':
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
prd_face_bgr = imagelib.reinhard_color_transfer ( np.clip( (prd_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
np.clip( (dst_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
source_mask=prd_face_mask_a, target_mask=prd_face_mask_a)
|
||||
prd_face_bgr = np.clip( prd_face_bgr.astype(np.float32) / 255.0, 0.0, 1.0)
|
||||
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
|
||||
elif self.color_transfer_mode == 'lct':
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
prd_face_bgr = imagelib.linear_color_transfer (prd_face_bgr, dst_face_bgr)
|
||||
prd_face_bgr = np.clip( prd_face_bgr, 0.0, 1.0)
|
||||
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
if self.mode == 'hist-match-bw':
|
||||
prd_face_bgr = cv2.cvtColor(prd_face_bgr, cv2.COLOR_BGR2GRAY)
|
||||
prd_face_bgr = np.repeat( np.expand_dims (prd_face_bgr, -1), (3,), -1 )
|
||||
|
||||
if self.mode == 'hist-match' or self.mode == 'hist-match-bw':
|
||||
if debug:
|
||||
debugs += [ cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ) ]
|
||||
|
||||
hist_mask_a = np.ones ( prd_face_bgr.shape[:2] + (1,) , dtype=np.float32)
|
||||
|
||||
if self.masked_hist_match:
|
||||
hist_mask_a *= prd_face_mask_a
|
||||
|
||||
white = (1.0-hist_mask_a)* np.ones ( prd_face_bgr.shape[:2] + (1,) , dtype=np.float32)
|
||||
|
||||
hist_match_1 = prd_face_bgr*hist_mask_a + white
|
||||
hist_match_1[ hist_match_1 > 1.0 ] = 1.0
|
||||
|
||||
hist_match_2 = dst_face_bgr*hist_mask_a + white
|
||||
hist_match_2[ hist_match_1 > 1.0 ] = 1.0
|
||||
|
||||
prd_face_bgr = imagelib.color_hist_match(hist_match_1, hist_match_2, self.hist_match_threshold )
|
||||
|
||||
#if self.masked_hist_match:
|
||||
# prd_face_bgr -= white
|
||||
|
||||
if self.mode == 'hist-match-bw':
|
||||
prd_face_bgr = prd_face_bgr.astype(dtype=np.float32)
|
||||
|
||||
out_img = cv2.warpAffine( prd_face_bgr, face_output_mat, img_size, out_img, cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip(out_img, 0.0, 1.0)
|
||||
|
||||
if debug:
|
||||
debugs += [out_img.copy()]
|
||||
|
||||
if self.mode == 'overlay':
|
||||
pass
|
||||
|
||||
if 'seamless' in self.mode:
|
||||
#mask used for cv2.seamlessClone
|
||||
img_face_seamless_mask_a = None
|
||||
img_face_mask_a = img_mask_blurry_aaa[...,0:1]
|
||||
for i in range(1,10):
|
||||
a = img_face_mask_a > i / 10.0
|
||||
if len(np.argwhere(a)) == 0:
|
||||
continue
|
||||
img_face_seamless_mask_a = img_mask_blurry_aaa[...,0:1].copy()
|
||||
img_face_seamless_mask_a[a] = 1.0
|
||||
img_face_seamless_mask_a[img_face_seamless_mask_a <= i / 10.0] = 0.0
|
||||
break
|
||||
|
||||
try:
|
||||
#calc same bounding rect and center point as in cv2.seamlessClone to prevent jittering
|
||||
l,t,w,h = cv2.boundingRect( (img_face_seamless_mask_a*255).astype(np.uint8) )
|
||||
s_maskx, s_masky = int(l+w/2), int(t+h/2)
|
||||
|
||||
out_img = cv2.seamlessClone( (out_img*255).astype(np.uint8), (img_bgr*255).astype(np.uint8), (img_face_seamless_mask_a*255).astype(np.uint8), (s_maskx,s_masky) , cv2.NORMAL_CLONE )
|
||||
out_img = out_img.astype(dtype=np.float32) / 255.0
|
||||
except Exception as e:
|
||||
#seamlessClone may fail in some cases
|
||||
e_str = traceback.format_exc()
|
||||
|
||||
if 'MemoryError' in e_str:
|
||||
raise Exception("Seamless fail: " + e_str) #reraise MemoryError in order to reprocess this data by other processes
|
||||
else:
|
||||
print ("Seamless fail: " + e_str)
|
||||
|
||||
if debug:
|
||||
debugs += [out_img.copy()]
|
||||
|
||||
out_img = np.clip( img_bgr*(1-img_mask_blurry_aaa) + (out_img*img_mask_blurry_aaa) , 0, 1.0 )
|
||||
|
||||
if 'seamless' in self.mode and self.color_transfer_mode is not None:
|
||||
out_face_bgr = cv2.warpAffine( out_img, face_mat, (output_size, output_size) )
|
||||
|
||||
if self.color_transfer_mode == 'rct':
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
new_out_face_bgr = imagelib.reinhard_color_transfer ( np.clip( (out_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
np.clip( (dst_face_bgr*255).astype(np.uint8), 0, 255),
|
||||
source_mask=face_mask_blurry_aaa, target_mask=face_mask_blurry_aaa)
|
||||
new_out_face_bgr = np.clip( new_out_face_bgr.astype(np.float32) / 255.0, 0.0, 1.0)
|
||||
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( new_out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
|
||||
elif self.color_transfer_mode == 'lct':
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
new_out_face_bgr = imagelib.linear_color_transfer (out_face_bgr, dst_face_bgr)
|
||||
new_out_face_bgr = np.clip( new_out_face_bgr, 0.0, 1.0)
|
||||
|
||||
if debug:
|
||||
debugs += [ np.clip( cv2.warpAffine( new_out_face_bgr, face_output_mat, img_size, np.zeros(img_bgr.shape, dtype=np.float32), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT ), 0, 1.0) ]
|
||||
|
||||
new_out = cv2.warpAffine( new_out_face_bgr, face_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip( img_bgr*(1-img_mask_blurry_aaa) + (new_out*img_mask_blurry_aaa) , 0, 1.0 )
|
||||
|
||||
if self.mode == 'seamless-hist-match':
|
||||
out_face_bgr = cv2.warpAffine( out_img, face_mat, (output_size, output_size) )
|
||||
new_out_face_bgr = imagelib.color_hist_match(out_face_bgr, dst_face_bgr, self.hist_match_threshold)
|
||||
new_out = cv2.warpAffine( new_out_face_bgr, face_mat, img_size, img_bgr.copy(), cv2.WARP_INVERSE_MAP | cv2.INTER_LANCZOS4, cv2.BORDER_TRANSPARENT )
|
||||
out_img = np.clip( img_bgr*(1-img_mask_blurry_aaa) + (new_out*img_mask_blurry_aaa) , 0, 1.0 )
|
||||
|
||||
if self.final_image_color_degrade_power != 0:
|
||||
if debug:
|
||||
debugs += [out_img.copy()]
|
||||
out_img_reduced = imagelib.reduce_colors(out_img, 256)
|
||||
if self.final_image_color_degrade_power == 100:
|
||||
out_img = out_img_reduced
|
||||
else:
|
||||
alpha = self.final_image_color_degrade_power / 100.0
|
||||
out_img = (out_img*(1.0-alpha) + out_img_reduced*alpha)
|
||||
|
||||
if self.alpha:
|
||||
out_img = np.concatenate ( [out_img, np.expand_dims (img_mask_blurry_aaa[:,:,0],-1)], -1 )
|
||||
|
||||
out_img = np.clip (out_img, 0.0, 1.0 )
|
||||
|
||||
if debug:
|
||||
debugs += [out_img.copy()]
|
||||
|
||||
return debugs if debug else out_img
|
6
converters/FrameInfo.py
Normal file
6
converters/FrameInfo.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
class FrameInfo(object):
|
||||
def __init__(self, filename=None, landmarks_list=None):
|
||||
self.filename = filename
|
||||
self.landmarks_list = landmarks_list or []
|
||||
self.motion_deg = 0
|
||||
self.motion_power = 0
|
|
@ -1,4 +1,4 @@
|
|||
from .Converter import Converter
|
||||
from .ConverterMasked import ConverterMasked
|
||||
from .ConverterImage import ConverterImage
|
||||
from .ConverterAvatar import ConverterAvatar
|
||||
from .FrameInfo import FrameInfo
|
||||
from .ConverterConfig import ConverterConfig, ConverterConfigMasked, ConverterConfigFaceAvatar
|
||||
from .ConvertMasked import ConvertMasked
|
||||
from .ConvertAvatar import ConvertFaceAvatar
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,6 +6,7 @@ class FaceType(IntEnum):
|
|||
HEAD = 2,
|
||||
|
||||
FULL_NO_ALIGN = 5,
|
||||
HEAD_NO_ALIGN = 6,
|
||||
MARK_ONLY = 10, #no align at all, just embedded faceinfo
|
||||
|
||||
@staticmethod
|
||||
|
@ -24,10 +25,12 @@ from_string_dict = {'half_face': FaceType.HALF,
|
|||
'head' : FaceType.HEAD,
|
||||
'mark_only' : FaceType.MARK_ONLY,
|
||||
'full_face_no_align' : FaceType.FULL_NO_ALIGN,
|
||||
'head_no_align' : FaceType.HEAD_NO_ALIGN,
|
||||
}
|
||||
to_string_dict = { FaceType.HALF : 'half_face',
|
||||
FaceType.FULL : 'full_face',
|
||||
FaceType.HEAD : 'head',
|
||||
FaceType.MARK_ONLY :'mark_only',
|
||||
FaceType.FULL_NO_ALIGN : 'full_face_no_align'
|
||||
FaceType.FULL_NO_ALIGN : 'full_face_no_align',
|
||||
FaceType.HEAD_NO_ALIGN : 'head_no_align'
|
||||
}
|
||||
|
|
|
@ -142,13 +142,16 @@ def get_transform_mat (image_landmarks, output_size, face_type, scale=1.0):
|
|||
if face_type == FaceType.FULL_NO_ALIGN:
|
||||
face_type = FaceType.FULL
|
||||
remove_align = True
|
||||
elif face_type == FaceType.HEAD_NO_ALIGN:
|
||||
face_type = FaceType.HEAD
|
||||
remove_align = True
|
||||
|
||||
if face_type == FaceType.HALF:
|
||||
padding = 0
|
||||
elif face_type == FaceType.FULL:
|
||||
padding = (output_size / 64) * 12
|
||||
elif face_type == FaceType.HEAD:
|
||||
padding = (output_size / 64) * 24
|
||||
padding = (output_size / 64) * 21
|
||||
else:
|
||||
raise ValueError ('wrong face_type: ', face_type)
|
||||
|
||||
|
|
|
@ -1,26 +1,21 @@
|
|||
from .estimate_sharpness import estimate_sharpness
|
||||
from .equalize_and_stack_square import equalize_and_stack_square
|
||||
|
||||
from .text import get_text_image
|
||||
from .text import get_draw_text_lines
|
||||
from .text import get_text_image, get_draw_text_lines
|
||||
|
||||
from .draw import draw_polygon
|
||||
from .draw import draw_rect
|
||||
from .draw import draw_polygon, draw_rect
|
||||
|
||||
from .morph import morph_by_points
|
||||
|
||||
from .warp import gen_warp_params
|
||||
from .warp import warp_by_params
|
||||
from .warp import gen_warp_params, warp_by_params
|
||||
|
||||
from .reduce_colors import reduce_colors
|
||||
|
||||
from .color_transfer import color_hist_match
|
||||
from .color_transfer import reinhard_color_transfer
|
||||
from .color_transfer import linear_color_transfer
|
||||
from .color_transfer import color_hist_match, reinhard_color_transfer, linear_color_transfer
|
||||
|
||||
from .DCSCN import DCSCN
|
||||
|
||||
from .common import normalize_channels
|
||||
from .common import normalize_channels, overlay_alpha_image
|
||||
|
||||
from .IEPolys import IEPolys
|
||||
|
||||
|
|
148
imagelib/blur.py
148
imagelib/blur.py
|
@ -1,143 +1,9 @@
|
|||
import math
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
from scipy.signal import convolve2d
|
||||
from skimage.draw import line
|
||||
|
||||
class LineDictionary:
|
||||
def __init__(self):
|
||||
self.lines = {}
|
||||
self.Create3x3Lines()
|
||||
self.Create5x5Lines()
|
||||
self.Create7x7Lines()
|
||||
self.Create9x9Lines()
|
||||
return
|
||||
|
||||
def Create3x3Lines(self):
|
||||
lines = {}
|
||||
lines[0] = [1,0,1,2]
|
||||
lines[45] = [2,0,0,2]
|
||||
lines[90] = [0,1,2,1]
|
||||
lines[135] = [0,0,2,2]
|
||||
self.lines[3] = lines
|
||||
return
|
||||
|
||||
def Create5x5Lines(self):
|
||||
lines = {}
|
||||
lines[0] = [2,0,2,4]
|
||||
lines[22.5] = [3,0,1,4]
|
||||
lines[45] = [0,4,4,0]
|
||||
lines[67.5] = [0,3,4,1]
|
||||
lines[90] = [0,2,4,2]
|
||||
lines[112.5] = [0,1,4,3]
|
||||
lines[135] = [0,0,4,4]
|
||||
lines[157.5]= [1,0,3,4]
|
||||
self.lines[5] = lines
|
||||
return
|
||||
|
||||
def Create7x7Lines(self):
|
||||
lines = {}
|
||||
lines[0] = [3,0,3,6]
|
||||
lines[15] = [4,0,2,6]
|
||||
lines[30] = [5,0,1,6]
|
||||
lines[45] = [6,0,0,6]
|
||||
lines[60] = [6,1,0,5]
|
||||
lines[75] = [6,2,0,4]
|
||||
lines[90] = [0,3,6,3]
|
||||
lines[105] = [0,2,6,4]
|
||||
lines[120] = [0,1,6,5]
|
||||
lines[135] = [0,0,6,6]
|
||||
lines[150] = [1,0,5,6]
|
||||
lines[165] = [2,0,4,6]
|
||||
self.lines[7] = lines
|
||||
return
|
||||
|
||||
def Create9x9Lines(self):
|
||||
lines = {}
|
||||
lines[0] = [4,0,4,8]
|
||||
lines[11.25] = [5,0,3,8]
|
||||
lines[22.5] = [6,0,2,8]
|
||||
lines[33.75] = [7,0,1,8]
|
||||
lines[45] = [8,0,0,8]
|
||||
lines[56.25] = [8,1,0,7]
|
||||
lines[67.5] = [8,2,0,6]
|
||||
lines[78.75] = [8,3,0,5]
|
||||
lines[90] = [8,4,0,4]
|
||||
lines[101.25] = [0,3,8,5]
|
||||
lines[112.5] = [0,2,8,6]
|
||||
lines[123.75] = [0,1,8,7]
|
||||
lines[135] = [0,0,8,8]
|
||||
lines[146.25] = [1,0,7,8]
|
||||
lines[157.5] = [2,0,6,8]
|
||||
lines[168.75] = [3,0,5,8]
|
||||
self.lines[9] = lines
|
||||
return
|
||||
|
||||
lineLengths =[3,5,7,9]
|
||||
lineTypes = ["full", "right", "left"]
|
||||
|
||||
lineDict = LineDictionary()
|
||||
|
||||
def LinearMotionBlur_random(img):
|
||||
lineLengthIdx = np.random.randint(0, len(lineLengths))
|
||||
lineTypeIdx = np.random.randint(0, len(lineTypes))
|
||||
lineLength = lineLengths[lineLengthIdx]
|
||||
lineType = lineTypes[lineTypeIdx]
|
||||
lineAngle = randomAngle(lineLength)
|
||||
return LinearMotionBlur(img, lineLength, lineAngle, lineType)
|
||||
|
||||
def LinearMotionBlur(img, dim, angle, linetype='full'):
|
||||
if len(img.shape) == 2:
|
||||
h, w = img.shape
|
||||
c = 1
|
||||
img = img[...,np.newaxis]
|
||||
elif len(img.shape) == 3:
|
||||
h,w,c = img.shape
|
||||
else:
|
||||
raise ValueError('unsupported img.shape')
|
||||
|
||||
kernel = LineKernel(dim, angle, linetype)
|
||||
|
||||
imgs = []
|
||||
for i in range(c):
|
||||
imgs.append ( convolve2d(img[...,i], kernel, mode='same') )
|
||||
|
||||
img = np.stack(imgs, axis=-1)
|
||||
img = np.squeeze(img)
|
||||
return img
|
||||
|
||||
def LineKernel(dim, angle, linetype):
|
||||
kernelwidth = dim
|
||||
kernelCenter = int(math.floor(dim/2))
|
||||
angle = SanitizeAngleValue(kernelCenter, angle)
|
||||
kernel = np.zeros((kernelwidth, kernelwidth), dtype=np.float32)
|
||||
lineAnchors = lineDict.lines[dim][angle]
|
||||
if(linetype == 'right'):
|
||||
lineAnchors[0] = kernelCenter
|
||||
lineAnchors[1] = kernelCenter
|
||||
if(linetype == 'left'):
|
||||
lineAnchors[2] = kernelCenter
|
||||
lineAnchors[3] = kernelCenter
|
||||
rr,cc = line(lineAnchors[0], lineAnchors[1], lineAnchors[2], lineAnchors[3])
|
||||
kernel[rr,cc]=1
|
||||
normalizationFactor = np.count_nonzero(kernel)
|
||||
kernel = kernel / normalizationFactor
|
||||
return kernel
|
||||
|
||||
def SanitizeAngleValue(kernelCenter, angle):
|
||||
numDistinctLines = kernelCenter * 4
|
||||
angle = math.fmod(angle, 180.0)
|
||||
validLineAngles = np.linspace(0,180, numDistinctLines, endpoint = False)
|
||||
angle = nearestValue(angle, validLineAngles)
|
||||
return angle
|
||||
|
||||
def nearestValue(theta, validAngles):
|
||||
idx = (np.abs(validAngles-theta)).argmin()
|
||||
return validAngles[idx]
|
||||
|
||||
def randomAngle(kerneldim):
|
||||
kernelCenter = int(math.floor(kerneldim/2))
|
||||
numDistinctLines = kernelCenter * 4
|
||||
validLineAngles = np.linspace(0,180, numDistinctLines, endpoint = False)
|
||||
angleIdx = np.random.randint(0, len(validLineAngles))
|
||||
return int(validLineAngles[angleIdx])
|
||||
def LinearMotionBlur(image, size, angle):
|
||||
k = np.zeros((size, size), dtype=np.float32)
|
||||
k[ (size-1)// 2 , :] = np.ones(size, dtype=np.float32)
|
||||
k = cv2.warpAffine(k, cv2.getRotationMatrix2D( (size / 2 -0.5 , size / 2 -0.5 ) , angle, 1.0), (size, size) )
|
||||
k = k * ( 1.0 / np.sum(k) )
|
||||
return cv2.filter2D(image, -1, k)
|
|
@ -19,3 +19,18 @@ def normalize_channels(img, target_channels):
|
|||
c = target_channels
|
||||
|
||||
return img
|
||||
|
||||
def overlay_alpha_image(img_target, img_source, xy_offset=(0,0) ):
|
||||
(h,w,c) = img_source.shape
|
||||
if c != 4:
|
||||
raise ValueError("overlay_alpha_image, img_source must have 4 channels")
|
||||
|
||||
x1, x2 = xy_offset[0], xy_offset[0] + w
|
||||
y1, y2 = xy_offset[1], xy_offset[1] + h
|
||||
|
||||
alpha_s = img_source[:, :, 3] / 255.0
|
||||
alpha_l = 1.0 - alpha_s
|
||||
|
||||
for c in range(0, 3):
|
||||
img_target[y1:y2, x1:x2, c] = (alpha_s * img_source[:, :, c] +
|
||||
alpha_l * img_target[y1:y2, x1:x2, c])
|
|
@ -67,20 +67,12 @@ class InteractBase(object):
|
|||
|
||||
def log_info(self, msg, end='\n'):
|
||||
if self.pg_bar is not None:
|
||||
try: # Attempt print before the pb
|
||||
tqdm.write(msg)
|
||||
return
|
||||
except:
|
||||
pass #Fallback to normal print upon failure
|
||||
print ("\n")
|
||||
print (msg, end=end)
|
||||
|
||||
def log_err(self, msg, end='\n'):
|
||||
if self.pg_bar is not None:
|
||||
try: # Attempt print before the pb
|
||||
tqdm.write(f'{self.error_log_line_prefix}{msg}')
|
||||
return
|
||||
except:
|
||||
pass #Fallback to normal print upon failure
|
||||
print ("\n")
|
||||
print (f'{self.error_log_line_prefix}{msg}', end=end)
|
||||
|
||||
def named_window(self, wnd_name):
|
||||
|
|
|
@ -30,8 +30,15 @@ class SubprocessFunctionCaller(object):
|
|||
result = self.func ( *obj['args'], **obj['kwargs'] )
|
||||
self.s2c.put (result)
|
||||
|
||||
def __getstate__(self):
|
||||
#disable pickling this class
|
||||
return dict()
|
||||
|
||||
def __setstate__(self, d):
|
||||
self.__dict__.update(d)
|
||||
|
||||
@staticmethod
|
||||
def make_pair( func ):
|
||||
def make_pair(func):
|
||||
s2c = multiprocessing.Queue()
|
||||
c2s = multiprocessing.Queue()
|
||||
lock = multiprocessing.Lock()
|
||||
|
|
|
@ -87,13 +87,15 @@ class Subprocessor(object):
|
|||
c2s.put ( {'op': 'error', 'data' : data} )
|
||||
|
||||
#overridable
|
||||
def __init__(self, name, SubprocessorCli_class, no_response_time_sec = 0):
|
||||
def __init__(self, name, SubprocessorCli_class, no_response_time_sec = 0, io_loop_sleep_time=0.005, initialize_subprocesses_in_serial=True):
|
||||
if not issubclass(SubprocessorCli_class, Subprocessor.Cli):
|
||||
raise ValueError("SubprocessorCli_class must be subclass of Subprocessor.Cli")
|
||||
|
||||
self.name = name
|
||||
self.SubprocessorCli_class = SubprocessorCli_class
|
||||
self.no_response_time_sec = no_response_time_sec
|
||||
self.io_loop_sleep_time = io_loop_sleep_time
|
||||
self.initialize_subprocesses_in_serial = initialize_subprocesses_in_serial
|
||||
|
||||
#overridable
|
||||
def process_info_generator(self):
|
||||
|
@ -133,7 +135,8 @@ class Subprocessor(object):
|
|||
#overridable
|
||||
def on_tick(self):
|
||||
#tick in main loop
|
||||
pass
|
||||
#return True if system can be finalized when no data in get_data, orelse False
|
||||
return True
|
||||
|
||||
#overridable
|
||||
def on_check_run(self):
|
||||
|
@ -157,23 +160,24 @@ class Subprocessor(object):
|
|||
|
||||
self.clis.append (cli)
|
||||
|
||||
while True:
|
||||
while not cli.c2s.empty():
|
||||
obj = cli.c2s.get()
|
||||
op = obj.get('op','')
|
||||
if op == 'init_ok':
|
||||
cli.state = 0
|
||||
elif op == 'log_info':
|
||||
io.log_info(obj['msg'])
|
||||
elif op == 'log_err':
|
||||
io.log_err(obj['msg'])
|
||||
elif op == 'error':
|
||||
cli.kill()
|
||||
self.clis.remove(cli)
|
||||
if self.initialize_subprocesses_in_serial:
|
||||
while True:
|
||||
while not cli.c2s.empty():
|
||||
obj = cli.c2s.get()
|
||||
op = obj.get('op','')
|
||||
if op == 'init_ok':
|
||||
cli.state = 0
|
||||
elif op == 'log_info':
|
||||
io.log_info(obj['msg'])
|
||||
elif op == 'log_err':
|
||||
io.log_err(obj['msg'])
|
||||
elif op == 'error':
|
||||
cli.kill()
|
||||
self.clis.remove(cli)
|
||||
break
|
||||
if cli.state == 0:
|
||||
break
|
||||
if cli.state == 0:
|
||||
break
|
||||
io.process_messages(0.005)
|
||||
io.process_messages(0.005)
|
||||
except:
|
||||
raise Exception ("Unable to start subprocess %s" % (name))
|
||||
|
||||
|
@ -232,6 +236,15 @@ class Subprocessor(object):
|
|||
elif op == 'progress_bar_inc':
|
||||
io.progress_bar_inc(obj['c'])
|
||||
|
||||
for cli in self.clis[:]:
|
||||
if cli.state == 1:
|
||||
if self.no_response_time_sec != 0 and (time.time() - cli.sent_time) > self.no_response_time_sec:
|
||||
#subprocess busy too long
|
||||
print ( '%s doesnt response, terminating it.' % (cli.name) )
|
||||
self.on_data_return (cli.host_dict, cli.sent_data )
|
||||
cli.kill()
|
||||
self.clis.remove(cli)
|
||||
|
||||
for cli in self.clis[:]:
|
||||
if cli.state == 0:
|
||||
#free state of subprocess, get some data from get_data
|
||||
|
@ -243,19 +256,14 @@ class Subprocessor(object):
|
|||
cli.sent_data = data
|
||||
cli.state = 1
|
||||
|
||||
elif cli.state == 1:
|
||||
if self.no_response_time_sec != 0 and (time.time() - cli.sent_time) > self.no_response_time_sec:
|
||||
#subprocess busy too long
|
||||
print ( '%s doesnt response, terminating it.' % (cli.name) )
|
||||
self.on_data_return (cli.host_dict, cli.sent_data )
|
||||
cli.kill()
|
||||
self.clis.remove(cli)
|
||||
if self.io_loop_sleep_time != 0:
|
||||
io.process_messages(self.io_loop_sleep_time)
|
||||
|
||||
if all ([cli.state == 0 for cli in self.clis]):
|
||||
if self.on_tick() and all ([cli.state == 0 for cli in self.clis]):
|
||||
#all subprocesses free and no more data available to process, ending loop
|
||||
break
|
||||
io.process_messages(0.005)
|
||||
self.on_tick()
|
||||
|
||||
|
||||
|
||||
#gracefully terminating subprocesses
|
||||
for cli in self.clis[:]:
|
||||
|
|
2
main.py
2
main.py
|
@ -153,7 +153,6 @@ if __name__ == "__main__":
|
|||
'aligned_dir' : arguments.aligned_dir,
|
||||
'model_dir' : arguments.model_dir,
|
||||
'model_name' : arguments.model_name,
|
||||
'debug' : arguments.debug,
|
||||
}
|
||||
device_args = {'cpu_only' : arguments.cpu_only,
|
||||
'force_gpu_idx' : arguments.force_gpu_idx,
|
||||
|
@ -167,7 +166,6 @@ if __name__ == "__main__":
|
|||
p.add_argument('--aligned-dir', action=fixPathAction, dest="aligned_dir", help="Aligned directory. This is where the extracted of dst faces stored.")
|
||||
p.add_argument('--model-dir', required=True, action=fixPathAction, dest="model_dir", help="Model dir.")
|
||||
p.add_argument('--model', required=True, dest="model_name", choices=Path_utils.get_all_dir_names_startswith ( Path(__file__).parent / 'models' , 'Model_'), help="Type of model")
|
||||
p.add_argument('--debug', action="store_true", dest="debug", default=False, help="Debug converter.")
|
||||
p.add_argument('--force-gpu-idx', type=int, dest="force_gpu_idx", default=-1, help="Force to choose this GPU idx.")
|
||||
p.add_argument('--cpu-only', action="store_true", dest="cpu_only", default=False, help="Convert on CPU.")
|
||||
p.set_defaults(func=process_convert)
|
||||
|
|
|
@ -1,37 +1,77 @@
|
|||
import sys
|
||||
import math
|
||||
import multiprocessing
|
||||
import operator
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import numpy.linalg as npla
|
||||
|
||||
from converters import Converter
|
||||
import imagelib
|
||||
from converters import FrameInfo, ConverterConfig, ConvertMasked, ConvertFaceAvatar
|
||||
from facelib import FaceType, FANSegmentator, LandmarksProcessor
|
||||
from interact import interact as io
|
||||
from joblib import SubprocessFunctionCaller, Subprocessor
|
||||
from utils import Path_utils
|
||||
from utils.cv2_utils import *
|
||||
from utils.DFLJPG import DFLJPG
|
||||
from utils.DFLPNG import DFLPNG
|
||||
from imagelib import normalize_channels
|
||||
|
||||
from .ConverterScreen import Screen, ScreenManager
|
||||
|
||||
CONVERTER_DEBUG = False
|
||||
|
||||
class ConvertSubprocessor(Subprocessor):
|
||||
|
||||
class Frame(object):
|
||||
def __init__(self, prev_temporal_frame_infos=None,
|
||||
frame_info=None,
|
||||
next_temporal_frame_infos=None):
|
||||
self.prev_temporal_frame_infos = prev_temporal_frame_infos
|
||||
self.frame_info = frame_info
|
||||
self.next_temporal_frame_infos = next_temporal_frame_infos
|
||||
self.output_filename = None
|
||||
|
||||
self.idx = None
|
||||
self.cfg = None
|
||||
self.is_done = False
|
||||
self.is_processing = False
|
||||
self.is_shown = False
|
||||
self.image = None
|
||||
|
||||
class ProcessingFrame(object):
|
||||
def __init__(self, idx=None,
|
||||
cfg=None,
|
||||
prev_temporal_frame_infos=None,
|
||||
frame_info=None,
|
||||
next_temporal_frame_infos=None,
|
||||
output_filename=None,
|
||||
need_return_image = False):
|
||||
self.idx = idx
|
||||
self.cfg = cfg
|
||||
self.prev_temporal_frame_infos = prev_temporal_frame_infos
|
||||
self.frame_info = frame_info
|
||||
self.next_temporal_frame_infos = next_temporal_frame_infos
|
||||
self.output_filename = output_filename
|
||||
|
||||
self.need_return_image = need_return_image
|
||||
if self.need_return_image:
|
||||
self.image = None
|
||||
|
||||
class Cli(Subprocessor.Cli):
|
||||
|
||||
#override
|
||||
def on_initialize(self, client_dict):
|
||||
io.log_info ('Running on %s.' % (client_dict['device_name']) )
|
||||
self.log_info ('Running on %s.' % (client_dict['device_name']) )
|
||||
self.device_idx = client_dict['device_idx']
|
||||
self.device_name = client_dict['device_name']
|
||||
self.converter = client_dict['converter']
|
||||
self.input_data = client_dict['input_data']
|
||||
self.output_path = Path(client_dict['output_dir']) if 'output_dir' in client_dict.keys() else None
|
||||
self.alignments = client_dict['alignments']
|
||||
self.debug = client_dict['debug']
|
||||
self.predictor_func = client_dict['predictor_func']
|
||||
self.dcscn_upscale_func = client_dict['dcscn_upscale_func']
|
||||
|
||||
#transfer and set stdin in order to work code.interact in debug subprocess
|
||||
stdin_fd = client_dict['stdin_fd']
|
||||
|
@ -44,180 +84,372 @@ class ConvertSubprocessor(Subprocessor):
|
|||
#therefore forcing active_DeviceConfig to CPU only
|
||||
nnlib.active_DeviceConfig = nnlib.DeviceConfig (cpu_only=True)
|
||||
|
||||
self.converter.on_cli_initialize()
|
||||
self.fanseg_by_face_type = {}
|
||||
self.fanseg_input_size = 256
|
||||
|
||||
def fanseg_extract(face_type, *args, **kwargs):
|
||||
fanseg = self.fanseg_by_face_type.get(face_type, None)
|
||||
if self.fanseg_by_face_type.get(face_type, None) is None:
|
||||
fanseg = FANSegmentator( self.fanseg_input_size , FaceType.toString( face_type ) )
|
||||
self.fanseg_by_face_type[face_type] = fanseg
|
||||
|
||||
return fanseg.extract(*args, **kwargs)
|
||||
|
||||
self.fanseg_extract_func = fanseg_extract
|
||||
|
||||
return None
|
||||
|
||||
#override
|
||||
def process_data(self, data):
|
||||
files_processed = 1
|
||||
faces_processed = 0
|
||||
def process_data(self, pf): #pf=ProcessingFrame
|
||||
cfg = pf.cfg.copy()
|
||||
cfg.predictor_func = self.predictor_func
|
||||
|
||||
frame_info = pf.frame_info
|
||||
|
||||
filename = frame_info.filename
|
||||
landmarks_list = frame_info.landmarks_list
|
||||
|
||||
idx, = data
|
||||
filename = self.input_data[idx][0]
|
||||
filename_path = Path(filename)
|
||||
output_filename = pf.output_filename
|
||||
need_return_image = pf.need_return_image
|
||||
|
||||
output_filename_path = self.output_path / (filename_path.stem + '.png')
|
||||
image = None
|
||||
if len(landmarks_list) == 0:
|
||||
self.log_info ( 'no faces found for %s, copying without faces' % (filename_path.name) )
|
||||
|
||||
if self.converter.type == Converter.TYPE_FACE:
|
||||
if filename_path.stem not in self.alignments.keys():
|
||||
if not self.debug:
|
||||
self.log_info ( 'no faces found for %s, copying without faces' % (filename_path.name) )
|
||||
|
||||
if filename_path.suffix == '.png':
|
||||
shutil.copy ( str(filename_path), str(output_filename_path) )
|
||||
else:
|
||||
image = cv2_imread(str(filename_path))
|
||||
cv2_imwrite ( str(output_filename_path), image )
|
||||
if filename_path.suffix == '.png':
|
||||
shutil.copy (filename, output_filename )
|
||||
else:
|
||||
image = (cv2_imread(str(filename_path)) / 255.0).astype(np.float32)
|
||||
image = normalize_channels (image, 3)
|
||||
img_bgr = cv2_imread(filename)
|
||||
cv2_imwrite (output_filename, img_bgr)
|
||||
|
||||
faces = self.alignments[filename_path.stem]
|
||||
if need_return_image:
|
||||
img_bgr = cv2_imread(filename)
|
||||
pf.image = img_bgr
|
||||
else:
|
||||
if cfg.type == ConverterConfig.TYPE_MASKED:
|
||||
cfg.dcscn_upscale_func = self.dcscn_upscale_func
|
||||
cfg.fanseg_input_size = self.fanseg_input_size
|
||||
cfg.fanseg_extract_func = self.fanseg_extract_func
|
||||
|
||||
if self.debug:
|
||||
debug_images = []
|
||||
try:
|
||||
final_img = ConvertMasked (cfg, frame_info)
|
||||
except Exception as e:
|
||||
e_str = traceback.format_exc()
|
||||
if 'MemoryError' in e_str:
|
||||
raise Subprocessor.SilenceException
|
||||
else:
|
||||
raise Exception( 'Error while converting file [%s]: %s' % (filename, e_str) )
|
||||
|
||||
for face_num, image_landmarks in enumerate(faces):
|
||||
try:
|
||||
if self.debug:
|
||||
self.log_info ( '\nConverting face_num [%d] in file [%s]' % (face_num, filename_path) )
|
||||
elif cfg.type == ConverterConfig.TYPE_FACE_AVATAR:
|
||||
final_img = ConvertFaceAvatar (cfg, pf.prev_temporal_frame_infos,
|
||||
pf.frame_info,
|
||||
pf.next_temporal_frame_infos )
|
||||
|
||||
if self.debug:
|
||||
debug_images += self.converter.cli_convert_face(image, image_landmarks, self.debug)
|
||||
else:
|
||||
image = self.converter.cli_convert_face(image, image_landmarks, self.debug)
|
||||
if output_filename is not None and final_img is not None:
|
||||
cv2_imwrite (output_filename, final_img )
|
||||
|
||||
except Exception as e:
|
||||
e_str = traceback.format_exc()
|
||||
if 'MemoryError' in e_str:
|
||||
raise Subprocessor.SilenceException
|
||||
else:
|
||||
raise Exception( 'Error while converting face_num [%d] in file [%s]: %s' % (face_num, filename_path, e_str) )
|
||||
if need_return_image:
|
||||
pf.image = final_img
|
||||
|
||||
if self.debug:
|
||||
return (1, debug_images)
|
||||
|
||||
faces_processed = len(faces)
|
||||
elif self.converter.type == Converter.TYPE_IMAGE:
|
||||
image = (cv2_imread(str(filename_path)) / 255.0).astype(np.float32)
|
||||
image = normalize_channels (image, 3)
|
||||
image = self.converter.cli_convert_image(image, None, self.debug)
|
||||
|
||||
if self.debug:
|
||||
return (1, image)
|
||||
|
||||
faces_processed = 1
|
||||
elif self.converter.type == Converter.TYPE_FACE_AVATAR:
|
||||
max_idx = len(self.input_data)-1
|
||||
|
||||
i0 = max (idx-1, 0)
|
||||
i1 = idx
|
||||
i2 = min (max_idx, idx+1)
|
||||
|
||||
f0 = (cv2_imread( self.input_data[i0][0] ) / 255.0).astype(np.float32)
|
||||
f0_lmrk = self.input_data[i0][1]
|
||||
f1 = (cv2_imread( self.input_data[i1][0] ) / 255.0).astype(np.float32)
|
||||
f1_lmrk = self.input_data[i1][1]
|
||||
f2 = (cv2_imread( self.input_data[i2][0] ) / 255.0).astype(np.float32)
|
||||
f2_lmrk = self.input_data[i2][1]
|
||||
|
||||
f0, f1, f2 = [ normalize_channels (f, 3) for f in [f0,f1,f2] ]
|
||||
|
||||
image = self.converter.cli_convert_face(f0, f0_lmrk, f1, f1_lmrk, f2, f2_lmrk, self.debug)
|
||||
|
||||
output_filename_path = self.output_path / self.input_data[idx][2]
|
||||
|
||||
if self.debug:
|
||||
return (1, image)
|
||||
|
||||
faces_processed = 1
|
||||
|
||||
if image is not None and not self.debug:
|
||||
cv2_imwrite (str(output_filename_path), (image*255).astype(np.uint8) )
|
||||
|
||||
return (0, files_processed, faces_processed)
|
||||
return pf
|
||||
|
||||
#overridable
|
||||
def get_data_name (self, data):
|
||||
def get_data_name (self, pf):
|
||||
#return string identificator of your data
|
||||
idx, = data
|
||||
return self.input_data[idx][0]
|
||||
return pf.frame_info.filename
|
||||
|
||||
#override
|
||||
def __init__(self, converter, input_data, output_path, alignments, debug = False):
|
||||
super().__init__('Converter', ConvertSubprocessor.Cli, 86400 if debug == True else 60)
|
||||
def __init__(self, is_interactive, converter_config, frames, output_path):
|
||||
if len (frames) == 0:
|
||||
raise ValueError ("len (frames) == 0")
|
||||
|
||||
self.converter = converter
|
||||
self.input_data = input_data
|
||||
self.input_data_idxs = [ *range(len(self.input_data)) ]
|
||||
super().__init__('Converter', ConvertSubprocessor.Cli, 86400 if CONVERTER_DEBUG else 60, io_loop_sleep_time=0.001, initialize_subprocesses_in_serial=False)# if debug == True else 60)
|
||||
|
||||
self.is_interactive = is_interactive
|
||||
self.converter_config = converter_config
|
||||
|
||||
#dummy predict and sleep, tensorflow caching kernels. If remove it, sometime conversion speed can be x2 slower
|
||||
self.converter_config.predictor_func (dummy_predict=True)
|
||||
time.sleep(2)
|
||||
|
||||
self.predictor_func_host, self.predictor_func = SubprocessFunctionCaller.make_pair(self.converter_config.predictor_func)
|
||||
self.converter_config.predictor_func = None
|
||||
|
||||
self.dcscn = None
|
||||
def DCSCN_upscale(*args, **kwargs):
|
||||
if self.dcscn is None:
|
||||
self.dcscn = imagelib.DCSCN()
|
||||
return self.dcscn.upscale(*args, **kwargs)
|
||||
|
||||
self.dcscn_host, self.dcscn_upscale_func = SubprocessFunctionCaller.make_pair(DCSCN_upscale)
|
||||
|
||||
self.frames = frames
|
||||
self.output_path = output_path
|
||||
self.alignments = alignments
|
||||
self.debug = debug
|
||||
self.prefetch_frame_count = self.process_count = min(6,multiprocessing.cpu_count())
|
||||
|
||||
self.files_processed = 0
|
||||
self.faces_processed = 0
|
||||
self.frames_idxs = [ *range(len(self.frames)) ]
|
||||
self.frames_done_idxs = []
|
||||
|
||||
for i in range( len(self.frames) ):
|
||||
frame = self.frames[i]
|
||||
frame.idx = i
|
||||
frame.output_filename = self.output_path / ('%.5d.png' % i)
|
||||
|
||||
frames[0].cfg = self.converter_config.copy()
|
||||
|
||||
#override
|
||||
def process_info_generator(self):
|
||||
r = [0] if self.debug else range( min(6,multiprocessing.cpu_count()) )
|
||||
r = [0] if CONVERTER_DEBUG else range(self.process_count)
|
||||
|
||||
for i in r:
|
||||
yield 'CPU%d' % (i), {}, {'device_idx': i,
|
||||
'device_name': 'CPU%d' % (i),
|
||||
'converter' : self.converter,
|
||||
'input_data' : self.input_data,
|
||||
'output_dir' : str(self.output_path),
|
||||
'alignments' : self.alignments,
|
||||
'debug': self.debug,
|
||||
'stdin_fd': sys.stdin.fileno() if self.debug else None
|
||||
'predictor_func': self.predictor_func,
|
||||
'dcscn_upscale_func': self.dcscn_upscale_func,
|
||||
'stdin_fd': sys.stdin.fileno() if CONVERTER_DEBUG else None
|
||||
}
|
||||
|
||||
#overridable optional
|
||||
def on_clients_initialized(self):
|
||||
if self.debug:
|
||||
io.named_window ("Debug convert")
|
||||
io.progress_bar ("Converting", len (self.frames_idxs) )
|
||||
|
||||
io.progress_bar ("Converting", len (self.input_data_idxs) )
|
||||
self.process_remain_frames = not self.is_interactive
|
||||
self.is_interactive_quitting = not self.is_interactive
|
||||
|
||||
if self.is_interactive:
|
||||
help_images = {
|
||||
ConverterConfig.TYPE_MASKED : cv2_imread ( str(Path(__file__).parent / 'gfx' / 'help_converter_masked.jpg') ),
|
||||
ConverterConfig.TYPE_FACE_AVATAR : cv2_imread ( str(Path(__file__).parent / 'gfx' / 'help_converter_face_avatar.jpg') ),
|
||||
}
|
||||
|
||||
self.main_screen = Screen(initial_scale_to_width=1368, image=None, waiting_icon=True)
|
||||
self.help_screen = Screen(initial_scale_to_height=768, image=help_images[self.converter_config.type], waiting_icon=False)
|
||||
self.screen_manager = ScreenManager( "Converter", [self.main_screen, self.help_screen], capture_keys=True )
|
||||
self.screen_manager.set_current (self.help_screen)
|
||||
self.screen_manager.show_current()
|
||||
|
||||
#overridable optional
|
||||
def on_clients_finalized(self):
|
||||
|
||||
if self.is_interactive:
|
||||
self.screen_manager.finalize()
|
||||
|
||||
io.progress_bar_close()
|
||||
|
||||
if self.debug:
|
||||
io.destroy_all_windows()
|
||||
cfg_change_keys = ['`','1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'q', 'a', 'w', 's', 'e', 'd', 'r', 'f', 't', 'g','y','h',
|
||||
'z', 'x', 'c', 'v', 'b' ]
|
||||
#override
|
||||
def on_tick(self):
|
||||
self.predictor_func_host.process_messages()
|
||||
self.dcscn_host.process_messages()
|
||||
|
||||
go_prev_frame = False
|
||||
go_prev_frame_overriding_cfg = False
|
||||
go_next_frame = self.process_remain_frames
|
||||
go_next_frame_overriding_cfg = False
|
||||
|
||||
cur_frame = None
|
||||
if len(self.frames_idxs) != 0:
|
||||
cur_frame = self.frames[self.frames_idxs[0]]
|
||||
|
||||
if self.is_interactive:
|
||||
self.main_screen.set_waiting_icon(False)
|
||||
|
||||
if not self.is_interactive_quitting and not self.process_remain_frames:
|
||||
if cur_frame is not None:
|
||||
if not cur_frame.is_shown:
|
||||
if cur_frame.is_done:
|
||||
cur_frame.is_shown = True
|
||||
io.log_info (cur_frame.cfg)
|
||||
|
||||
if cur_frame.image is None:
|
||||
cur_frame.image = cv2_imread ( cur_frame.output_filename)
|
||||
if cur_frame.image is None:
|
||||
cur_frame.is_done = False #unable to read? recompute then
|
||||
cur_frame.is_shown = False
|
||||
self.main_screen.set_image(cur_frame.image)
|
||||
else:
|
||||
self.main_screen.set_waiting_icon(True)
|
||||
|
||||
else:
|
||||
self.main_screen.set_image(None)
|
||||
else:
|
||||
self.main_screen.set_image(None)
|
||||
self.main_screen.set_waiting_icon(True)
|
||||
|
||||
self.screen_manager.show_current()
|
||||
|
||||
key_events = self.screen_manager.get_key_events()
|
||||
key, chr_key, ctrl_pressed, alt_pressed, shift_pressed = key_events[-1] if len(key_events) > 0 else (0,0,False,False,False)
|
||||
|
||||
if key == 9: #tab
|
||||
self.screen_manager.switch_screens()
|
||||
else:
|
||||
if key == 27: #esc
|
||||
self.is_interactive_quitting = True
|
||||
elif self.screen_manager.get_current() is self.main_screen:
|
||||
if chr_key in self.cfg_change_keys:
|
||||
self.process_remain_frames = False
|
||||
|
||||
if cur_frame is not None:
|
||||
cfg = cur_frame.cfg
|
||||
prev_cfg = cfg.copy()
|
||||
|
||||
if cfg.type == ConverterConfig.TYPE_MASKED:
|
||||
if chr_key == '`':
|
||||
cfg.set_mode(0)
|
||||
elif key >= ord('1') and key <= ord('9'):
|
||||
cfg.set_mode( key - ord('0') )
|
||||
elif chr_key == 'q':
|
||||
cfg.add_hist_match_threshold(1 if not shift_pressed else 5)
|
||||
elif chr_key == 'a':
|
||||
cfg.add_hist_match_threshold(-1 if not shift_pressed else -5)
|
||||
elif chr_key == 'w':
|
||||
cfg.add_erode_mask_modifier(1 if not shift_pressed else 5)
|
||||
elif chr_key == 's':
|
||||
cfg.add_erode_mask_modifier(-1 if not shift_pressed else -5)
|
||||
elif chr_key == 'e':
|
||||
cfg.add_blur_mask_modifier(1 if not shift_pressed else 5)
|
||||
elif chr_key == 'd':
|
||||
cfg.add_blur_mask_modifier(-1 if not shift_pressed else -5)
|
||||
elif chr_key == 'r':
|
||||
cfg.add_motion_blur_power(1 if not shift_pressed else 5)
|
||||
elif chr_key == 'f':
|
||||
cfg.add_motion_blur_power(-1 if not shift_pressed else -5)
|
||||
elif chr_key == 't':
|
||||
cfg.add_color_degrade_power(1 if not shift_pressed else 5)
|
||||
elif chr_key == 'g':
|
||||
cfg.add_color_degrade_power(-1 if not shift_pressed else -5)
|
||||
elif chr_key == 'y':
|
||||
cfg.add_output_face_scale(1 if not shift_pressed else 5)
|
||||
elif chr_key == 'h':
|
||||
cfg.add_output_face_scale(-1 if not shift_pressed else -5)
|
||||
|
||||
elif chr_key == 'z':
|
||||
cfg.toggle_masked_hist_match()
|
||||
elif chr_key == 'x':
|
||||
cfg.toggle_mask_mode()
|
||||
elif chr_key == 'c':
|
||||
cfg.toggle_color_transfer_mode()
|
||||
elif chr_key == 'v':
|
||||
cfg.toggle_super_resolution()
|
||||
elif chr_key == 'b':
|
||||
cfg.toggle_export_mask_alpha()
|
||||
else:
|
||||
if chr_key == 's':
|
||||
cfg.toggle_add_source_image()
|
||||
|
||||
if prev_cfg != cfg:
|
||||
io.log_info (cfg)
|
||||
cur_frame.is_done = False
|
||||
cur_frame.is_shown = False
|
||||
else:
|
||||
if chr_key == ',' or chr_key == 'm':
|
||||
self.process_remain_frames = False
|
||||
go_prev_frame = True
|
||||
go_prev_frame_overriding_cfg = chr_key == 'm'
|
||||
elif chr_key == '.' or chr_key == '/':
|
||||
self.process_remain_frames = False
|
||||
go_next_frame = True
|
||||
go_next_frame_overriding_cfg = chr_key == '/'
|
||||
elif chr_key == '\r' or chr_key == '\n':
|
||||
self.process_remain_frames = not self.process_remain_frames
|
||||
elif chr_key == '-':
|
||||
self.screen_manager.get_current().diff_scale(-0.1)
|
||||
elif chr_key == '=':
|
||||
self.screen_manager.get_current().diff_scale(0.1)
|
||||
|
||||
|
||||
if go_prev_frame:
|
||||
if cur_frame is not None and cur_frame.is_done:
|
||||
cur_frame.image = None
|
||||
|
||||
|
||||
if len(self.frames_done_idxs) > 0:
|
||||
prev_frame = self.frames[self.frames_done_idxs.pop()]
|
||||
self.frames_idxs.insert(0, prev_frame.idx)
|
||||
prev_frame.is_shown = False
|
||||
io.progress_bar_inc(-1)
|
||||
|
||||
if go_prev_frame_overriding_cfg:
|
||||
if cur_frame is not None:
|
||||
if prev_frame.cfg != cur_frame.cfg:
|
||||
prev_frame.cfg = cur_frame.cfg
|
||||
prev_frame.is_done = False
|
||||
|
||||
elif go_next_frame:
|
||||
if cur_frame is not None and cur_frame.is_done:
|
||||
cur_frame.image = None
|
||||
cur_frame.is_shown = True
|
||||
self.frames_done_idxs.append(cur_frame.idx)
|
||||
self.frames_idxs.pop(0)
|
||||
io.progress_bar_inc(1)
|
||||
|
||||
if len(self.frames_idxs) != 0:
|
||||
next_frame = self.frames[ self.frames_idxs[0] ]
|
||||
|
||||
if go_next_frame_overriding_cfg:
|
||||
f = self.frames
|
||||
for i in range( next_frame.idx, len(self.frames) ):
|
||||
f[i].cfg = None
|
||||
f[i].is_shown = False
|
||||
|
||||
if next_frame.cfg is None or next_frame.is_shown == False: #next frame is never shown or override current cfg to next frames and the prefetches
|
||||
for i in range( min(len(self.frames_idxs), self.prefetch_frame_count) ):
|
||||
frame = self.frames[ self.frames_idxs[i] ]
|
||||
|
||||
if frame.cfg is None or frame.cfg != cur_frame.cfg:
|
||||
frame.cfg = cur_frame.cfg.copy()
|
||||
frame.is_done = False #initiate solve again
|
||||
|
||||
|
||||
next_frame.is_shown = False
|
||||
|
||||
if len(self.frames_idxs) == 0:
|
||||
self.process_remain_frames = False
|
||||
|
||||
return (self.is_interactive and self.is_interactive_quitting) or \
|
||||
(not self.is_interactive and self.process_remain_frames == False)
|
||||
|
||||
|
||||
#override
|
||||
def on_data_return (self, host_dict, pf):
|
||||
frame = self.frames[pf.idx]
|
||||
frame.is_done = False
|
||||
frame.is_processing = False
|
||||
|
||||
#override
|
||||
def on_result (self, host_dict, pf_sent, pf_result):
|
||||
frame = self.frames[pf_result.idx]
|
||||
frame.is_processing = False
|
||||
if frame.cfg == pf_result.cfg:
|
||||
frame.is_done = True
|
||||
frame.image = pf_result.image
|
||||
|
||||
#override
|
||||
def get_data(self, host_dict):
|
||||
if len (self.input_data_idxs) > 0:
|
||||
idx = self.input_data_idxs.pop(0)
|
||||
return (idx, )
|
||||
if self.is_interactive and self.is_interactive_quitting:
|
||||
return None
|
||||
|
||||
for i in range ( min(len(self.frames_idxs), self.prefetch_frame_count) ):
|
||||
frame = self.frames[ self.frames_idxs[i] ]
|
||||
|
||||
if not frame.is_done and not frame.is_processing and frame.cfg is not None:
|
||||
frame.is_processing = True
|
||||
return ConvertSubprocessor.ProcessingFrame(idx=frame.idx,
|
||||
cfg=frame.cfg.copy(),
|
||||
prev_temporal_frame_infos=frame.prev_temporal_frame_infos,
|
||||
frame_info=frame.frame_info,
|
||||
next_temporal_frame_infos=frame.next_temporal_frame_infos,
|
||||
output_filename=frame.output_filename,
|
||||
need_return_image=True )
|
||||
|
||||
return None
|
||||
|
||||
#override
|
||||
def on_data_return (self, host_dict, data):
|
||||
idx, = data
|
||||
self.input_data_idxs.insert(0, idx)
|
||||
|
||||
#override
|
||||
def on_result (self, host_dict, data, result):
|
||||
if result[0] == 0:
|
||||
self.files_processed += result[0]
|
||||
self.faces_processed += result[1]
|
||||
elif result[0] == 1:
|
||||
for img in result[1]:
|
||||
io.show_image ('Debug convert', (img*255).astype(np.uint8) )
|
||||
io.wait_any_key()
|
||||
io.progress_bar_inc(1)
|
||||
|
||||
#override
|
||||
def on_tick(self):
|
||||
self.converter.on_host_tick()
|
||||
|
||||
#override
|
||||
def get_result(self):
|
||||
return self.files_processed, self.faces_processed
|
||||
return 0
|
||||
|
||||
def main (args, device_args):
|
||||
io.log_info ("Running converter.\r\n")
|
||||
|
@ -244,14 +476,19 @@ def main (args, device_args):
|
|||
io.log_err('Model directory not found. Please ensure it exists.')
|
||||
return
|
||||
|
||||
is_interactive = io.input_bool ("Use interactive converter? (y/n skip:y) : ", True) if not io.is_colab() else False
|
||||
|
||||
import models
|
||||
model = models.import_model( args['model_name'] )(model_path, device_args=device_args)
|
||||
converter = model.get_converter()
|
||||
|
||||
cfg = model.get_ConverterConfig()
|
||||
|
||||
if not is_interactive:
|
||||
cfg.ask_settings()
|
||||
|
||||
input_path_image_paths = Path_utils.get_image_paths(input_path)
|
||||
|
||||
alignments = None
|
||||
if converter.type == Converter.TYPE_FACE:
|
||||
if cfg.type == ConverterConfig.TYPE_MASKED:
|
||||
if aligned_dir is None:
|
||||
io.log_err('Aligned directory not found. Please ensure it exists.')
|
||||
return
|
||||
|
@ -262,7 +499,7 @@ def main (args, device_args):
|
|||
return
|
||||
|
||||
alignments = {}
|
||||
|
||||
multiple_faces_detected = False
|
||||
aligned_path_image_paths = Path_utils.get_image_paths(aligned_path)
|
||||
for filepath in io.progress_bar_generator(aligned_path_image_paths, "Collecting alignments"):
|
||||
filepath = Path(filepath)
|
||||
|
@ -282,13 +519,52 @@ def main (args, device_args):
|
|||
if source_filename_stem not in alignments.keys():
|
||||
alignments[ source_filename_stem ] = []
|
||||
|
||||
alignments[ source_filename_stem ].append (dflimg.get_source_landmarks())
|
||||
#avatar_alignments += [ ( str(filepath), dflimg.get_source_landmarks(), dflimg.get_source_filename() ) ]
|
||||
alignments_ar = alignments[ source_filename_stem ]
|
||||
alignments_ar.append (dflimg.get_source_landmarks())
|
||||
if len(alignments_ar) > 1:
|
||||
multiple_faces_detected = True
|
||||
|
||||
input_data = [ (p,) for p in input_path_image_paths ]
|
||||
elif converter.type == Converter.TYPE_FACE_AVATAR:
|
||||
if multiple_faces_detected:
|
||||
io.log_info ("Warning: multiple faces detected. Strongly recommended to process them separately.")
|
||||
|
||||
input_data = []
|
||||
frames = [ ConvertSubprocessor.Frame( frame_info=FrameInfo(filename=p, landmarks_list=alignments.get(Path(p).stem, None))) for p in input_path_image_paths ]
|
||||
|
||||
if multiple_faces_detected:
|
||||
io.log_info ("Warning: multiple faces detected. Motion blur will not be used.")
|
||||
else:
|
||||
s = 256
|
||||
local_pts = [ (s//2-1, s//2-1), (s//2-1,0) ] #center+up
|
||||
frames_len = len(frames)
|
||||
for i in io.progress_bar_generator( range(len(frames)) , "Computing motion vectors"):
|
||||
fi_prev = frames[max(0, i-1)].frame_info
|
||||
fi = frames[i].frame_info
|
||||
fi_next = frames[min(i+1, frames_len-1)].frame_info
|
||||
if len(fi_prev.landmarks_list) == 0 or \
|
||||
len(fi.landmarks_list) == 0 or \
|
||||
len(fi_next.landmarks_list) == 0:
|
||||
continue
|
||||
|
||||
mat_prev = LandmarksProcessor.get_transform_mat ( fi_prev.landmarks_list[0], s, face_type=FaceType.FULL)
|
||||
mat = LandmarksProcessor.get_transform_mat ( fi.landmarks_list[0] , s, face_type=FaceType.FULL)
|
||||
mat_next = LandmarksProcessor.get_transform_mat ( fi_next.landmarks_list[0], s, face_type=FaceType.FULL)
|
||||
|
||||
pts_prev = LandmarksProcessor.transform_points (local_pts, mat_prev, True)
|
||||
pts = LandmarksProcessor.transform_points (local_pts, mat, True)
|
||||
pts_next = LandmarksProcessor.transform_points (local_pts, mat_next, True)
|
||||
|
||||
prev_vector = pts[0]-pts_prev[0]
|
||||
next_vector = pts_next[0]-pts[0]
|
||||
|
||||
motion_vector = pts_next[0] - pts_prev[0]
|
||||
fi.motion_power = npla.norm(motion_vector)
|
||||
|
||||
motion_vector = motion_vector / fi.motion_power if fi.motion_power != 0 else np.array([0,0],dtype=np.float32)
|
||||
|
||||
fi.motion_deg = -math.atan2(motion_vector[1],motion_vector[0])*180 / math.pi
|
||||
|
||||
|
||||
elif cfg.type == ConverterConfig.TYPE_FACE_AVATAR:
|
||||
filesdata = []
|
||||
for filepath in io.progress_bar_generator(input_path_image_paths, "Collecting info"):
|
||||
filepath = Path(filepath)
|
||||
|
||||
|
@ -302,18 +578,36 @@ def main (args, device_args):
|
|||
if dflimg is None:
|
||||
io.log_err ("%s is not a dfl image file" % (filepath.name) )
|
||||
continue
|
||||
input_data += [ ( str(filepath), dflimg.get_landmarks(), dflimg.get_source_filename() ) ]
|
||||
filesdata += [ ( FrameInfo(filename=str(filepath), landmarks_list=[dflimg.get_landmarks()] ), dflimg.get_source_filename() ) ]
|
||||
|
||||
input_data = sorted(input_data, key=operator.itemgetter(2))
|
||||
filesdata = sorted(filesdata, key=operator.itemgetter(1)) #sort by filename
|
||||
frames = []
|
||||
filesdata_len = len(filesdata)
|
||||
for i in range(len(filesdata)):
|
||||
frame_info = filesdata[i][0]
|
||||
|
||||
prev_temporal_frame_infos = []
|
||||
next_temporal_frame_infos = []
|
||||
|
||||
for t in range (cfg.temporal_face_count):
|
||||
prev_frame_info = filesdata[ max(i -t, 0) ][0]
|
||||
next_frame_info = filesdata[ min(i +t, filesdata_len-1 )][0]
|
||||
|
||||
prev_temporal_frame_infos.insert (0, prev_frame_info )
|
||||
next_temporal_frame_infos.append ( next_frame_info )
|
||||
|
||||
frames.append ( ConvertSubprocessor.Frame(prev_temporal_frame_infos=prev_temporal_frame_infos,
|
||||
frame_info=frame_info,
|
||||
next_temporal_frame_infos=next_temporal_frame_infos) )
|
||||
|
||||
if len(frames) == 0:
|
||||
io.log_info ("No frames to convert in input_dir.")
|
||||
else:
|
||||
input_data = [ (p,) for p in input_path_image_paths ]
|
||||
|
||||
files_processed, faces_processed = ConvertSubprocessor (
|
||||
converter = converter,
|
||||
input_data = input_data,
|
||||
output_path = output_path,
|
||||
alignments = alignments,
|
||||
debug = args.get('debug',False)
|
||||
ConvertSubprocessor (
|
||||
is_interactive = is_interactive,
|
||||
converter_config = cfg,
|
||||
frames = frames,
|
||||
output_path = output_path,
|
||||
).run()
|
||||
|
||||
model.finalize()
|
||||
|
@ -322,29 +616,6 @@ def main (args, device_args):
|
|||
print ( 'Error: %s' % (str(e)))
|
||||
traceback.print_exc()
|
||||
|
||||
'''
|
||||
if model_name == 'AVATAR':
|
||||
output_path_image_paths = Path_utils.get_image_paths(output_path)
|
||||
|
||||
last_ok_frame = -1
|
||||
for filename in output_path_image_paths:
|
||||
filename_path = Path(filename)
|
||||
stem = Path(filename).stem
|
||||
try:
|
||||
frame = int(stem)
|
||||
except:
|
||||
raise Exception ('Aligned avatars must be created from indexed sequence files.')
|
||||
|
||||
if frame-last_ok_frame > 1:
|
||||
start = last_ok_frame + 1
|
||||
end = frame - 1
|
||||
|
||||
print ("Filling gaps: [%d...%d]" % (start, end) )
|
||||
for i in range (start, end+1):
|
||||
shutil.copy ( str(filename), str( output_path / ('%.5d%s' % (i, filename_path.suffix )) ) )
|
||||
|
||||
last_ok_frame = frame
|
||||
'''
|
||||
#interpolate landmarks
|
||||
#from facelib import LandmarksProcessor
|
||||
#from facelib import FaceType
|
||||
|
@ -376,29 +647,3 @@ if model_name == 'AVATAR':
|
|||
# new_points = np.concatenate( [np.expand_dims(p1,-1),np.expand_dims(p2,-1)], -1 )
|
||||
#
|
||||
# alignments[ a[i] ][0] = LandmarksProcessor.transform_points (new_points, m0, True).astype(np.int32)
|
||||
|
||||
"""
|
||||
elif self.converter.type == Converter.TYPE_IMAGE_WITH_LANDMARKS:
|
||||
#currently unused
|
||||
if filename_path.suffix == '.png':
|
||||
dflimg = DFLPNG.load( str(filename_path) )
|
||||
elif filename_path.suffix == '.jpg':
|
||||
dflimg = DFLJPG.load ( str(filename_path) )
|
||||
else:
|
||||
dflimg = None
|
||||
|
||||
if dflimg is not None:
|
||||
image_landmarks = dflimg.get_landmarks()
|
||||
|
||||
image = self.converter.convert_image(image, image_landmarks, self.debug)
|
||||
|
||||
if self.debug:
|
||||
raise NotImplementedError
|
||||
#for img in image:
|
||||
# io.show_image ('Debug convert', img )
|
||||
# cv2.waitKey(0)
|
||||
faces_processed = 1
|
||||
else:
|
||||
self.log_err ("%s is not a dfl image file" % (filename_path.name) )
|
||||
|
||||
"""
|
138
mainscripts/ConverterScreen/ConverterScreen.py
Normal file
138
mainscripts/ConverterScreen/ConverterScreen.py
Normal file
|
@ -0,0 +1,138 @@
|
|||
import math
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
import imagelib
|
||||
from interact import interact as io
|
||||
from utils.cv2_utils import *
|
||||
from utils.os_utils import get_screen_size
|
||||
|
||||
|
||||
class ScreenAssets(object):
|
||||
waiting_icon_image = cv2_imread ( str(Path(__file__).parent / 'gfx' / 'sand_clock_64.png') )
|
||||
|
||||
@staticmethod
|
||||
def build_checkerboard_a( sh, size=5):
|
||||
h,w = sh[0], sh[1]
|
||||
tile = np.array([[0,1],[1,0]]).repeat(size, axis=0).repeat(size, axis=1)
|
||||
grid = np.tile(tile,(int(math.ceil((h+0.0)/(2*size))),int(math.ceil((w+0.0)/(2*size)))))
|
||||
return grid[:h,:w,None]
|
||||
|
||||
class Screen(object):
|
||||
def __init__(self, initial_scale_to_width=0, initial_scale_to_height=0, image=None, waiting_icon=False, **kwargs):
|
||||
self.initial_scale_to_width = initial_scale_to_width
|
||||
self.initial_scale_to_height = initial_scale_to_height
|
||||
self.image = image
|
||||
self.waiting_icon = waiting_icon
|
||||
|
||||
self.state = -1
|
||||
self.scale = 1
|
||||
self.force_update = True
|
||||
self.is_first_appear = True
|
||||
|
||||
self.last_screen_shape = (480,640,3)
|
||||
self.checkerboard_image = None
|
||||
self.set_image (image)
|
||||
self.scrn_manager = None
|
||||
|
||||
def set_waiting_icon(self, b):
|
||||
self.waiting_icon = b
|
||||
|
||||
def set_image(self, img):
|
||||
if not img is self.image:
|
||||
self.force_update = True
|
||||
|
||||
self.image = img
|
||||
|
||||
if self.image is not None:
|
||||
self.last_screen_shape = self.image.shape
|
||||
|
||||
if self.initial_scale_to_width != 0:
|
||||
if self.last_screen_shape[1] > self.initial_scale_to_width:
|
||||
self.scale = self.initial_scale_to_width / self.last_screen_shape[1]
|
||||
self.force_update = True
|
||||
self.initial_scale_to_width = 0
|
||||
|
||||
elif self.initial_scale_to_height != 0:
|
||||
if self.last_screen_shape[0] > self.initial_scale_to_height:
|
||||
self.scale = self.initial_scale_to_height / self.last_screen_shape[0]
|
||||
self.force_update = True
|
||||
self.initial_scale_to_height = 0
|
||||
|
||||
|
||||
def diff_scale(self, diff):
|
||||
self.scale = np.clip (self.scale + diff, 0.1, 4.0)
|
||||
self.force_update = True
|
||||
|
||||
def show(self, force=False):
|
||||
new_state = 0 | self.waiting_icon
|
||||
|
||||
if self.state != new_state or self.force_update or force:
|
||||
self.state = new_state
|
||||
self.force_update = False
|
||||
|
||||
if self.image is None:
|
||||
screen = np.zeros ( self.last_screen_shape, dtype=np.uint8 )
|
||||
else:
|
||||
screen = self.image.copy()
|
||||
|
||||
if self.waiting_icon:
|
||||
imagelib.overlay_alpha_image (screen, ScreenAssets.waiting_icon_image, (0,0) )
|
||||
|
||||
h,w,c = screen.shape
|
||||
if self.scale != 1.0:
|
||||
screen = cv2.resize ( screen, ( int(w*self.scale), int(h*self.scale) ) )
|
||||
|
||||
if c == 4:
|
||||
if self.checkerboard_image is None or self.checkerboard_image.shape[0:2] != screen.shape[0:2]:
|
||||
self.checkerboard_image = ScreenAssets.build_checkerboard_a(screen.shape)
|
||||
|
||||
screen = screen[...,0:3]*0.75 + 64*self.checkerboard_image*(1- (screen[...,3:4].astype(np.float32)/255.0) )
|
||||
screen = screen.astype(np.uint8)
|
||||
|
||||
io.show_image(self.scrn_manager.wnd_name, screen)
|
||||
|
||||
if self.is_first_appear:
|
||||
self.is_first_appear = False
|
||||
#center window
|
||||
desktop_w, desktop_h = get_screen_size()
|
||||
h,w,c = screen.shape
|
||||
cv2.moveWindow(self.scrn_manager.wnd_name, max(0,(desktop_w-w) // 2), max(0, (desktop_h-h) // 2) )
|
||||
|
||||
io.process_messages(0.0001)
|
||||
|
||||
class ScreenManager(object):
|
||||
def __init__(self, window_name="ScreenManager", screens=None, capture_keys=False ):
|
||||
self.screens = screens or []
|
||||
self.current_screen_id = 0
|
||||
|
||||
if self.screens is not None:
|
||||
for screen in self.screens:
|
||||
screen.scrn_manager = self
|
||||
|
||||
self.wnd_name = window_name
|
||||
io.named_window(self.wnd_name)
|
||||
|
||||
|
||||
if capture_keys:
|
||||
io.capture_keys(self.wnd_name)
|
||||
|
||||
def finalize(self):
|
||||
io.destroy_all_windows()
|
||||
|
||||
def get_key_events(self):
|
||||
return io.get_key_events(self.wnd_name)
|
||||
|
||||
def switch_screens(self):
|
||||
self.current_screen_id = (self.current_screen_id + 1) % len(self.screens)
|
||||
self.screens[self.current_screen_id].show(force=True)
|
||||
|
||||
def show_current(self):
|
||||
self.screens[self.current_screen_id].show()
|
||||
|
||||
def get_current(self):
|
||||
return self.screens[self.current_screen_id]
|
||||
|
||||
def set_current(self, screen):
|
||||
self.current_screen_id = self.screens.index(screen)
|
1
mainscripts/ConverterScreen/__init__.py
Normal file
1
mainscripts/ConverterScreen/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .ConverterScreen import Screen, ScreenManager
|
BIN
mainscripts/ConverterScreen/gfx/sand_clock_64.png
Normal file
BIN
mainscripts/ConverterScreen/gfx/sand_clock_64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
mainscripts/gfx/help_converter_face_avatar.jpg
Normal file
BIN
mainscripts/gfx/help_converter_face_avatar.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 KiB |
BIN
mainscripts/gfx/help_converter_face_avatar_source.psd
Normal file
BIN
mainscripts/gfx/help_converter_face_avatar_source.psd
Normal file
Binary file not shown.
BIN
mainscripts/gfx/help_converter_masked.jpg
Normal file
BIN
mainscripts/gfx/help_converter_masked.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 270 KiB |
BIN
mainscripts/gfx/help_converter_masked_source.psd
Normal file
BIN
mainscripts/gfx/help_converter_masked_source.psd
Normal file
Binary file not shown.
|
@ -323,6 +323,11 @@ class ModelBase(object):
|
|||
def get_model_filename_list(self):
|
||||
return []
|
||||
|
||||
#overridable
|
||||
def get_ConverterConfig(self):
|
||||
#return ConverterConfig() for the model
|
||||
raise NotImplementedError
|
||||
|
||||
#overridable
|
||||
def get_converter(self):
|
||||
raise NotImplementedError
|
||||
|
|
737
models/Model_AVATAR/Model.py
Normal file
737
models/Model_AVATAR/Model.py
Normal file
|
@ -0,0 +1,737 @@
|
|||
from functools import partial
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from facelib import FaceType
|
||||
from interact import interact as io
|
||||
from mathlib import get_power_of_two
|
||||
from models import ModelBase
|
||||
from nnlib import nnlib
|
||||
from samplelib import *
|
||||
|
||||
from facelib import PoseEstimator
|
||||
|
||||
class AVATARModel(ModelBase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs,
|
||||
ask_sort_by_yaw=False,
|
||||
ask_random_flip=False,
|
||||
ask_src_scale_mod=False)
|
||||
|
||||
#override
|
||||
def onInitializeOptions(self, is_first_run, ask_override):
|
||||
if is_first_run:
|
||||
avatar_type = io.input_int("Avatar type ( 0:source, 1:head, 2:full_face ?:help skip:1) : ", 1, [0,1,2],
|
||||
help_message="Training target for the model. Source is direct untouched images. Full_face or head are centered nose unaligned faces.")
|
||||
avatar_type = {0:'source',
|
||||
1:'head',
|
||||
2:'full_face'}[avatar_type]
|
||||
|
||||
self.options['avatar_type'] = avatar_type
|
||||
else:
|
||||
self.options['avatar_type'] = self.options.get('avatar_type', 'head')
|
||||
|
||||
if is_first_run or ask_override:
|
||||
def_stage = self.options.get('stage', 1)
|
||||
self.options['stage'] = io.input_int("Stage (0, 1, 2 ?:help skip:%d) : " % def_stage, def_stage, [0,1,2], help_message="Train first stage, then second. Tune batch size to maximum possible for both stages.")
|
||||
else:
|
||||
self.options['stage'] = self.options.get('stage', 1)
|
||||
|
||||
#override
|
||||
def onInitialize(self, batch_size=-1, **in_options):
|
||||
exec(nnlib.code_import_all, locals(), globals())
|
||||
self.set_vram_batch_requirements({6:4})
|
||||
AVATARModel.initialize_nn_functions()
|
||||
|
||||
resolution = self.resolution = 224
|
||||
avatar_type = self.options['avatar_type']
|
||||
stage = self.stage = self.options['stage']
|
||||
df_res = self.df_res = 128
|
||||
df_bgr_shape = (df_res, df_res, 3)
|
||||
df_mask_shape = (df_res, df_res, 1)
|
||||
res_bgr_shape = (resolution, resolution, 3)
|
||||
res_bgr_t_shape = (resolution, resolution, 9)
|
||||
|
||||
self.enc = modelify(AVATARModel.EncFlow())( [Input(df_bgr_shape),] )
|
||||
|
||||
self.decA64 = modelify(AVATARModel.DecFlow()) ( [ Input(K.int_shape(self.enc.outputs[0])[1:]) ] )
|
||||
self.decB64 = modelify(AVATARModel.DecFlow()) ( [ Input(K.int_shape(self.enc.outputs[0])[1:]) ] )
|
||||
self.D = modelify(AVATARModel.Discriminator() ) (Input(df_bgr_shape))
|
||||
self.C = modelify(AVATARModel.ResNet (9, use_batch_norm=False, n_blocks=6, ngf=128, use_dropout=False))( Input(res_bgr_t_shape))
|
||||
#self.CD = modelify(AVATARModel.CDiscriminator() ) (Input(res_bgr_t_shape))
|
||||
|
||||
if self.is_first_run():
|
||||
conv_weights_list = []
|
||||
for model, _ in self.get_model_filename_list():
|
||||
for layer in model.layers:
|
||||
if type(layer) == keras.layers.Conv2D:
|
||||
conv_weights_list += [layer.weights[0]] #Conv2D kernel_weights
|
||||
CAInitializerMP ( conv_weights_list )
|
||||
|
||||
if not self.is_first_run():
|
||||
self.load_weights_safe( self.get_model_filename_list() )
|
||||
|
||||
def DLoss(labels,logits):
|
||||
return K.mean(K.binary_crossentropy(labels,logits))
|
||||
|
||||
warped_A64 = Input(df_bgr_shape)
|
||||
real_A64 = Input(df_bgr_shape)
|
||||
real_A64m = Input(df_mask_shape)
|
||||
|
||||
real_B64_t0 = Input(df_bgr_shape)
|
||||
real_B64_t1 = Input(df_bgr_shape)
|
||||
real_B64_t2 = Input(df_bgr_shape)
|
||||
|
||||
real_A64_t0 = Input(df_bgr_shape)
|
||||
real_A64m_t0 = Input(df_mask_shape)
|
||||
real_A_t0 = Input(res_bgr_shape)
|
||||
real_A64_t1 = Input(df_bgr_shape)
|
||||
real_A64m_t1 = Input(df_mask_shape)
|
||||
real_A_t1 = Input(res_bgr_shape)
|
||||
real_A64_t2 = Input(df_bgr_shape)
|
||||
real_A64m_t2 = Input(df_mask_shape)
|
||||
real_A_t2 = Input(res_bgr_shape)
|
||||
|
||||
warped_B64 = Input(df_bgr_shape)
|
||||
real_B64 = Input(df_bgr_shape)
|
||||
real_B64m = Input(df_mask_shape)
|
||||
|
||||
warped_A_code = self.enc (warped_A64)
|
||||
warped_B_code = self.enc (warped_B64)
|
||||
|
||||
rec_A64 = self.decA64(warped_A_code)
|
||||
rec_B64 = self.decB64(warped_B_code)
|
||||
rec_AB64 = self.decA64(warped_B_code)
|
||||
|
||||
def Lambda_grey_mask (x,m):
|
||||
return Lambda (lambda x: x[0]*m+(1-m)*0.5, output_shape= K.int_shape(x)[1:3] + (3,)) ([x, m])
|
||||
|
||||
def Lambda_gray_pad(x):
|
||||
a = np.ones((resolution,resolution,3))*0.5
|
||||
pad = ( resolution - df_res ) // 2
|
||||
a[pad:-pad:,pad:-pad:,:] = 0
|
||||
|
||||
return Lambda ( lambda x: K.spatial_2d_padding(x, padding=((pad, pad), (pad, pad)) ) + K.constant(a, dtype=K.floatx() ),
|
||||
output_shape=(resolution,resolution,3) ) (x)
|
||||
|
||||
def Lambda_concat ( x ):
|
||||
c = sum ( [ K.int_shape(l)[-1] for l in x ] )
|
||||
return Lambda ( lambda x: K.concatenate (x, axis=-1), output_shape=K.int_shape(x[0])[1:3] + (c,) ) (x)
|
||||
|
||||
def Lambda_Cto3t(x):
|
||||
return Lambda ( lambda x: x[...,0:3], output_shape= K.int_shape(x)[1:3] + (3,) ) (x), \
|
||||
Lambda ( lambda x: x[...,3:6], output_shape= K.int_shape(x)[1:3] + (3,) ) (x), \
|
||||
Lambda ( lambda x: x[...,6:9], output_shape= K.int_shape(x)[1:3] + (3,) ) (x)
|
||||
|
||||
real_A64_d = self.D( Lambda_grey_mask(real_A64, real_A64m) )
|
||||
|
||||
real_A64_d_ones = K.ones_like(real_A64_d)
|
||||
fake_A64_d = self.D(rec_AB64)
|
||||
fake_A64_d_ones = K.ones_like(fake_A64_d)
|
||||
fake_A64_d_zeros = K.zeros_like(fake_A64_d)
|
||||
|
||||
rec_AB_t0 = Lambda_gray_pad( self.decA64 (self.enc (real_B64_t0)) )
|
||||
rec_AB_t1 = Lambda_gray_pad( self.decA64 (self.enc (real_B64_t1)) )
|
||||
rec_AB_t2 = Lambda_gray_pad( self.decA64 (self.enc (real_B64_t2)) )
|
||||
|
||||
C_in_A_t0 = Lambda_gray_pad( Lambda_grey_mask (real_A64_t0, real_A64m_t0) )
|
||||
C_in_A_t1 = Lambda_gray_pad( Lambda_grey_mask (real_A64_t1, real_A64m_t1) )
|
||||
C_in_A_t2 = Lambda_gray_pad( Lambda_grey_mask (real_A64_t2, real_A64m_t2) )
|
||||
|
||||
rec_C_A_t0, rec_C_A_t1, rec_C_A_t2 = Lambda_Cto3t ( self.C ( Lambda_concat ( [C_in_A_t0, C_in_A_t1, C_in_A_t2]) ) )
|
||||
rec_C_AB_t0, rec_C_AB_t1, rec_C_AB_t2 = Lambda_Cto3t( self.C ( Lambda_concat ( [rec_AB_t0, rec_AB_t1, rec_AB_t2]) ) )
|
||||
|
||||
#real_A_t012_d = self.CD ( K.concatenate ( [real_A_t0, real_A_t1,real_A_t2], axis=-1) )
|
||||
#real_A_t012_d_ones = K.ones_like(real_A_t012_d)
|
||||
#rec_C_AB_t012_d = self.CD ( K.concatenate ( [rec_C_AB_t0,rec_C_AB_t1, rec_C_AB_t2], axis=-1) )
|
||||
#rec_C_AB_t012_d_ones = K.ones_like(rec_C_AB_t012_d)
|
||||
#rec_C_AB_t012_d_zeros = K.zeros_like(rec_C_AB_t012_d)
|
||||
|
||||
self.G64_view = K.function([warped_A64, warped_B64],[rec_A64, rec_B64, rec_AB64])
|
||||
self.G_view = K.function([real_A64_t0, real_A64m_t0, real_A64_t1, real_A64m_t1, real_A64_t2, real_A64m_t2, real_B64_t0, real_B64_t1, real_B64_t2], [rec_C_A_t0, rec_C_A_t1, rec_C_A_t2, rec_C_AB_t0, rec_C_AB_t1, rec_C_AB_t2])
|
||||
|
||||
if self.is_training_mode:
|
||||
loss_AB64 = K.mean(10 * dssim(kernel_size=int(df_res/11.6),max_value=1.0) ( rec_A64, real_A64*real_A64m + (1-real_A64m)*0.5) ) + \
|
||||
K.mean(10 * dssim(kernel_size=int(df_res/11.6),max_value=1.0) ( rec_B64, real_B64*real_B64m + (1-real_B64m)*0.5) ) + 0.1*DLoss(fake_A64_d_ones, fake_A64_d )
|
||||
|
||||
weights_AB64 = self.enc.trainable_weights + self.decA64.trainable_weights + self.decB64.trainable_weights
|
||||
|
||||
loss_C = K.mean( 10 * dssim(kernel_size=int(resolution/11.6),max_value=1.0) ( real_A_t0, rec_C_A_t0 ) ) + \
|
||||
K.mean( 10 * dssim(kernel_size=int(resolution/11.6),max_value=1.0) ( real_A_t1, rec_C_A_t1 ) ) + \
|
||||
K.mean( 10 * dssim(kernel_size=int(resolution/11.6),max_value=1.0) ( real_A_t2, rec_C_A_t2 ) )
|
||||
#0.1*DLoss(rec_C_AB_t012_d_ones, rec_C_AB_t012_d )
|
||||
|
||||
weights_C = self.C.trainable_weights
|
||||
|
||||
loss_D = (DLoss(real_A64_d_ones, real_A64_d ) + \
|
||||
DLoss(fake_A64_d_zeros, fake_A64_d ) ) * 0.5
|
||||
|
||||
#loss_CD = ( DLoss(real_A_t012_d_ones, real_A_t012_d) + \
|
||||
# DLoss(rec_C_AB_t012_d_zeros, rec_C_AB_t012_d) ) * 0.5
|
||||
#
|
||||
#weights_CD = self.CD.trainable_weights
|
||||
|
||||
def opt(lr=5e-5):
|
||||
return Adam(lr=lr, beta_1=0.5, beta_2=0.999, tf_cpu_mode=2 if 'tensorflow' in self.device_config.backend else 0 )
|
||||
|
||||
self.AB64_train = K.function ([warped_A64, real_A64, real_A64m, warped_B64, real_B64, real_B64m], [loss_AB64], opt().get_updates(loss_AB64, weights_AB64) )
|
||||
self.C_train = K.function ([real_A64_t0, real_A64m_t0, real_A_t0,
|
||||
real_A64_t1, real_A64m_t1, real_A_t1,
|
||||
real_A64_t2, real_A64m_t2, real_A_t2,
|
||||
real_B64_t0, real_B64_t1, real_B64_t2],[ loss_C ], opt().get_updates(loss_C, weights_C) )
|
||||
|
||||
self.D_train = K.function ([warped_A64, real_A64, real_A64m, warped_B64, real_B64, real_B64m],[loss_D], opt().get_updates(loss_D, self.D.trainable_weights) )
|
||||
|
||||
|
||||
#self.CD_train = K.function ([real_A64_t0, real_A64m_t0, real_A_t0,
|
||||
# real_A64_t1, real_A64m_t1, real_A_t1,
|
||||
# real_A64_t2, real_A64m_t2, real_A_t2,
|
||||
# real_B64_t0, real_B64_t1, real_B64_t2 ],[ loss_CD ], opt().get_updates(loss_CD, weights_CD) )
|
||||
|
||||
###########
|
||||
t = SampleProcessor.Types
|
||||
|
||||
training_target = {'source' : t.NONE,
|
||||
'full_face' : t.FACE_TYPE_FULL_NO_ALIGN,
|
||||
'head' : t.FACE_TYPE_HEAD_NO_ALIGN}[avatar_type]
|
||||
|
||||
generators = [
|
||||
SampleGeneratorFace(self.training_data_src_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||
sample_process_options=SampleProcessor.Options(random_flip=False),
|
||||
output_sample_types=[ {'types': (t.IMG_WARPED_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_BGR), 'resolution':df_res},
|
||||
{'types': (t.IMG_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_BGR), 'resolution':df_res},
|
||||
{'types': (t.IMG_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_M), 'resolution':df_res}
|
||||
] ),
|
||||
SampleGeneratorFace(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||
sample_process_options=SampleProcessor.Options(random_flip=False),
|
||||
output_sample_types=[ {'types': (t.IMG_WARPED_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_BGR), 'resolution':df_res},
|
||||
{'types': (t.IMG_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_BGR), 'resolution':df_res},
|
||||
{'types': (t.IMG_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_M), 'resolution':df_res}
|
||||
] ),
|
||||
|
||||
SampleGeneratorFaceTemporal(self.training_data_src_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||
temporal_image_count=3,
|
||||
sample_process_options=SampleProcessor.Options(random_flip=False),
|
||||
output_sample_types=[{'types': (t.IMG_WARPED_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_BGR), 'resolution':df_res},#IMG_WARPED_TRANSFORMED
|
||||
{'types': (t.IMG_WARPED_TRANSFORMED, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_M), 'resolution':df_res},
|
||||
{'types': (t.IMG_SOURCE, training_target, t.MODE_BGR), 'resolution':resolution},
|
||||
] ),
|
||||
|
||||
SampleGeneratorFaceTemporal(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||
temporal_image_count=3,
|
||||
sample_process_options=SampleProcessor.Options(random_flip=False),
|
||||
output_sample_types=[{'types': (t.IMG_SOURCE, t.FACE_TYPE_FULL_NO_ALIGN, t.MODE_BGR), 'resolution':df_res},
|
||||
{'types': (t.IMG_SOURCE, t.NONE, t.MODE_BGR), 'resolution':resolution},
|
||||
] ),
|
||||
]
|
||||
|
||||
if self.stage == 1:
|
||||
generators[2].set_active(False)
|
||||
generators[3].set_active(False)
|
||||
elif self.stage == 2:
|
||||
generators[0].set_active(False)
|
||||
generators[1].set_active(False)
|
||||
|
||||
self.set_training_data_generators (generators)
|
||||
else:
|
||||
self.G_convert = K.function([real_B64_t0, real_B64_t1, real_B64_t2],[rec_C_AB_t1])
|
||||
|
||||
#override , return [ [model, filename],... ] list
|
||||
def get_model_filename_list(self):
|
||||
return [ [self.enc, 'enc.h5'],
|
||||
[self.decA64, 'decA64.h5'],
|
||||
[self.decB64, 'decB64.h5'],
|
||||
[self.C, 'C.h5'],
|
||||
[self.D, 'D.h5'],
|
||||
#[self.CD, 'CD.h5'],
|
||||
]
|
||||
|
||||
#override
|
||||
def onSave(self):
|
||||
self.save_weights_safe( self.get_model_filename_list() )
|
||||
|
||||
#override
|
||||
def onTrainOneIter(self, generators_samples, generators_list):
|
||||
warped_src64, src64, src64m = generators_samples[0]
|
||||
warped_dst64, dst64, dst64m = generators_samples[1]
|
||||
|
||||
real_A64_t0, real_A64m_t0, real_A_t0, real_A64_t1, real_A64m_t1, real_A_t1, real_A64_t2, real_A64m_t2, real_A_t2 = generators_samples[2]
|
||||
real_B64_t0, _, real_B64_t1, _, real_B64_t2, _ = generators_samples[3]
|
||||
|
||||
if self.stage == 0 or self.stage == 1:
|
||||
loss, = self.AB64_train ( [warped_src64, src64, src64m, warped_dst64, dst64, dst64m] )
|
||||
loss_D, = self.D_train ( [warped_src64, src64, src64m, warped_dst64, dst64, dst64m] )
|
||||
if self.stage != 0:
|
||||
loss_C = loss_CD = 0
|
||||
|
||||
if self.stage == 0 or self.stage == 2:
|
||||
loss_C1, = self.C_train ( [real_A64_t0, real_A64m_t0, real_A_t0,
|
||||
real_A64_t1, real_A64m_t1, real_A_t1,
|
||||
real_A64_t2, real_A64m_t2, real_A_t2,
|
||||
real_B64_t0, real_B64_t1, real_B64_t2] )
|
||||
|
||||
loss_C2, = self.C_train ( [real_A64_t2, real_A64m_t2, real_A_t2,
|
||||
real_A64_t1, real_A64m_t1, real_A_t1,
|
||||
real_A64_t0, real_A64m_t0, real_A_t0,
|
||||
real_B64_t0, real_B64_t1, real_B64_t2] )
|
||||
|
||||
#loss_CD1, = self.CD_train ( [real_A64_t0, real_A64m_t0, real_A_t0,
|
||||
# real_A64_t1, real_A64m_t1, real_A_t1,
|
||||
# real_A64_t2, real_A64m_t2, real_A_t2,
|
||||
# real_B64_t0, real_B64_t1, real_B64_t2] )
|
||||
#
|
||||
#loss_CD2, = self.CD_train ( [real_A64_t2, real_A64m_t2, real_A_t2,
|
||||
# real_A64_t1, real_A64m_t1, real_A_t1,
|
||||
# real_A64_t0, real_A64m_t0, real_A_t0,
|
||||
# real_B64_t0, real_B64_t1, real_B64_t2] )
|
||||
|
||||
loss_C = (loss_C1 + loss_C2) / 2
|
||||
#loss_CD = (loss_CD1 + loss_CD2) / 2
|
||||
if self.stage != 0:
|
||||
loss = loss_D = 0
|
||||
|
||||
return ( ('loss', loss), ('D', loss_D), ('C', loss_C), ) #('CD', loss_CD) )
|
||||
|
||||
#override
|
||||
def onGetPreview(self, sample):
|
||||
test_A064w = sample[0][0][0:4]
|
||||
test_A064r = sample[0][1][0:4]
|
||||
test_A064m = sample[0][2][0:4]
|
||||
|
||||
test_B064w = sample[1][0][0:4]
|
||||
test_B064r = sample[1][1][0:4]
|
||||
test_B064m = sample[1][2][0:4]
|
||||
|
||||
t_src64_0 = sample[2][0][0:4]
|
||||
t_src64m_0 = sample[2][1][0:4]
|
||||
t_src_0 = sample[2][2][0:4]
|
||||
t_src64_1 = sample[2][3][0:4]
|
||||
t_src64m_1 = sample[2][4][0:4]
|
||||
t_src_1 = sample[2][5][0:4]
|
||||
t_src64_2 = sample[2][6][0:4]
|
||||
t_src64m_2 = sample[2][7][0:4]
|
||||
t_src_2 = sample[2][8][0:4]
|
||||
|
||||
t_dst64_0 = sample[3][0][0:4]
|
||||
t_dst_0 = sample[3][1][0:4]
|
||||
t_dst64_1 = sample[3][2][0:4]
|
||||
t_dst_1 = sample[3][3][0:4]
|
||||
t_dst64_2 = sample[3][4][0:4]
|
||||
t_dst_2 = sample[3][5][0:4]
|
||||
|
||||
G64_view_result = self.G64_view ([test_A064r, test_B064r])
|
||||
test_A064r, test_B064r, rec_A64, rec_B64, rec_AB64 = [ x[0] for x in ([test_A064r, test_B064r] + G64_view_result) ]
|
||||
|
||||
sample64x4 = np.concatenate ([ np.concatenate ( [rec_B64, rec_A64], axis=1 ),
|
||||
np.concatenate ( [test_B064r, rec_AB64], axis=1) ], axis=0 )
|
||||
|
||||
sample64x4 = cv2.resize (sample64x4, (self.resolution, self.resolution) )
|
||||
|
||||
G_view_result = self.G_view([t_src64_0, t_src64m_0, t_src64_1, t_src64m_1, t_src64_2, t_src64m_2, t_dst64_0, t_dst64_1, t_dst64_2 ])
|
||||
|
||||
t_dst_0, t_dst_1, t_dst_2, rec_C_A_t0, rec_C_A_t1, rec_C_A_t2, rec_C_AB_t0, rec_C_AB_t1, rec_C_AB_t2 = [ x[0] for x in ([t_dst_0, t_dst_1, t_dst_2, ] + G_view_result) ]
|
||||
|
||||
c1 = np.concatenate ( (sample64x4, rec_C_A_t0, t_dst_0, rec_C_AB_t0 ), axis=1 )
|
||||
c2 = np.concatenate ( (sample64x4, rec_C_A_t1, t_dst_1, rec_C_AB_t1 ), axis=1 )
|
||||
c3 = np.concatenate ( (sample64x4, rec_C_A_t2, t_dst_2, rec_C_AB_t2 ), axis=1 )
|
||||
|
||||
r = np.concatenate ( [c1,c2,c3], axis=0 )
|
||||
|
||||
return [ ('AVATAR', r ) ]
|
||||
|
||||
def predictor_func (self, prev_imgs=None, img=None, next_imgs=None, dummy_predict=False):
|
||||
if dummy_predict:
|
||||
z = np.zeros ( (1, self.df_res, self.df_res, 3), dtype=np.float32 )
|
||||
self.G_convert ([z,z,z])
|
||||
else:
|
||||
feed = [ prev_imgs[-1][np.newaxis,...], img[np.newaxis,...], next_imgs[0][np.newaxis,...] ]
|
||||
x = self.G_convert (feed)[0]
|
||||
return np.clip ( x[0], 0, 1)
|
||||
|
||||
#override
|
||||
def get_ConverterConfig(self):
|
||||
import converters
|
||||
return converters.ConverterConfigFaceAvatar(predictor_func=self.predictor_func,
|
||||
predictor_input_shape=(self.df_res, self.df_res, 3),
|
||||
temporal_face_count=1
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def NLayerDiscriminator(ndf=64, n_layers=3):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
#use_bias = True
|
||||
#def XNormalization(x):
|
||||
# return InstanceNormalization (axis=-1)(x)
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, use_bias=use_bias)
|
||||
|
||||
def func(x):
|
||||
f = ndf
|
||||
|
||||
x = XConv2D( f, 4, strides=2, padding='same', use_bias=True)(x)
|
||||
f = min( ndf*8, f*2 )
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
for i in range(n_layers):
|
||||
x = XConv2D( f, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
f = min( ndf*8, f*2 )
|
||||
|
||||
x = XConv2D( f, 4, strides=1, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
return XConv2D( 1, 4, strides=1, padding='same', use_bias=True, activation='sigmoid')(x)#
|
||||
return func
|
||||
|
||||
"""
|
||||
@staticmethod
|
||||
def Discriminator(ndf=128):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
#use_bias = True
|
||||
#def XNormalization(x):
|
||||
# return InstanceNormalization (axis=-1)(x)
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
b,h,w,c = K.int_shape(input)
|
||||
|
||||
x = input
|
||||
|
||||
x = XConv2D( ndf, 4, strides=2, padding='same', use_bias=True)(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*2, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*4, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*8, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
return XConv2D( 1, 4, strides=1, padding='same', use_bias=True, activation='sigmoid')(x)#
|
||||
return func
|
||||
"""
|
||||
@staticmethod
|
||||
def Discriminator(ndf=128):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
use_bias = True
|
||||
def XNormalization(x):
|
||||
return InstanceNormalization (axis=-1)(x)
|
||||
#use_bias = False
|
||||
#def XNormalization(x):
|
||||
# return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
b,h,w,c = K.int_shape(input)
|
||||
|
||||
x = input
|
||||
|
||||
x = XConv2D( ndf, 4, strides=2, padding='same', use_bias=True)(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*2, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*4, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*8, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
return XConv2D( 1, 4, strides=1, padding='same', use_bias=True, activation='sigmoid')(x)#
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def CDiscriminator(ndf=256):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
use_bias = True
|
||||
def XNormalization(x):
|
||||
return InstanceNormalization (axis=-1)(x)
|
||||
#use_bias = False
|
||||
#def XNormalization(x):
|
||||
# return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
b,h,w,c = K.int_shape(input)
|
||||
|
||||
x = input
|
||||
|
||||
x = XConv2D( ndf, 4, strides=2, padding='same', use_bias=True)(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*2, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = XConv2D( ndf*4, 4, strides=2, padding='same')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
#x = XConv2D( ndf*8, 4, strides=2, padding='same')(x)
|
||||
#x = XNormalization(x)
|
||||
#x = LeakyReLU(0.2)(x)
|
||||
|
||||
return XConv2D( 1, 4, strides=1, padding='same', use_bias=True, activation='sigmoid')(x)#
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def EncFlow(padding='zero', **kwargs):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
use_bias = False
|
||||
def XNorm(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
XConv2D = partial(Conv2D, padding=padding, use_bias=use_bias)
|
||||
|
||||
def downscale (dim):
|
||||
def func(x):
|
||||
return LeakyReLU(0.1)( Conv2D(dim, 5, strides=2, padding='same')(x))
|
||||
return func
|
||||
|
||||
def upscale (dim):
|
||||
def func(x):
|
||||
return SubpixelUpscaler()(LeakyReLU(0.1)(Conv2D(dim * 4, 3, strides=1, padding='same')(x)))
|
||||
return func
|
||||
|
||||
|
||||
def func(input):
|
||||
x, = input
|
||||
b,h,w,c = K.int_shape(x)
|
||||
|
||||
dim_res = w // 16
|
||||
|
||||
x = downscale(64)(x)
|
||||
x = downscale(128)(x)
|
||||
x = downscale(256)(x)
|
||||
x = downscale(512)(x)
|
||||
|
||||
x = Dense(512)(Flatten()(x))
|
||||
x = Dense(dim_res * dim_res * 512)(x)
|
||||
x = Reshape((dim_res, dim_res, 512))(x)
|
||||
x = upscale(512)(x)
|
||||
return x
|
||||
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def DecFlow(output_nc=3, **kwargs):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
ResidualBlock = AVATARModel.ResidualBlock
|
||||
upscale = AVATARModel.upscale
|
||||
to_bgr = AVATARModel.to_bgr
|
||||
|
||||
def func(input):
|
||||
x = input[0]
|
||||
|
||||
x = upscale(512)(x)
|
||||
x = upscale(256)(x)
|
||||
x = upscale(128)(x)
|
||||
return to_bgr(output_nc) (x)
|
||||
|
||||
return func
|
||||
"""
|
||||
@staticmethod
|
||||
def CNet(output_nc, use_batch_norm, ngf=64, n_blocks=6, use_dropout=False):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
if not use_batch_norm:
|
||||
use_bias = True
|
||||
def XNormalization(x):
|
||||
return InstanceNormalization (axis=-1)(x)
|
||||
else:
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, padding='same', use_bias=use_bias)
|
||||
XConv2DTranspose = partial(Conv2DTranspose, padding='same', use_bias=use_bias)
|
||||
|
||||
def ResnetBlock(dim, use_dropout=False):
|
||||
def func(input):
|
||||
x = input
|
||||
|
||||
x = XConv2D(dim, 3, strides=1)(x)
|
||||
x = XNormalization(x)
|
||||
x = ReLU()(x)
|
||||
|
||||
if use_dropout:
|
||||
x = Dropout(0.5)(x)
|
||||
|
||||
x = XConv2D(dim, 3, strides=1)(x)
|
||||
x = XNormalization(x)
|
||||
x = ReLU()(x)
|
||||
return Add()([x,input])
|
||||
return func
|
||||
|
||||
def preprocess(target_res):
|
||||
def func(input):
|
||||
inp_shape = K.int_shape (input[0])
|
||||
t_len = len(input)
|
||||
total_ch = 0
|
||||
for i in range(t_len):
|
||||
total_ch += K.int_shape (input[i])[-1]
|
||||
|
||||
K.concatenate ( input, axis=-1) )
|
||||
import code
|
||||
c ode.interact(local=dict(globals(), **locals()))
|
||||
|
||||
x_shape = K.int_shape(x)[1:]
|
||||
|
||||
pad = (target_res - x_shape[0]) // 2
|
||||
|
||||
a = np.ones((target_res,target_res,3))*0.5
|
||||
a[pad:-pad:,pad:-pad:,:] = 0
|
||||
return K.spatial_2d_padding(x, padding=((pad, pad), (pad, pad)) ) + K.constant(a, dtype=K.floatx() )
|
||||
return func
|
||||
|
||||
def func(input):
|
||||
inp_shape = K.int_shape (input[0])
|
||||
t_len = len(input)
|
||||
total_ch = 0
|
||||
for i in range(t_len):
|
||||
total_ch += K.int_shape (input[i])[-1]
|
||||
|
||||
x = Lambda ( preprocess(128) , output_shape=(inp_shape[1], inp_shape[2], total_ch) ) (input)
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf, 7, strides=1)(x)))
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*2, 3, strides=2)(x)))
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*4, 3, strides=2)(x)))
|
||||
|
||||
for i in range(n_blocks):
|
||||
x = ResnetBlock(ngf*4, use_dropout=use_dropout)(x)
|
||||
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf*2, 3, strides=2)(x)))
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf , 3, strides=2)(x)))
|
||||
|
||||
x = XConv2D(output_nc, 7, strides=1, activation='sigmoid', use_bias=True)(x)
|
||||
|
||||
return x
|
||||
|
||||
return func
|
||||
"""
|
||||
@staticmethod
|
||||
def ResNet(output_nc, use_batch_norm, ngf=64, n_blocks=6, use_dropout=False):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
if not use_batch_norm:
|
||||
use_bias = True
|
||||
def XNormalization(x):
|
||||
return InstanceNormalization (axis=-1)(x)
|
||||
else:
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, padding='same', use_bias=use_bias)
|
||||
XConv2DTranspose = partial(Conv2DTranspose, padding='same', use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
|
||||
|
||||
def ResnetBlock(dim, use_dropout=False):
|
||||
def func(input):
|
||||
x = input
|
||||
|
||||
x = XConv2D(dim, 3, strides=1)(x)
|
||||
x = XNormalization(x)
|
||||
x = ReLU()(x)
|
||||
|
||||
if use_dropout:
|
||||
x = Dropout(0.5)(x)
|
||||
|
||||
x = XConv2D(dim, 3, strides=1)(x)
|
||||
x = XNormalization(x)
|
||||
x = ReLU()(x)
|
||||
return Add()([x,input])
|
||||
return func
|
||||
|
||||
x = input
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf, 7, strides=1)(x)))
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*2, 3, strides=2)(x)))
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*4, 3, strides=2)(x)))
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*4, 3, strides=2)(x)))
|
||||
|
||||
for i in range(n_blocks):
|
||||
x = ResnetBlock(ngf*4, use_dropout=use_dropout)(x)
|
||||
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf*4, 3, strides=2)(x)))
|
||||
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf*2, 3, strides=2)(x)))
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf , 3, strides=2)(x)))
|
||||
|
||||
x = XConv2D(output_nc, 7, strides=1, activation='sigmoid', use_bias=True)(x)
|
||||
|
||||
return x
|
||||
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def initialize_nn_functions():
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
class ResidualBlock(object):
|
||||
def __init__(self, filters, kernel_size=3, padding='zero', **kwargs):
|
||||
self.filters = filters
|
||||
self.kernel_size = kernel_size
|
||||
self.padding = padding
|
||||
|
||||
def __call__(self, inp):
|
||||
x = inp
|
||||
x = Conv2D(self.filters, kernel_size=self.kernel_size, padding=self.padding)(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
x = Conv2D(self.filters, kernel_size=self.kernel_size, padding=self.padding)(x)
|
||||
x = Add()([x, inp])
|
||||
x = LeakyReLU(0.2)(x)
|
||||
return x
|
||||
AVATARModel.ResidualBlock = ResidualBlock
|
||||
|
||||
def downscale (dim, padding='zero', act='', **kwargs):
|
||||
def func(x):
|
||||
return LeakyReLU(0.2) (Conv2D(dim, kernel_size=5, strides=2, padding=padding)(x))
|
||||
return func
|
||||
AVATARModel.downscale = downscale
|
||||
|
||||
def upscale (dim, padding='zero', norm='', act='', **kwargs):
|
||||
def func(x):
|
||||
return SubpixelUpscaler()( LeakyReLU(0.2)(Conv2D(dim * 4, kernel_size=3, strides=1, padding=padding)(x)))
|
||||
return func
|
||||
AVATARModel.upscale = upscale
|
||||
|
||||
def to_bgr (output_nc, padding='zero', **kwargs):
|
||||
def func(x):
|
||||
return Conv2D(output_nc, kernel_size=5, padding=padding, activation='sigmoid')(x)
|
||||
return func
|
||||
AVATARModel.to_bgr = to_bgr
|
||||
|
||||
Model = AVATARModel
|
|
@ -111,18 +111,26 @@ class Model(ModelBase):
|
|||
|
||||
return [ ('DF', np.concatenate ( st, axis=0 ) ) ]
|
||||
|
||||
def predictor_func (self, face):
|
||||
x, mx = self.convert ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
def predictor_func (self, face=None, dummy_predict=False):
|
||||
if dummy_predict:
|
||||
self.convert ([ np.zeros ( (1, 128, 128, 3), dtype=np.float32 ) ])
|
||||
else:
|
||||
x, mx = self.convert ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
|
||||
#override
|
||||
def get_converter(self):
|
||||
from converters import ConverterMasked
|
||||
return ConverterMasked(self.predictor_func,
|
||||
predictor_input_size=128,
|
||||
face_type=FaceType.FULL,
|
||||
base_erode_mask_modifier=30,
|
||||
base_blur_mask_modifier=0)
|
||||
def get_ConverterConfig(self):
|
||||
import converters
|
||||
return converters.ConverterConfigMasked(predictor_func=self.predictor_func,
|
||||
predictor_input_shape=(128,128,3),
|
||||
predictor_masked=True,
|
||||
face_type=FaceType.FULL,
|
||||
default_mode=4,
|
||||
base_erode_mask_modifier=30,
|
||||
base_blur_mask_modifier=0,
|
||||
default_erode_mask_modifier=0,
|
||||
default_blur_mask_modifier=0,
|
||||
)
|
||||
|
||||
def Build(self, input_layer):
|
||||
exec(nnlib.code_import_all, locals(), globals())
|
||||
|
|
|
@ -119,18 +119,26 @@ class Model(ModelBase):
|
|||
|
||||
return [ ('H128', np.concatenate ( st, axis=0 ) ) ]
|
||||
|
||||
def predictor_func (self, face):
|
||||
x, mx = self.src_view ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
def predictor_func (self, face=None, dummy_predict=False):
|
||||
if dummy_predict:
|
||||
self.src_view ([ np.zeros ( (1, 128, 128, 3), dtype=np.float32 ) ])
|
||||
else:
|
||||
x, mx = self.src_view ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
|
||||
#override
|
||||
def get_converter(self):
|
||||
from converters import ConverterMasked
|
||||
return ConverterMasked(self.predictor_func,
|
||||
predictor_input_size=128,
|
||||
face_type=FaceType.HALF,
|
||||
base_erode_mask_modifier=100,
|
||||
base_blur_mask_modifier=100)
|
||||
def get_ConverterConfig(self):
|
||||
import converters
|
||||
return converters.ConverterConfigMasked(predictor_func=self.predictor_func,
|
||||
predictor_input_shape=(128,128,3),
|
||||
predictor_masked=True,
|
||||
face_type=FaceType.HALF,
|
||||
default_mode=4,
|
||||
base_erode_mask_modifier=100,
|
||||
base_blur_mask_modifier=100,
|
||||
default_erode_mask_modifier=0,
|
||||
default_blur_mask_modifier=0,
|
||||
)
|
||||
|
||||
def Build(self, lighter_ae):
|
||||
exec(nnlib.code_import_all, locals(), globals())
|
||||
|
|
|
@ -120,18 +120,26 @@ class Model(ModelBase):
|
|||
|
||||
return [ ('H64', np.concatenate ( st, axis=0 ) ) ]
|
||||
|
||||
def predictor_func (self, face):
|
||||
x, mx = self.src_view ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
def predictor_func (self, face=None, dummy_predict=False):
|
||||
if dummy_predict:
|
||||
self.src_view ([ np.zeros ( (1, 64, 64, 3), dtype=np.float32 ) ])
|
||||
else:
|
||||
x, mx = self.src_view ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
|
||||
#override
|
||||
def get_converter(self):
|
||||
from converters import ConverterMasked
|
||||
return ConverterMasked(self.predictor_func,
|
||||
predictor_input_size=64,
|
||||
face_type=FaceType.HALF,
|
||||
base_erode_mask_modifier=100,
|
||||
base_blur_mask_modifier=100)
|
||||
def get_ConverterConfig(self):
|
||||
import converters
|
||||
return converters.ConverterConfigMasked(predictor_func=self.predictor_func,
|
||||
predictor_input_shape=(64,64,3),
|
||||
predictor_masked=True,
|
||||
face_type=FaceType.HALF,
|
||||
default_mode=4,
|
||||
base_erode_mask_modifier=100,
|
||||
base_blur_mask_modifier=100,
|
||||
default_erode_mask_modifier=0,
|
||||
default_blur_mask_modifier=0,
|
||||
)
|
||||
|
||||
def Build(self, lighter_ae):
|
||||
exec(nnlib.code_import_all, locals(), globals())
|
||||
|
|
|
@ -117,18 +117,26 @@ class Model(ModelBase):
|
|||
|
||||
return [ ('LIAEF128', np.concatenate ( st, axis=0 ) ) ]
|
||||
|
||||
def predictor_func (self, face):
|
||||
x, mx = self.convert ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
def predictor_func (self, face=None, dummy_predict=False):
|
||||
if dummy_predict:
|
||||
self.convert ([ np.zeros ( (1, 128, 128, 3), dtype=np.float32 ) ])
|
||||
else:
|
||||
x, mx = self.convert ( [ face[np.newaxis,...] ] )
|
||||
return x[0], mx[0][...,0]
|
||||
|
||||
#override
|
||||
def get_converter(self):
|
||||
from converters import ConverterMasked
|
||||
return ConverterMasked(self.predictor_func,
|
||||
predictor_input_size=128,
|
||||
face_type=FaceType.FULL,
|
||||
base_erode_mask_modifier=30,
|
||||
base_blur_mask_modifier=0)
|
||||
def get_ConverterConfig(self):
|
||||
import converters
|
||||
return converters.ConverterConfigMasked(predictor_func=self.predictor_func,
|
||||
predictor_input_shape=(128,128,3),
|
||||
predictor_masked=True,
|
||||
face_type=FaceType.FULL,
|
||||
default_mode=4,
|
||||
base_erode_mask_modifier=30,
|
||||
base_blur_mask_modifier=0,
|
||||
default_erode_mask_modifier=0,
|
||||
default_blur_mask_modifier=0,
|
||||
)
|
||||
|
||||
def Build(self, input_layer):
|
||||
exec(nnlib.code_import_all, locals(), globals())
|
||||
|
|
|
@ -1,482 +0,0 @@
|
|||
from functools import partial
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
from facelib import FaceType
|
||||
from interact import interact as io
|
||||
from mathlib import get_power_of_two
|
||||
from models import ModelBase
|
||||
from nnlib import nnlib
|
||||
from samplelib import *
|
||||
|
||||
class RecycleGANModel(ModelBase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs,
|
||||
ask_sort_by_yaw=False,
|
||||
ask_random_flip=False,
|
||||
ask_src_scale_mod=False)
|
||||
|
||||
#override
|
||||
def onInitializeOptions(self, is_first_run, ask_override):
|
||||
if is_first_run:
|
||||
self.options['resolution'] = io.input_int("Resolution ( 128,256 ?:help skip:128) : ", 128, [128,256], help_message="More resolution requires more VRAM and time to train. Value will be adjusted to multiple of 16.")
|
||||
else:
|
||||
self.options['resolution'] = self.options.get('resolution', 128)
|
||||
|
||||
#override
|
||||
def onInitialize(self, batch_size=-1, **in_options):
|
||||
exec(nnlib.code_import_all, locals(), globals())
|
||||
self.set_vram_batch_requirements({6:16})
|
||||
|
||||
resolution = self.options['resolution']
|
||||
bgr_shape = (resolution, resolution, 3)
|
||||
ngf = 64
|
||||
npf = 32
|
||||
ndf = 64
|
||||
lambda_A = 10
|
||||
lambda_B = 10
|
||||
|
||||
use_batch_norm = True #created_batch_size > 1
|
||||
self.GA = modelify(RecycleGANModel.ResNet (bgr_shape[2], use_batch_norm, n_blocks=6, ngf=ngf, use_dropout=True))(Input(bgr_shape))
|
||||
self.GB = modelify(RecycleGANModel.ResNet (bgr_shape[2], use_batch_norm, n_blocks=6, ngf=ngf, use_dropout=True))(Input(bgr_shape))
|
||||
|
||||
#self.GA = modelify(UNet (bgr_shape[2], use_batch_norm, num_downs=get_power_of_two(resolution)-1, ngf=ngf, use_dropout=True))(Input(bgr_shape))
|
||||
#self.GB = modelify(UNet (bgr_shape[2], use_batch_norm, num_downs=get_power_of_two(resolution)-1, ngf=ngf, use_dropout=True))(Input(bgr_shape))
|
||||
|
||||
self.PA = modelify(RecycleGANModel.UNetTemporalPredictor(bgr_shape[2], use_batch_norm, ngf=npf))([Input(bgr_shape), Input(bgr_shape)])
|
||||
self.PB = modelify(RecycleGANModel.UNetTemporalPredictor(bgr_shape[2], use_batch_norm, ngf=npf))([Input(bgr_shape), Input(bgr_shape)])
|
||||
|
||||
self.DA = modelify(RecycleGANModel.PatchDiscriminator(ndf=ndf) ) (Input(bgr_shape))
|
||||
self.DB = modelify(RecycleGANModel.PatchDiscriminator(ndf=ndf) ) (Input(bgr_shape))
|
||||
|
||||
if not self.is_first_run():
|
||||
weights_to_load = [
|
||||
(self.GA, 'GA.h5'),
|
||||
(self.DA, 'DA.h5'),
|
||||
(self.PA, 'PA.h5'),
|
||||
(self.GB, 'GB.h5'),
|
||||
(self.DB, 'DB.h5'),
|
||||
(self.PB, 'PB.h5'),
|
||||
]
|
||||
self.load_weights_safe(weights_to_load)
|
||||
|
||||
real_A0 = Input(bgr_shape, name="real_A0")
|
||||
real_A1 = Input(bgr_shape, name="real_A1")
|
||||
real_A2 = Input(bgr_shape, name="real_A2")
|
||||
|
||||
real_B0 = Input(bgr_shape, name="real_B0")
|
||||
real_B1 = Input(bgr_shape, name="real_B1")
|
||||
real_B2 = Input(bgr_shape, name="real_B2")
|
||||
|
||||
DA_ones = K.ones_like ( K.shape(self.DA.outputs[0]) )
|
||||
DA_zeros = K.zeros_like ( K.shape(self.DA.outputs[0] ))
|
||||
DB_ones = K.ones_like ( K.shape(self.DB.outputs[0] ))
|
||||
DB_zeros = K.zeros_like ( K.shape(self.DB.outputs[0] ))
|
||||
|
||||
def DLoss(labels,logits):
|
||||
return K.mean(K.binary_crossentropy(labels,logits))
|
||||
|
||||
def CycleLoss (t1,t2):
|
||||
return K.mean(K.abs(t1 - t2))
|
||||
|
||||
def RecurrentLOSS(t1,t2):
|
||||
return K.mean(K.abs(t1 - t2))
|
||||
|
||||
def RecycleLOSS(t1,t2):
|
||||
return K.mean(K.abs(t1 - t2))
|
||||
|
||||
fake_B0 = self.GA(real_A0)
|
||||
fake_B1 = self.GA(real_A1)
|
||||
|
||||
fake_A0 = self.GB(real_B0)
|
||||
fake_A1 = self.GB(real_B1)
|
||||
|
||||
real_A0_d = self.DA(real_A0)
|
||||
real_A0_d_ones = K.ones_like(real_A0_d)
|
||||
real_A1_d = self.DA(real_A1)
|
||||
real_A1_d_ones = K.ones_like(real_A1_d)
|
||||
|
||||
fake_A0_d = self.DA(fake_A0)
|
||||
fake_A0_d_ones = K.ones_like(fake_A0_d)
|
||||
fake_A0_d_zeros = K.zeros_like(fake_A0_d)
|
||||
|
||||
fake_A1_d = self.DA(fake_A1)
|
||||
fake_A1_d_ones = K.ones_like(fake_A1_d)
|
||||
fake_A1_d_zeros = K.zeros_like(fake_A1_d)
|
||||
|
||||
real_B0_d = self.DB(real_B0)
|
||||
real_B0_d_ones = K.ones_like(real_B0_d)
|
||||
|
||||
real_B1_d = self.DB(real_B1)
|
||||
real_B1_d_ones = K.ones_like(real_B1_d)
|
||||
|
||||
fake_B0_d = self.DB(fake_B0)
|
||||
fake_B0_d_ones = K.ones_like(fake_B0_d)
|
||||
fake_B0_d_zeros = K.zeros_like(fake_B0_d)
|
||||
|
||||
fake_B1_d = self.DB(fake_B1)
|
||||
fake_B1_d_ones = K.ones_like(fake_B1_d)
|
||||
fake_B1_d_zeros = K.zeros_like(fake_B1_d)
|
||||
|
||||
pred_A2 = self.PA ( [real_A0, real_A1])
|
||||
pred_B2 = self.PB ( [real_B0, real_B1])
|
||||
rec_A2 = self.GB ( self.PB ( [fake_B0, fake_B1]) )
|
||||
rec_B2 = self.GA ( self.PA ( [fake_A0, fake_A1]))
|
||||
|
||||
|
||||
loss_GA = DLoss(fake_B0_d_ones, fake_B0_d ) + \
|
||||
DLoss(fake_B1_d_ones, fake_B1_d ) + \
|
||||
lambda_A * (RecurrentLOSS(pred_A2, real_A2) + \
|
||||
RecycleLOSS(rec_B2, real_B2) )
|
||||
|
||||
|
||||
weights_GA = self.GA.trainable_weights + self.PA.trainable_weights
|
||||
|
||||
loss_GB = DLoss(fake_A0_d_ones, fake_A0_d ) + \
|
||||
DLoss(fake_A1_d_ones, fake_A1_d ) + \
|
||||
lambda_B * (RecurrentLOSS(pred_B2, real_B2) + \
|
||||
RecycleLOSS(rec_A2, real_A2) )
|
||||
|
||||
weights_GB = self.GB.trainable_weights + self.PB.trainable_weights
|
||||
|
||||
def opt():
|
||||
return Adam(lr=2e-4, beta_1=0.5, beta_2=0.999, tf_cpu_mode=2)#, clipnorm=1)
|
||||
|
||||
self.GA_train = K.function ([real_A0, real_A1, real_A2, real_B0, real_B1, real_B2],[loss_GA],
|
||||
opt().get_updates(loss_GA, weights_GA) )
|
||||
|
||||
self.GB_train = K.function ([real_A0, real_A1, real_A2, real_B0, real_B1, real_B2],[loss_GB],
|
||||
opt().get_updates(loss_GB, weights_GB) )
|
||||
|
||||
###########
|
||||
|
||||
loss_D_A0 = ( DLoss(real_A0_d_ones, real_A0_d ) + \
|
||||
DLoss(fake_A0_d_zeros, fake_A0_d ) ) * 0.5
|
||||
|
||||
loss_D_A1 = ( DLoss(real_A1_d_ones, real_A1_d ) + \
|
||||
DLoss(fake_A1_d_zeros, fake_A1_d ) ) * 0.5
|
||||
|
||||
loss_D_A = loss_D_A0 + loss_D_A1
|
||||
|
||||
self.DA_train = K.function ([real_A0, real_A1, real_A2, real_B0, real_B1, real_B2],[loss_D_A],
|
||||
opt().get_updates(loss_D_A, self.DA.trainable_weights) )
|
||||
|
||||
############
|
||||
|
||||
loss_D_B0 = ( DLoss(real_B0_d_ones, real_B0_d ) + \
|
||||
DLoss(fake_B0_d_zeros, fake_B0_d ) ) * 0.5
|
||||
|
||||
loss_D_B1 = ( DLoss(real_B1_d_ones, real_B1_d ) + \
|
||||
DLoss(fake_B1_d_zeros, fake_B1_d ) ) * 0.5
|
||||
|
||||
loss_D_B = loss_D_B0 + loss_D_B1
|
||||
|
||||
self.DB_train = K.function ([real_A0, real_A1, real_A2, real_B0, real_B1, real_B2],[loss_D_B],
|
||||
opt().get_updates(loss_D_B, self.DB.trainable_weights) )
|
||||
|
||||
############
|
||||
|
||||
|
||||
self.G_view = K.function([real_A0, real_A1, real_A2, real_B0, real_B1, real_B2],[fake_A0, fake_A1, pred_A2, rec_A2, fake_B0, fake_B1, pred_B2, rec_B2 ])
|
||||
|
||||
|
||||
|
||||
if self.is_training_mode:
|
||||
t = SampleProcessor.Types
|
||||
output_sample_types=[ { 'types': (t.IMG_SOURCE, t.MODE_BGR), 'resolution':resolution, 'normalize_tanh' : True} ]
|
||||
|
||||
self.set_training_data_generators ([
|
||||
SampleGeneratorImageTemporal(self.training_data_src_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||
temporal_image_count=3,
|
||||
sample_process_options=SampleProcessor.Options(random_flip = False),
|
||||
output_sample_types=output_sample_types ),
|
||||
|
||||
SampleGeneratorImageTemporal(self.training_data_dst_path, debug=self.is_debug(), batch_size=self.batch_size,
|
||||
temporal_image_count=3,
|
||||
sample_process_options=SampleProcessor.Options(random_flip = False),
|
||||
output_sample_types=output_sample_types ),
|
||||
])
|
||||
else:
|
||||
self.G_convert = K.function([real_B0],[fake_A0])
|
||||
|
||||
#override
|
||||
def get_model_filename_list(self):
|
||||
return [ [self.GA, 'GA.h5'],
|
||||
[self.GB, 'GB.h5'],
|
||||
[self.DA, 'DA.h5'],
|
||||
[self.DB, 'DB.h5'],
|
||||
[self.PA, 'PA.h5'],
|
||||
[self.PB, 'PB.h5'] ]
|
||||
|
||||
#override
|
||||
def onSave(self):
|
||||
self.save_weights_safe( self.get_model_filename_list() )
|
||||
|
||||
#override
|
||||
def onTrainOneIter(self, generators_samples, generators_list):
|
||||
source_src_0, source_src_1, source_src_2, = generators_samples[0]
|
||||
source_dst_0, source_dst_1, source_dst_2, = generators_samples[1]
|
||||
|
||||
feed = [source_src_0, source_src_1, source_src_2, source_dst_0, source_dst_1, source_dst_2]
|
||||
|
||||
loss_GA, = self.GA_train ( feed )
|
||||
loss_GB, = self.GB_train ( feed )
|
||||
loss_DA, = self.DA_train( feed )
|
||||
loss_DB, = self.DB_train( feed )
|
||||
|
||||
return ( ('GA', loss_GA), ('GB', loss_GB), ('DA', loss_DA), ('DB', loss_DB) )
|
||||
|
||||
#override
|
||||
def onGetPreview(self, sample):
|
||||
test_A0 = sample[0][0]
|
||||
test_A1 = sample[0][1]
|
||||
test_A2 = sample[0][2]
|
||||
|
||||
test_B0 = sample[1][0]
|
||||
test_B1 = sample[1][1]
|
||||
test_B2 = sample[1][2]
|
||||
|
||||
G_view_result = self.G_view([test_A0, test_A1, test_A2, test_B0, test_B1, test_B2])
|
||||
|
||||
fake_A0, fake_A1, pred_A2, rec_A2, fake_B0, fake_B1, pred_B2, rec_B2 = [ x[0] / 2 + 0.5 for x in G_view_result]
|
||||
test_A0, test_A1, test_A2, test_B0, test_B1, test_B2 = [ x[0] / 2 + 0.5 for x in [test_A0, test_A1, test_A2, test_B0, test_B1, test_B2] ]
|
||||
|
||||
r = np.concatenate ((np.concatenate ( (test_A0, test_A1, test_A2, pred_A2, fake_B0, fake_B1, rec_A2), axis=1),
|
||||
np.concatenate ( (test_B0, test_B1, test_B2, pred_B2, fake_A0, fake_A1, rec_B2), axis=1)
|
||||
), axis=0)
|
||||
|
||||
return [ ('RecycleGAN', r ) ]
|
||||
|
||||
def predictor_func (self, face):
|
||||
x = self.G_convert ( [ face[np.newaxis,...]*2-1 ] )[0]
|
||||
return np.clip ( x[0] / 2 + 0.5 , 0, 1)
|
||||
|
||||
#override
|
||||
def get_converter(self, **in_options):
|
||||
from converters import ConverterImage
|
||||
return ConverterImage(self.predictor_func,
|
||||
predictor_input_size=self.options['resolution'],
|
||||
**in_options)
|
||||
|
||||
@staticmethod
|
||||
def ResNet(output_nc, use_batch_norm, ngf=64, n_blocks=6, use_dropout=False):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
if not use_batch_norm:
|
||||
use_bias = True
|
||||
def XNormalization(x):
|
||||
return InstanceNormalization (axis=-1)(x)
|
||||
else:
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, padding='same', use_bias=use_bias)
|
||||
XConv2DTranspose = partial(Conv2DTranspose, padding='same', use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
|
||||
|
||||
def ResnetBlock(dim, use_dropout=False):
|
||||
def func(input):
|
||||
x = input
|
||||
|
||||
x = XConv2D(dim, 3, strides=1)(x)
|
||||
x = XNormalization(x)
|
||||
x = ReLU()(x)
|
||||
|
||||
if use_dropout:
|
||||
x = Dropout(0.5)(x)
|
||||
|
||||
x = XConv2D(dim, 3, strides=1)(x)
|
||||
x = XNormalization(x)
|
||||
x = ReLU()(x)
|
||||
return Add()([x,input])
|
||||
return func
|
||||
|
||||
x = input
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf, 7, strides=1)(x)))
|
||||
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*2, 3, strides=2)(x)))
|
||||
x = ReLU()(XNormalization(XConv2D(ngf*4, 3, strides=2)(x)))
|
||||
|
||||
for i in range(n_blocks):
|
||||
x = ResnetBlock(ngf*4, use_dropout=use_dropout)(x)
|
||||
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf*2, 3, strides=2)(x)))
|
||||
x = ReLU()(XNormalization(XConv2DTranspose(ngf , 3, strides=2)(x)))
|
||||
|
||||
x = XConv2D(output_nc, 7, strides=1, activation='tanh', use_bias=True)(x)
|
||||
|
||||
return x
|
||||
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def UNet(output_nc, use_batch_norm, ngf=64, use_dropout=False):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
if not use_batch_norm:
|
||||
use_bias = True
|
||||
def XNormalizationL():
|
||||
return InstanceNormalization (axis=-1)
|
||||
else:
|
||||
use_bias = False
|
||||
def XNormalizationL():
|
||||
return BatchNormalization (axis=-1)
|
||||
|
||||
def XNormalization(x):
|
||||
return XNormalizationL()(x)
|
||||
|
||||
XConv2D = partial(Conv2D, padding='same', use_bias=use_bias)
|
||||
XConv2DTranspose = partial(Conv2DTranspose, padding='same', use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
|
||||
b,h,w,c = K.int_shape(input)
|
||||
|
||||
n_downs = get_power_of_two(w) - 4
|
||||
|
||||
Norm = XNormalizationL()
|
||||
Norm2 = XNormalizationL()
|
||||
Norm4 = XNormalizationL()
|
||||
Norm8 = XNormalizationL()
|
||||
|
||||
x = input
|
||||
|
||||
x = e1 = XConv2D( ngf, 4, strides=2, use_bias=True ) (x)
|
||||
|
||||
x = e2 = Norm2( XConv2D( ngf*2, 4, strides=2 )( LeakyReLU(0.2)(x) ) )
|
||||
x = e3 = Norm4( XConv2D( ngf*4, 4, strides=2 )( LeakyReLU(0.2)(x) ) )
|
||||
|
||||
l = []
|
||||
for i in range(n_downs):
|
||||
x = Norm8( XConv2D( ngf*8, 4, strides=2 )( LeakyReLU(0.2)(x) ) )
|
||||
l += [x]
|
||||
|
||||
x = XConv2D( ngf*8, 4, strides=2, use_bias=True )( LeakyReLU(0.2)(x) )
|
||||
|
||||
for i in range(n_downs):
|
||||
x = Norm8( XConv2DTranspose( ngf*8, 4, strides=2 )( ReLU()(x) ) )
|
||||
if i <= n_downs-2:
|
||||
x = Dropout(0.5)(x)
|
||||
x = Concatenate(axis=-1)([x, l[-i-1] ])
|
||||
|
||||
x = Norm4( XConv2DTranspose( ngf*4, 4, strides=2 )( ReLU()(x) ) )
|
||||
x = Concatenate(axis=-1)([x, e3])
|
||||
|
||||
x = Norm2( XConv2DTranspose( ngf*2, 4, strides=2 )( ReLU()(x) ) )
|
||||
x = Concatenate(axis=-1)([x, e2])
|
||||
|
||||
x = Norm( XConv2DTranspose( ngf, 4, strides=2 )( ReLU()(x) ) )
|
||||
x = Concatenate(axis=-1)([x, e1])
|
||||
|
||||
x = XConv2DTranspose(output_nc, 4, strides=2, activation='tanh', use_bias=True)( ReLU()(x) )
|
||||
|
||||
return x
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def UNetTemporalPredictor(output_nc, use_batch_norm, ngf=64, use_dropout=False):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
def func(inputs):
|
||||
past_2_image_tensor, past_1_image_tensor = inputs
|
||||
|
||||
x = Concatenate(axis=-1)([ past_2_image_tensor, past_1_image_tensor ])
|
||||
x = UNet(3, use_batch_norm, ngf=ngf, use_dropout=use_dropout) (x)
|
||||
|
||||
return x
|
||||
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def PatchDiscriminator(ndf=64):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
#use_bias = True
|
||||
#def XNormalization(x):
|
||||
# return InstanceNormalization (axis=-1)(x)
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
b,h,w,c = K.int_shape(input)
|
||||
|
||||
x = input
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( ndf, 4, strides=2, padding='valid', use_bias=True)(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( ndf*2, 4, strides=2, padding='valid')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( ndf*4, 4, strides=2, padding='valid')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( ndf*8, 4, strides=2, padding='valid')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( ndf*8, 4, strides=2, padding='valid')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
return XConv2D( 1, 4, strides=1, padding='valid', use_bias=True, activation='sigmoid')(x)#
|
||||
return func
|
||||
|
||||
@staticmethod
|
||||
def NLayerDiscriminator(ndf=64, n_layers=3):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
||||
#use_bias = True
|
||||
#def XNormalization(x):
|
||||
# return InstanceNormalization (axis=-1)(x)
|
||||
use_bias = False
|
||||
def XNormalization(x):
|
||||
return BatchNormalization (axis=-1)(x)
|
||||
|
||||
XConv2D = partial(Conv2D, use_bias=use_bias)
|
||||
|
||||
def func(input):
|
||||
b,h,w,c = K.int_shape(input)
|
||||
|
||||
x = input
|
||||
|
||||
f = ndf
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( f, 4, strides=2, padding='valid', use_bias=True)(x)
|
||||
f = min( ndf*8, f*2 )
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
for i in range(n_layers):
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( f, 4, strides=2, padding='valid')(x)
|
||||
f = min( ndf*8, f*2 )
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
x = XConv2D( f, 4, strides=1, padding='valid')(x)
|
||||
x = XNormalization(x)
|
||||
x = LeakyReLU(0.2)(x)
|
||||
|
||||
x = ZeroPadding2D((1,1))(x)
|
||||
return XConv2D( 1, 4, strides=1, padding='valid', use_bias=True, activation='sigmoid')(x)#
|
||||
return func
|
||||
|
||||
Model = RecycleGANModel
|
|
@ -469,17 +469,20 @@ class SAEModel(ModelBase):
|
|||
|
||||
return result
|
||||
|
||||
def predictor_func (self, face):
|
||||
if self.options['learn_mask']:
|
||||
bgr, mask_dst_dstm, mask_src_dstm = self.AE_convert ([face[np.newaxis,...]])
|
||||
mask = mask_dst_dstm[0] * mask_src_dstm[0]
|
||||
return bgr[0], mask[...,0]
|
||||
def predictor_func (self, face=None, dummy_predict=False):
|
||||
if dummy_predict:
|
||||
self.AE_convert ([ np.zeros ( (1, self.options['resolution'], self.options['resolution'], 3), dtype=np.float32 ) ])
|
||||
else:
|
||||
bgr, = self.AE_convert ([face[np.newaxis,...]])
|
||||
return bgr[0]
|
||||
if self.options['learn_mask']:
|
||||
bgr, mask_dst_dstm, mask_src_dstm = self.AE_convert ([face[np.newaxis,...]])
|
||||
mask = mask_dst_dstm[0] * mask_src_dstm[0]
|
||||
return bgr[0], mask[...,0]
|
||||
else:
|
||||
bgr, = self.AE_convert ([face[np.newaxis,...]])
|
||||
return bgr[0]
|
||||
|
||||
#override
|
||||
def get_converter(self):
|
||||
def get_ConverterConfig(self):
|
||||
base_erode_mask_modifier = 30 if self.options['face_type'] == 'f' else 100
|
||||
base_blur_mask_modifier = 0 if self.options['face_type'] == 'f' else 100
|
||||
|
||||
|
@ -489,17 +492,18 @@ class SAEModel(ModelBase):
|
|||
|
||||
face_type = FaceType.FULL if self.options['face_type'] == 'f' else FaceType.HALF
|
||||
|
||||
from converters import ConverterMasked
|
||||
return ConverterMasked(self.predictor_func,
|
||||
predictor_input_size=self.options['resolution'],
|
||||
predictor_masked=self.options['learn_mask'],
|
||||
face_type=face_type,
|
||||
default_mode = 1 if self.options['apply_random_ct'] or self.options['face_style_power'] or self.options['bg_style_power'] else 4,
|
||||
base_erode_mask_modifier=base_erode_mask_modifier,
|
||||
base_blur_mask_modifier=base_blur_mask_modifier,
|
||||
default_erode_mask_modifier=default_erode_mask_modifier,
|
||||
default_blur_mask_modifier=default_blur_mask_modifier,
|
||||
clip_hborder_mask_per=0.0625 if (self.options['face_type'] == 'f') else 0)
|
||||
import converters
|
||||
return converters.ConverterConfigMasked(predictor_func=self.predictor_func,
|
||||
predictor_input_shape=(self.options['resolution'], self.options['resolution'], 3),
|
||||
predictor_masked=self.options['learn_mask'],
|
||||
face_type=face_type,
|
||||
default_mode = 1 if self.options['apply_random_ct'] or self.options['face_style_power'] or self.options['bg_style_power'] else 4,
|
||||
base_erode_mask_modifier=base_erode_mask_modifier,
|
||||
base_blur_mask_modifier=base_blur_mask_modifier,
|
||||
default_erode_mask_modifier=default_erode_mask_modifier,
|
||||
default_blur_mask_modifier=default_blur_mask_modifier,
|
||||
clip_hborder_mask_per=0.0625 if (self.options['face_type'] == 'f') else 0,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def initialize_nn_functions():
|
||||
|
|
|
@ -133,6 +133,10 @@ NLayerDiscriminator = nnlib.NLayerDiscriminator
|
|||
|
||||
os.environ['TF_MIN_GPU_MULTIPROCESSOR_COUNT'] = '2'
|
||||
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' #tf log errors only
|
||||
|
||||
import warnings
|
||||
warnings.simplefilter(action='ignore', category=FutureWarning)
|
||||
|
||||
import tensorflow as tf
|
||||
nnlib.tf = tf
|
||||
|
||||
|
@ -678,7 +682,9 @@ NLayerDiscriminator = nnlib.NLayerDiscriminator
|
|||
|
||||
def CAInitializerMP( conv_weights_list ):
|
||||
#Convolution Aware Initialization https://arxiv.org/abs/1702.06295
|
||||
result = CAInitializerMPSubprocessor ( [ (i, K.int_shape(conv_weights)) for i, conv_weights in enumerate(conv_weights_list) ], K.floatx(), K.image_data_format() ).run()
|
||||
data = [ (i, K.int_shape(conv_weights)) for i, conv_weights in enumerate(conv_weights_list) ]
|
||||
data = sorted(data, key=lambda data: sum(data[1]) )
|
||||
result = CAInitializerMPSubprocessor (data, K.floatx(), K.image_data_format() ).run()
|
||||
for idx, weights in result:
|
||||
K.set_value ( conv_weights_list[idx], weights )
|
||||
nnlib.CAInitializerMP = CAInitializerMP
|
||||
|
@ -1086,7 +1092,6 @@ class CAInitializerMPSubprocessor(Subprocessor):
|
|||
|
||||
#override
|
||||
def __init__(self, idx_shapes_list, floatx, data_format ):
|
||||
|
||||
self.idx_shapes_list = idx_shapes_list
|
||||
self.floatx = floatx
|
||||
self.data_format = data_format
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
numpy==1.16.3
|
||||
numpy==1.17.0
|
||||
h5py==2.9.0
|
||||
Keras==2.2.4
|
||||
opencv-python==4.0.0.21
|
||||
opencv-python==4.1.0.25
|
||||
tensorflow-gpu==1.13.1
|
||||
plaidml-keras==0.5.0
|
||||
scikit-image
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
numpy==1.16.3
|
||||
numpy==1.17.0
|
||||
h5py==2.9.0
|
||||
Keras==2.2.4
|
||||
opencv-python==4.0.0.21
|
||||
opencv-python==4.1.0.25
|
||||
tensorflow==1.12.0
|
||||
scikit-image
|
||||
tqdm
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
numpy==1.16.3
|
||||
numpy==1.17.0
|
||||
h5py==2.9.0
|
||||
Keras==2.2.4
|
||||
opencv-python==4.0.0.21
|
||||
opencv-python==4.1.0.25
|
||||
tensorflow-gpu==1.12.0
|
||||
plaidml==0.6.0
|
||||
plaidml-keras==0.5.0
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
numpy==1.16.3
|
||||
numpy==1.17.0
|
||||
h5py==2.9.0
|
||||
Keras==2.2.4
|
||||
opencv-python==4.0.0.21
|
||||
opencv-python==4.1.0.25
|
||||
tensorflow==1.12.0
|
||||
plaidml==0.6.0
|
||||
plaidml-keras==0.5.0
|
||||
|
|
|
@ -62,6 +62,7 @@ class SampleProcessor(object):
|
|||
FACE_TYPE_HEAD = 12 #currently unused
|
||||
FACE_TYPE_AVATAR = 13 #currently unused
|
||||
FACE_TYPE_FULL_NO_ALIGN = 14
|
||||
FACE_TYPE_HEAD_NO_ALIGN = 15
|
||||
FACE_TYPE_END = 20
|
||||
|
||||
MODE_BEGIN = 40
|
||||
|
@ -73,7 +74,6 @@ class SampleProcessor(object):
|
|||
MODE_END = 50
|
||||
|
||||
class Options(object):
|
||||
|
||||
def __init__(self, random_flip = True, rotation_range=[-10,10], scale_range=[-0.05, 0.05], tx_range=[-0.05, 0.05], ty_range=[-0.05, 0.05] ):
|
||||
self.random_flip = random_flip
|
||||
self.rotation_range = rotation_range
|
||||
|
@ -81,6 +81,13 @@ class SampleProcessor(object):
|
|||
self.tx_range = tx_range
|
||||
self.ty_range = ty_range
|
||||
|
||||
SPTF_FACETYPE_TO_FACETYPE = { Types.FACE_TYPE_HALF : FaceType.HALF,
|
||||
Types.FACE_TYPE_FULL : FaceType.FULL,
|
||||
Types.FACE_TYPE_HEAD : FaceType.HEAD,
|
||||
Types.FACE_TYPE_FULL_NO_ALIGN : FaceType.FULL_NO_ALIGN,
|
||||
Types.FACE_TYPE_HEAD_NO_ALIGN : FaceType.HEAD_NO_ALIGN,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def process (sample, sample_process_options, output_sample_types, debug, ct_sample=None):
|
||||
SPTF = SampleProcessor.Types
|
||||
|
@ -101,10 +108,7 @@ class SampleProcessor(object):
|
|||
|
||||
sample_rnd_seed = np.random.randint(0x80000000)
|
||||
|
||||
SPTF_FACETYPE_TO_FACETYPE = { SPTF.FACE_TYPE_HALF : FaceType.HALF,
|
||||
SPTF.FACE_TYPE_FULL : FaceType.FULL,
|
||||
SPTF.FACE_TYPE_HEAD : FaceType.HEAD,
|
||||
SPTF.FACE_TYPE_FULL_NO_ALIGN : FaceType.FULL_NO_ALIGN }
|
||||
|
||||
|
||||
outputs = []
|
||||
for opts in output_sample_types:
|
||||
|
@ -206,7 +210,7 @@ class SampleProcessor(object):
|
|||
cached_images[img_type] = img
|
||||
|
||||
if is_face_sample and target_face_type != SPTF.NONE:
|
||||
ft = SPTF_FACETYPE_TO_FACETYPE[target_face_type]
|
||||
ft = SampleProcessor.SPTF_FACETYPE_TO_FACETYPE[target_face_type]
|
||||
if ft > sample.face_type:
|
||||
raise Exception ('sample %s type %s does not match model requirement %s. Consider extract necessary type of faces.' % (sample.filename, sample.face_type, ft) )
|
||||
|
||||
|
|
|
@ -23,3 +23,14 @@ def set_process_lowest_prio():
|
|||
def set_process_dpi_aware():
|
||||
if sys.platform[0:3] == 'win':
|
||||
windll.user32.SetProcessDPIAware(True)
|
||||
|
||||
def get_screen_size():
|
||||
if sys.platform[0:3] == 'win':
|
||||
user32 = windll.user32
|
||||
return user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
|
||||
elif 'darwin' in sys.platform:
|
||||
pass
|
||||
elif 'linux' in sys.platform:
|
||||
pass
|
||||
|
||||
return (1366, 768)
|
Loading…
Add table
Add a link
Reference in a new issue