Converter: added new mask modes: FAN-prd, FAN-dst

This commit is contained in:
iperov 2019-03-21 21:19:03 +04:00
parent 9849bcc1e5
commit c4f41a7e76
5 changed files with 11426 additions and 11214 deletions

View file

@ -2,6 +2,7 @@ import traceback
from .Converter import Converter from .Converter import Converter
from facelib import LandmarksProcessor from facelib import LandmarksProcessor
from facelib import FaceType from facelib import FaceType
from facelib import FANSegmentator
import cv2 import cv2
import numpy as np import numpy as np
from utils import image_utils from utils import image_utils
@ -65,9 +66,15 @@ class ConverterMasked(Converter):
if self.mode == 'hist-match' or self.mode == 'hist-match-bw' or self.mode == 'seamless-hist-match': 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) self.hist_match_threshold = np.clip ( io.input_int("Hist match threshold [0..255] (skip:255) : ", 255), 0, 255)
if face_type == FaceType.FULL:
self.mask_mode = io.input_int ("Mask mode: (1) learned, (2) dst, (3) FAN-prd, (4) 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.")
else:
self.mask_mode = io.input_int ("Mask mode: (1) learned, (2) dst . Default - %d : " % (1) , 1)
self.use_predicted_mask = io.input_bool("Use predicted mask? (y/n skip:y) : ", True) if self.mask_mode == 3 or self.mask_mode == 4:
self.fan_seg = None
if self.mode != 'raw': 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.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.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)
@ -93,7 +100,9 @@ class ConverterMasked(Converter):
#override #override
def convert_face (self, img_bgr, img_face_landmarks, debug): def convert_face (self, img_bgr, img_face_landmarks, debug):
if (self.mask_mode == 3 or self.mask_mode == 4) and self.fan_seg == None:
self.fan_seg = FANSegmentator(256, FaceType.toString(FaceType.FULL) )
if self.over_res != 1: if self.over_res != 1:
img_bgr = cv2.resize ( img_bgr, ( img_bgr.shape[1]*self.over_res, img_bgr.shape[0]*self.over_res ) ) img_bgr = cv2.resize ( img_bgr, ( img_bgr.shape[1]*self.over_res, img_bgr.shape[0]*self.over_res ) )
img_face_landmarks = img_face_landmarks*self.over_res img_face_landmarks = img_face_landmarks*self.over_res
@ -120,9 +129,18 @@ class ConverterMasked(Converter):
prd_face_bgr = np.clip (predicted_bgra[:,:,0:3], 0, 1.0 ) prd_face_bgr = np.clip (predicted_bgra[:,:,0:3], 0, 1.0 )
prd_face_mask_a_0 = np.clip (predicted_bgra[:,:,3], 0.0, 1.0) prd_face_mask_a_0 = np.clip (predicted_bgra[:,:,3], 0.0, 1.0)
if not self.use_predicted_mask: if self.mask_mode == 2: #dst
prd_face_mask_a_0 = predictor_input_mask_a_0 prd_face_mask_a_0 = predictor_input_mask_a_0
elif self.mask_mode == 3: #FAN-prd
prd_face_bgr_256 = cv2.resize (prd_face_bgr, (256,256) )
prd_face_bgr_256_mask = self.fan_seg.extract_from_bgr( np.expand_dims(prd_face_bgr_256,0) ) [0]
prd_face_mask_a_0 = cv2.resize (prd_face_bgr_256_mask, (self.predictor_input_size, self.predictor_input_size))
elif self.mask_mode == 4: #FAN-dst
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_from_bgr( np.expand_dims(dst_face_256_bgr,0) ) [0]
prd_face_mask_a_0 = cv2.resize (dst_face_256_mask, (self.predictor_input_size, self.predictor_input_size))
prd_face_mask_a_0[ prd_face_mask_a_0 < 0.001 ] = 0.0 prd_face_mask_a_0[ prd_face_mask_a_0 < 0.001 ] = 0.0
prd_face_mask_a = np.expand_dims (prd_face_mask_a_0, axis=-1) prd_face_mask_a = np.expand_dims (prd_face_mask_a_0, axis=-1)

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View file

@ -8,16 +8,16 @@ from interact import interact as io
class FANSegmentator(object): class FANSegmentator(object):
def __init__ (self, resolution, face_type_str, load_weights=True, weights_file_root=None, training=False): def __init__ (self, resolution, face_type_str, load_weights=True, weights_file_root=None, training=False):
exec( nnlib.import_all(), locals(), globals() ) exec( nnlib.import_all(), locals(), globals() )
self.model = FANSegmentator.BuildModel(resolution, ngf=32) self.model = FANSegmentator.BuildModel(resolution, ngf=32)
if weights_file_root: if weights_file_root:
weights_file_root = Path(weights_file_root) weights_file_root = Path(weights_file_root)
else: else:
weights_file_root = Path(__file__).parent weights_file_root = Path(__file__).parent
self.weights_path = weights_file_root / ('FANSeg_%d_%s.h5' % (resolution, face_type_str) ) self.weights_path = weights_file_root / ('FANSeg_%d_%s.h5' % (resolution, face_type_str) )
if load_weights: if load_weights:
self.model.load_weights (str(self.weights_path)) self.model.load_weights (str(self.weights_path))
else: else:
@ -33,19 +33,19 @@ class FANSegmentator(object):
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, exc_type=None, exc_value=None, traceback=None): def __exit__(self, exc_type=None, exc_value=None, traceback=None):
return False #pass exception between __enter__ and __exit__ to outter level return False #pass exception between __enter__ and __exit__ to outter level
def save_weights(self): def save_weights(self):
self.model.save_weights (str(self.weights_path)) self.model.save_weights (str(self.weights_path))
def train_on_batch(self, inp, outp): def train_on_batch(self, inp, outp):
return self.model.train_on_batch(inp, outp) return self.model.train_on_batch(inp, outp)
def extract_from_bgr (self, input_image): def extract_from_bgr (self, input_image):
return np.clip ( (self.model.predict(input_image) + 1) / 2.0, 0, 1.0 ) return np.clip ( (self.model.predict(input_image) + 1) / 2.0, 0, 1.0 )
@staticmethod @staticmethod
def BuildModel ( resolution, ngf=64): def BuildModel ( resolution, ngf=64):
exec( nnlib.import_all(), locals(), globals() ) exec( nnlib.import_all(), locals(), globals() )
@ -55,7 +55,7 @@ class FANSegmentator(object):
x = FANSegmentator.DecFlow(ngf=ngf)(x) x = FANSegmentator.DecFlow(ngf=ngf)(x)
model = Model(inp,x) model = Model(inp,x)
return model return model
@staticmethod @staticmethod
def EncFlow(ngf=64, num_downs=4): def EncFlow(ngf=64, num_downs=4):
exec( nnlib.import_all(), locals(), globals() ) exec( nnlib.import_all(), locals(), globals() )
@ -67,19 +67,19 @@ class FANSegmentator(object):
def downscale (dim): def downscale (dim):
def func(x): def func(x):
return LeakyReLU(0.1)(XNormalization(Conv2D(dim, kernel_size=5, strides=2, padding='same', kernel_initializer=RandomNormal(0, 0.02))(x))) return LeakyReLU(0.1)(XNormalization(Conv2D(dim, kernel_size=5, strides=2, padding='same', kernel_initializer=RandomNormal(0, 0.02))(x)))
return func return func
def func(input): def func(input):
x = input x = input
result = [] result = []
for i in range(num_downs): for i in range(num_downs):
x = downscale ( min(ngf*(2**i), ngf*8) )(x) x = downscale ( min(ngf*(2**i), ngf*8) )(x)
result += [x] result += [x]
return result return result
return func return func
@staticmethod @staticmethod
def DecFlow(output_nc=1, ngf=64, activation='tanh'): def DecFlow(output_nc=1, ngf=64, activation='tanh'):
exec (nnlib.import_all(), locals(), globals()) exec (nnlib.import_all(), locals(), globals())
@ -87,23 +87,22 @@ class FANSegmentator(object):
use_bias = True use_bias = True
def XNormalization(x): def XNormalization(x):
return InstanceNormalization (axis=3, gamma_initializer=RandomNormal(1., 0.02))(x) return InstanceNormalization (axis=3, gamma_initializer=RandomNormal(1., 0.02))(x)
def Conv2D (filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=use_bias, kernel_initializer=RandomNormal(0, 0.02), bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None): def Conv2D (filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=use_bias, kernel_initializer=RandomNormal(0, 0.02), bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None):
return keras.layers.Conv2D( filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, data_format=data_format, dilation_rate=dilation_rate, activation=activation, use_bias=use_bias, kernel_initializer=kernel_initializer, bias_initializer=bias_initializer, kernel_regularizer=kernel_regularizer, bias_regularizer=bias_regularizer, activity_regularizer=activity_regularizer, kernel_constraint=kernel_constraint, bias_constraint=bias_constraint ) return keras.layers.Conv2D( filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, data_format=data_format, dilation_rate=dilation_rate, activation=activation, use_bias=use_bias, kernel_initializer=kernel_initializer, bias_initializer=bias_initializer, kernel_regularizer=kernel_regularizer, bias_regularizer=bias_regularizer, activity_regularizer=activity_regularizer, kernel_constraint=kernel_constraint, bias_constraint=bias_constraint )
def upscale (dim): def upscale (dim):
def func(x): def func(x):
return SubpixelUpscaler()( LeakyReLU(0.1)(XNormalization(Conv2D(dim, kernel_size=3, strides=1, padding='same', kernel_initializer=RandomNormal(0, 0.02))(x)))) return SubpixelUpscaler()( LeakyReLU(0.1)(XNormalization(Conv2D(dim, kernel_size=3, strides=1, padding='same', kernel_initializer=RandomNormal(0, 0.02))(x))))
return func return func
def func(input): def func(input):
input_len = len(input) input_len = len(input)
x = input[input_len-1] x = input[input_len-1]
for i in range(input_len-1, -1, -1): for i in range(input_len-1, -1, -1):
x = upscale( min(ngf* (2**i) *4, ngf*8 *4 ) )(x) x = upscale( min(ngf* (2**i) *4, ngf*8 *4 ) )(x)
if i != 0: if i != 0:
x = Concatenate(axis=3)([ input[i-1] , x]) x = Concatenate(axis=3)([ input[i-1] , x])
return Conv2D(output_nc, 3, 1, 'same', activation=activation)(x) return Conv2D(output_nc, 3, 1, 'same', activation=activation)(x)
return func return func