mirror of
https://github.com/iperov/DeepFaceLab.git
synced 2025-08-20 21:43:21 -07:00
removing trailing spaces
This commit is contained in:
parent
fa4e579b95
commit
a3df04999c
61 changed files with 2110 additions and 2103 deletions
|
@ -4,36 +4,36 @@ import cv2
|
|||
|
||||
from pathlib import Path
|
||||
|
||||
class DLIBExtractor(object):
|
||||
class DLIBExtractor(object):
|
||||
def __init__(self, dlib):
|
||||
self.scale_to = 1850
|
||||
#3100 eats ~1.687GB VRAM on 2GB 730 desktop card, but >4Gb on 6GB card,
|
||||
self.scale_to = 1850
|
||||
#3100 eats ~1.687GB VRAM on 2GB 730 desktop card, but >4Gb on 6GB card,
|
||||
#but 3100 doesnt work on 2GB 850M notebook card, I cant understand this behaviour
|
||||
#1850 works on 2GB 850M notebook card, works faster than 3100, produces good result
|
||||
self.dlib = dlib
|
||||
|
||||
def __enter__(self):
|
||||
def __enter__(self):
|
||||
self.dlib_cnn_face_detector = self.dlib.cnn_face_detection_model_v1( str(Path(__file__).parent / "mmod_human_face_detector.dat") )
|
||||
self.dlib_cnn_face_detector ( np.zeros ( (self.scale_to, self.scale_to, 3), dtype=np.uint8), 0 )
|
||||
self.dlib_cnn_face_detector ( np.zeros ( (self.scale_to, self.scale_to, 3), dtype=np.uint8), 0 )
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
del self.dlib_cnn_face_detector
|
||||
return False #pass exception between __enter__ and __exit__ to outter level
|
||||
|
||||
|
||||
def extract_from_bgr (self, input_image):
|
||||
input_image = input_image[:,:,::-1].copy()
|
||||
(h, w, ch) = input_image.shape
|
||||
|
||||
detected_faces = []
|
||||
detected_faces = []
|
||||
input_scale = self.scale_to / (w if w > h else h)
|
||||
input_image = cv2.resize (input_image, ( int(w*input_scale), int(h*input_scale) ), interpolation=cv2.INTER_LINEAR)
|
||||
detected_faces = self.dlib_cnn_face_detector(input_image, 0)
|
||||
|
||||
result = []
|
||||
result = []
|
||||
for d_rect in detected_faces:
|
||||
if type(d_rect) == self.dlib.mmod_rectangle:
|
||||
d_rect = d_rect.rect
|
||||
d_rect = d_rect.rect
|
||||
left, top, right, bottom = d_rect.left(), d_rect.top(), d_rect.right(), d_rect.bottom()
|
||||
result.append ( (int(left/input_scale), int(top/input_scale), int(right/input_scale), int(bottom/input_scale)) )
|
||||
|
||||
|
|
|
@ -8,16 +8,16 @@ from interact import interact as io
|
|||
class FANSegmentator(object):
|
||||
def __init__ (self, resolution, face_type_str, load_weights=True, weights_file_root=None):
|
||||
exec( nnlib.import_all(), locals(), globals() )
|
||||
|
||||
|
||||
self.model = FANSegmentator.BuildModel(resolution, ngf=32)
|
||||
|
||||
|
||||
if weights_file_root:
|
||||
weights_file_root = Path(weights_file_root)
|
||||
else:
|
||||
weights_file_root = Path(__file__).parent
|
||||
|
||||
|
||||
self.weights_path = weights_file_root / ('FANSeg_%d_%s.h5' % (resolution, face_type_str) )
|
||||
|
||||
|
||||
if load_weights:
|
||||
self.model.load_weights (str(self.weights_path))
|
||||
else:
|
||||
|
@ -31,19 +31,19 @@ class FANSegmentator(object):
|
|||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
return False #pass exception between __enter__ and __exit__ to outter level
|
||||
|
||||
|
||||
def save_weights(self):
|
||||
self.model.save_weights (str(self.weights_path))
|
||||
|
||||
|
||||
def train_on_batch(self, inp, outp):
|
||||
return self.model.train_on_batch(inp, outp)
|
||||
|
||||
|
||||
def extract_from_bgr (self, input_image):
|
||||
return np.clip ( (self.model.predict(input_image) + 1) / 2.0, 0, 1.0 )
|
||||
|
||||
|
||||
@staticmethod
|
||||
def BuildModel ( resolution, ngf=64):
|
||||
exec( nnlib.import_all(), locals(), globals() )
|
||||
|
@ -53,7 +53,7 @@ class FANSegmentator(object):
|
|||
x = FANSegmentator.DecFlow(ngf=ngf)(x)
|
||||
model = Model(inp,x)
|
||||
return model
|
||||
|
||||
|
||||
@staticmethod
|
||||
def EncFlow(ngf=64, num_downs=4):
|
||||
exec( nnlib.import_all(), locals(), globals() )
|
||||
|
@ -65,19 +65,19 @@ class FANSegmentator(object):
|
|||
def downscale (dim):
|
||||
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 func
|
||||
|
||||
def func(input):
|
||||
return func
|
||||
|
||||
def func(input):
|
||||
x = input
|
||||
|
||||
|
||||
result = []
|
||||
for i in range(num_downs):
|
||||
x = downscale ( min(ngf*(2**i), ngf*8) )(x)
|
||||
result += [x]
|
||||
|
||||
result += [x]
|
||||
|
||||
return result
|
||||
return func
|
||||
|
||||
|
||||
@staticmethod
|
||||
def DecFlow(output_nc=1, ngf=64, activation='tanh'):
|
||||
exec (nnlib.import_all(), locals(), globals())
|
||||
|
@ -85,23 +85,23 @@ class FANSegmentator(object):
|
|||
use_bias = True
|
||||
def XNormalization(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):
|
||||
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 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 func
|
||||
|
||||
return func
|
||||
|
||||
def func(input):
|
||||
input_len = len(input)
|
||||
|
||||
|
||||
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)
|
||||
if i != 0:
|
||||
x = Concatenate(axis=3)([ input[i-1] , x])
|
||||
|
||||
|
||||
return Conv2D(output_nc, 3, 1, 'same', activation=activation)(x)
|
||||
return func
|
||||
return func
|
||||
|
|
|
@ -3,7 +3,7 @@ from enum import IntEnum
|
|||
class FaceType(IntEnum):
|
||||
HALF = 0,
|
||||
FULL = 1,
|
||||
HEAD = 2,
|
||||
HEAD = 2,
|
||||
AVATAR = 3, #centered nose only
|
||||
MARK_ONLY = 4, #no align at all, just embedded faceinfo
|
||||
QTY = 5
|
||||
|
@ -13,12 +13,12 @@ class FaceType(IntEnum):
|
|||
r = from_string_dict.get (s.lower())
|
||||
if r is None:
|
||||
raise Exception ('FaceType.fromString value error')
|
||||
return r
|
||||
|
||||
@staticmethod
|
||||
return r
|
||||
|
||||
@staticmethod
|
||||
def toString (face_type):
|
||||
return to_string_list[face_type]
|
||||
|
||||
|
||||
from_string_dict = {'half_face': FaceType.HALF,
|
||||
'full_face': FaceType.FULL,
|
||||
'head' : FaceType.HEAD,
|
||||
|
@ -29,6 +29,5 @@ to_string_list = [ 'half_face',
|
|||
'full_face',
|
||||
'head',
|
||||
'avatar',
|
||||
'mark_only'
|
||||
'mark_only'
|
||||
]
|
||||
|
||||
|
|
|
@ -10,38 +10,38 @@ class LandmarksExtractor(object):
|
|||
def __init__ (self, keras):
|
||||
self.keras = keras
|
||||
K = self.keras.backend
|
||||
|
||||
def __enter__(self):
|
||||
|
||||
def __enter__(self):
|
||||
keras_model_path = Path(__file__).parent / "2DFAN-4.h5"
|
||||
if not keras_model_path.exists():
|
||||
return None
|
||||
|
||||
self.keras_model = self.keras.models.load_model (str(keras_model_path))
|
||||
|
||||
self.keras_model = self.keras.models.load_model (str(keras_model_path))
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
del self.keras_model
|
||||
return False #pass exception between __enter__ and __exit__ to outter level
|
||||
|
||||
|
||||
def extract_from_bgr (self, input_image, rects, second_pass_extractor=None):
|
||||
input_image = input_image[:,:,::-1].copy()
|
||||
(h, w, ch) = input_image.shape
|
||||
|
||||
|
||||
landmarks = []
|
||||
for (left, top, right, bottom) in rects:
|
||||
try:
|
||||
center = np.array( [ (left + right) / 2.0, (top + bottom) / 2.0] )
|
||||
#center[1] -= (bottom - top) * 0.12
|
||||
scale = (right - left + bottom - top) / 195.0
|
||||
|
||||
|
||||
image = self.crop(input_image, center, scale).astype(np.float32)
|
||||
image = np.expand_dims(image, 0)
|
||||
|
||||
|
||||
predicted = self.keras_model.predict (image).transpose (0,3,1,2)
|
||||
|
||||
|
||||
pts_img = self.get_pts_from_predict ( predicted[-1], center, scale)
|
||||
pts_img = [ ( int(pt[0]), int(pt[1]) ) for pt in pts_img ]
|
||||
pts_img = [ ( int(pt[0]), int(pt[1]) ) for pt in pts_img ]
|
||||
landmarks.append ( ( (left, top, right, bottom),pts_img ) )
|
||||
except Exception as e:
|
||||
landmarks.append ( ( (left, top, right, bottom), None ) )
|
||||
|
@ -52,26 +52,26 @@ class LandmarksExtractor(object):
|
|||
rect, lmrks = landmarks[i]
|
||||
if lmrks is None:
|
||||
continue
|
||||
|
||||
|
||||
image_to_face_mat = LandmarksProcessor.get_transform_mat (lmrks, 256, FaceType.FULL)
|
||||
face_image = cv2.warpAffine(input_image, image_to_face_mat, (256, 256), cv2.INTER_CUBIC)
|
||||
|
||||
|
||||
rects2 = second_pass_extractor.extract_from_bgr(face_image)
|
||||
if len(rects2) != 1: #dont do second pass if more than 1 or zero faces detected in cropped image
|
||||
continue
|
||||
|
||||
|
||||
rect2 = rects2[0]
|
||||
|
||||
|
||||
lmrks2 = self.extract_from_bgr (face_image, [rect2] )[0][1]
|
||||
source_lmrks2 = LandmarksProcessor.transform_points (lmrks2, image_to_face_mat, True)
|
||||
source_lmrks2 = LandmarksProcessor.transform_points (lmrks2, image_to_face_mat, True)
|
||||
landmarks[i] = (rect, source_lmrks2)
|
||||
except:
|
||||
continue
|
||||
|
||||
|
||||
return landmarks
|
||||
|
||||
def transform(self, point, center, scale, resolution):
|
||||
pt = np.array ( [point[0], point[1], 1.0] )
|
||||
pt = np.array ( [point[0], point[1], 1.0] )
|
||||
h = 200.0 * scale
|
||||
m = np.eye(3)
|
||||
m[0,0] = resolution / h
|
||||
|
@ -80,11 +80,11 @@ class LandmarksExtractor(object):
|
|||
m[1,2] = resolution * ( -center[1] / h + 0.5 )
|
||||
m = np.linalg.inv(m)
|
||||
return np.matmul (m, pt)[0:2]
|
||||
|
||||
|
||||
def crop(self, image, center, scale, resolution=256.0):
|
||||
ul = self.transform([1, 1], center, scale, resolution).astype( np.int )
|
||||
br = self.transform([resolution, resolution], center, scale, resolution).astype( np.int )
|
||||
|
||||
|
||||
if image.ndim > 2:
|
||||
newDim = np.array([br[1] - ul[1], br[0] - ul[0], image.shape[2]], dtype=np.int32)
|
||||
newImg = np.zeros(newDim, dtype=np.uint8)
|
||||
|
@ -98,14 +98,14 @@ class LandmarksExtractor(object):
|
|||
oldX = np.array([max(1, ul[0] + 1), min(br[0], wd)], dtype=np.int32)
|
||||
oldY = np.array([max(1, ul[1] + 1), min(br[1], ht)], dtype=np.int32)
|
||||
newImg[newY[0] - 1:newY[1], newX[0] - 1:newX[1] ] = image[oldY[0] - 1:oldY[1], oldX[0] - 1:oldX[1], :]
|
||||
|
||||
|
||||
newImg = cv2.resize(newImg, dsize=(int(resolution), int(resolution)), interpolation=cv2.INTER_LINEAR)
|
||||
return newImg
|
||||
|
||||
|
||||
def get_pts_from_predict(self, a, center, scale):
|
||||
b = a.reshape ( (a.shape[0], a.shape[1]*a.shape[2]) )
|
||||
b = a.reshape ( (a.shape[0], a.shape[1]*a.shape[2]) )
|
||||
c = b.argmax(1).reshape ( (a.shape[0], 1) ).repeat(2, axis=1).astype(np.float)
|
||||
c[:,0] %= a.shape[2]
|
||||
c[:,0] %= a.shape[2]
|
||||
c[:,1] = np.apply_along_axis ( lambda x: np.floor(x / a.shape[2]), 0, c[:,1] )
|
||||
|
||||
for i in range(a.shape[0]):
|
||||
|
@ -113,6 +113,6 @@ class LandmarksExtractor(object):
|
|||
if pX > 0 and pX < 63 and pY > 0 and pY < 63:
|
||||
diff = np.array ( [a[i,pY,pX+1]-a[i,pY,pX-1], a[i,pY+1,pX]-a[i,pY-1,pX]] )
|
||||
c[i] += np.sign(diff)*0.25
|
||||
|
||||
|
||||
c += 0.5
|
||||
return [ self.transform (c[i], center, scale, a.shape[2]) for i in range(a.shape[0]) ]
|
||||
return [ self.transform (c[i], center, scale, a.shape[2]) for i in range(a.shape[0]) ]
|
||||
|
|
|
@ -36,7 +36,7 @@ landmarks_68_pt = { "mouth": (48,68),
|
|||
"left_eye": (42, 48),
|
||||
"nose": (27, 36), # missed one point
|
||||
"jaw": (0, 17) }
|
||||
|
||||
|
||||
|
||||
landmarks_68_3D = np.array( [
|
||||
[-73.393523 , -29.801432 , 47.667532 ],
|
||||
|
@ -107,20 +107,20 @@ landmarks_68_3D = np.array( [
|
|||
[8.449166 , 30.596216 , -20.671489 ],
|
||||
[0.205322 , 31.408738 , -21.903670 ],
|
||||
[-7.198266 , 30.844876 , -20.328022 ] ], dtype=np.float32)
|
||||
|
||||
|
||||
def get_transform_mat (image_landmarks, output_size, face_type, scale=1.0):
|
||||
if not isinstance(image_landmarks, np.ndarray):
|
||||
image_landmarks = np.array (image_landmarks)
|
||||
|
||||
image_landmarks = np.array (image_landmarks)
|
||||
|
||||
if face_type == FaceType.AVATAR:
|
||||
centroid = np.mean (image_landmarks, axis=0)
|
||||
|
||||
|
||||
mat = umeyama(image_landmarks[17:], landmarks_2D, True)[0:2]
|
||||
a, c = mat[0,0], mat[1,0]
|
||||
scale = math.sqrt((a * a) + (c * c))
|
||||
|
||||
|
||||
padding = (output_size / 64) * 32
|
||||
|
||||
|
||||
mat = np.eye ( 2,3 )
|
||||
mat[0,2] = -centroid[0]
|
||||
mat[1,2] = -centroid[1]
|
||||
|
@ -135,15 +135,15 @@ def get_transform_mat (image_landmarks, output_size, face_type, scale=1.0):
|
|||
padding = (output_size / 64) * 24
|
||||
else:
|
||||
raise ValueError ('wrong face_type: ', face_type)
|
||||
|
||||
|
||||
mat = umeyama(image_landmarks[17:], landmarks_2D, True)[0:2]
|
||||
mat = mat * (output_size - 2 * padding)
|
||||
mat[:,2] += padding
|
||||
mat[:,2] += padding
|
||||
mat *= (1 / scale)
|
||||
mat[:,2] += -output_size*( ( (1 / scale) - 1.0 ) / 2 )
|
||||
|
||||
|
||||
return mat
|
||||
|
||||
|
||||
def transform_points(points, mat, invert=False):
|
||||
if invert:
|
||||
mat = cv2.invertAffineTransform (mat)
|
||||
|
@ -151,68 +151,68 @@ def transform_points(points, mat, invert=False):
|
|||
points = cv2.transform(points, mat, points.shape)
|
||||
points = np.squeeze(points)
|
||||
return points
|
||||
|
||||
|
||||
def get_image_hull_mask (image_shape, image_landmarks):
|
||||
|
||||
|
||||
def get_image_hull_mask (image_shape, image_landmarks):
|
||||
if len(image_landmarks) != 68:
|
||||
raise Exception('get_image_hull_mask works only with 68 landmarks')
|
||||
|
||||
|
||||
hull_mask = np.zeros(image_shape[0:2]+(1,),dtype=np.float32)
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[0:9],
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[0:9],
|
||||
image_landmarks[17:18]))) , (1,) )
|
||||
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[8:17],
|
||||
np.concatenate ( (image_landmarks[8:17],
|
||||
image_landmarks[26:27]))) , (1,) )
|
||||
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[17:20],
|
||||
np.concatenate ( (image_landmarks[17:20],
|
||||
image_landmarks[8:9]))) , (1,) )
|
||||
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[24:27],
|
||||
np.concatenate ( (image_landmarks[24:27],
|
||||
image_landmarks[8:9]))) , (1,) )
|
||||
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[19:25],
|
||||
np.concatenate ( (image_landmarks[19:25],
|
||||
image_landmarks[8:9],
|
||||
))) , (1,) )
|
||||
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[17:22],
|
||||
np.concatenate ( (image_landmarks[17:22],
|
||||
image_landmarks[27:28],
|
||||
image_landmarks[31:36],
|
||||
image_landmarks[8:9]
|
||||
))) , (1,) )
|
||||
|
||||
))) , (1,) )
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(
|
||||
np.concatenate ( (image_landmarks[22:27],
|
||||
np.concatenate ( (image_landmarks[22:27],
|
||||
image_landmarks[27:28],
|
||||
image_landmarks[31:36],
|
||||
image_landmarks[8:9]
|
||||
))) , (1,) )
|
||||
|
||||
))) , (1,) )
|
||||
|
||||
#nose
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull(image_landmarks[27:36]), (1,) )
|
||||
|
||||
|
||||
return hull_mask
|
||||
|
||||
def get_image_eye_mask (image_shape, image_landmarks):
|
||||
def get_image_eye_mask (image_shape, image_landmarks):
|
||||
if len(image_landmarks) != 68:
|
||||
raise Exception('get_image_eye_mask works only with 68 landmarks')
|
||||
|
||||
|
||||
hull_mask = np.zeros(image_shape[0:2]+(1,),dtype=np.float32)
|
||||
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull( image_landmarks[36:42]), (1,) )
|
||||
cv2.fillConvexPoly( hull_mask, cv2.convexHull( image_landmarks[42:48]), (1,) )
|
||||
|
||||
|
||||
return hull_mask
|
||||
|
||||
def get_image_hull_mask_3D (image_shape, image_landmarks):
|
||||
|
||||
def get_image_hull_mask_3D (image_shape, image_landmarks):
|
||||
result = get_image_hull_mask(image_shape, image_landmarks)
|
||||
|
||||
|
||||
return np.repeat ( result, (3,), -1 )
|
||||
|
||||
def blur_image_hull_mask (hull_mask):
|
||||
|
@ -224,7 +224,7 @@ def blur_image_hull_mask (hull_mask):
|
|||
leny = maxy - miny;
|
||||
masky = int(minx+(lenx//2))
|
||||
maskx = int(miny+(leny//2))
|
||||
lowest_len = min (lenx, leny)
|
||||
lowest_len = min (lenx, leny)
|
||||
ero = int( lowest_len * 0.085 )
|
||||
blur = int( lowest_len * 0.10 )
|
||||
|
||||
|
@ -233,10 +233,10 @@ def blur_image_hull_mask (hull_mask):
|
|||
hull_mask = np.expand_dims (hull_mask,-1)
|
||||
|
||||
return hull_mask
|
||||
|
||||
def get_blurred_image_hull_mask(image_shape, image_landmarks):
|
||||
|
||||
def get_blurred_image_hull_mask(image_shape, image_landmarks):
|
||||
return blur_image_hull_mask ( get_image_hull_mask(image_shape, image_landmarks) )
|
||||
|
||||
|
||||
mirror_idxs = [
|
||||
[0,16],
|
||||
[1,15],
|
||||
|
@ -246,23 +246,23 @@ mirror_idxs = [
|
|||
[5,11],
|
||||
[6,10],
|
||||
[7,9],
|
||||
|
||||
|
||||
[17,26],
|
||||
[18,25],
|
||||
[19,24],
|
||||
[20,23],
|
||||
[21,22],
|
||||
|
||||
[21,22],
|
||||
|
||||
[36,45],
|
||||
[37,44],
|
||||
[38,43],
|
||||
[39,42],
|
||||
[40,47],
|
||||
[41,46],
|
||||
|
||||
[41,46],
|
||||
|
||||
[31,35],
|
||||
[32,34],
|
||||
|
||||
|
||||
[50,52],
|
||||
[49,53],
|
||||
[48,54],
|
||||
|
@ -271,28 +271,28 @@ mirror_idxs = [
|
|||
[67,65],
|
||||
[60,64],
|
||||
[61,63] ]
|
||||
|
||||
def mirror_landmarks (landmarks, val):
|
||||
|
||||
def mirror_landmarks (landmarks, val):
|
||||
result = landmarks.copy()
|
||||
|
||||
|
||||
for idx in mirror_idxs:
|
||||
result [ idx ] = result [ idx[::-1] ]
|
||||
|
||||
result[:,0] = val - result[:,0] - 1
|
||||
return result
|
||||
|
||||
|
||||
def draw_landmarks (image, image_landmarks, color=(0,255,0), transparent_mask=False):
|
||||
if len(image_landmarks) != 68:
|
||||
raise Exception('get_image_eye_mask works only with 68 landmarks')
|
||||
|
||||
jaw = image_landmarks[slice(*landmarks_68_pt["jaw"])]
|
||||
raise Exception('get_image_eye_mask works only with 68 landmarks')
|
||||
|
||||
jaw = image_landmarks[slice(*landmarks_68_pt["jaw"])]
|
||||
right_eyebrow = image_landmarks[slice(*landmarks_68_pt["right_eyebrow"])]
|
||||
left_eyebrow = image_landmarks[slice(*landmarks_68_pt["left_eyebrow"])]
|
||||
mouth = image_landmarks[slice(*landmarks_68_pt["mouth"])]
|
||||
right_eye = image_landmarks[slice(*landmarks_68_pt["right_eye"])]
|
||||
left_eye = image_landmarks[slice(*landmarks_68_pt["left_eye"])]
|
||||
nose = image_landmarks[slice(*landmarks_68_pt["nose"])]
|
||||
|
||||
mouth = image_landmarks[slice(*landmarks_68_pt["mouth"])]
|
||||
right_eye = image_landmarks[slice(*landmarks_68_pt["right_eye"])]
|
||||
left_eye = image_landmarks[slice(*landmarks_68_pt["left_eye"])]
|
||||
nose = image_landmarks[slice(*landmarks_68_pt["nose"])]
|
||||
|
||||
# open shapes
|
||||
cv2.polylines(image, tuple(np.array([v]) for v in ( right_eyebrow, jaw, left_eyebrow, np.concatenate((nose, [nose[-6]])) )),
|
||||
False, color, lineType=cv2.LINE_AA)
|
||||
|
@ -303,9 +303,9 @@ def draw_landmarks (image, image_landmarks, color=(0,255,0), transparent_mask=Fa
|
|||
for x, y in np.concatenate((right_eyebrow, left_eyebrow, mouth, right_eye, left_eye, nose), axis=0):
|
||||
cv2.circle(image, (x, y), 1, color, 1, lineType=cv2.LINE_AA)
|
||||
# jaw big circles
|
||||
for x, y in jaw:
|
||||
for x, y in jaw:
|
||||
cv2.circle(image, (x, y), 2, color, lineType=cv2.LINE_AA)
|
||||
|
||||
|
||||
if transparent_mask:
|
||||
mask = get_image_hull_mask (image.shape, image_landmarks)
|
||||
image[...] = ( image * (1-mask) + image * mask / 2 )[...]
|
||||
|
@ -314,24 +314,24 @@ def draw_rect_landmarks (image, rect, image_landmarks, face_size, face_type, tra
|
|||
draw_landmarks(image, image_landmarks, color=landmarks_color, transparent_mask=transparent_mask)
|
||||
image_utils.draw_rect (image, rect, (255,0,0), 2 )
|
||||
|
||||
image_to_face_mat = get_transform_mat (image_landmarks, face_size, face_type)
|
||||
image_to_face_mat = get_transform_mat (image_landmarks, face_size, face_type)
|
||||
points = transform_points ( [ (0,0), (0,face_size-1), (face_size-1, face_size-1), (face_size-1,0) ], image_to_face_mat, True)
|
||||
image_utils.draw_polygon (image, points, (0,0,255), 2)
|
||||
|
||||
image_utils.draw_polygon (image, points, (0,0,255), 2)
|
||||
|
||||
def calc_face_pitch(landmarks):
|
||||
if not isinstance(landmarks, np.ndarray):
|
||||
landmarks = np.array (landmarks)
|
||||
t = ( (landmarks[6][1]-landmarks[8][1]) + (landmarks[10][1]-landmarks[8][1]) ) / 2.0
|
||||
t = ( (landmarks[6][1]-landmarks[8][1]) + (landmarks[10][1]-landmarks[8][1]) ) / 2.0
|
||||
b = landmarks[8][1]
|
||||
return float(b-t)
|
||||
|
||||
|
||||
def calc_face_yaw(landmarks):
|
||||
if not isinstance(landmarks, np.ndarray):
|
||||
landmarks = np.array (landmarks)
|
||||
l = ( (landmarks[27][0]-landmarks[0][0]) + (landmarks[28][0]-landmarks[1][0]) + (landmarks[29][0]-landmarks[2][0]) ) / 3.0
|
||||
l = ( (landmarks[27][0]-landmarks[0][0]) + (landmarks[28][0]-landmarks[1][0]) + (landmarks[29][0]-landmarks[2][0]) ) / 3.0
|
||||
r = ( (landmarks[16][0]-landmarks[27][0]) + (landmarks[15][0]-landmarks[28][0]) + (landmarks[14][0]-landmarks[29][0]) ) / 3.0
|
||||
return float(r-l)
|
||||
|
||||
|
||||
#returns pitch,yaw [-1...+1]
|
||||
def estimate_pitch_yaw(aligned_256px_landmarks):
|
||||
shape = (256,256)
|
||||
|
@ -347,8 +347,8 @@ def estimate_pitch_yaw(aligned_256px_landmarks):
|
|||
aligned_256px_landmarks.astype(np.float32),
|
||||
camera_matrix,
|
||||
np.zeros((4, 1)) )
|
||||
|
||||
|
||||
pitch, yaw, _ = mathlib.rotationMatrixToEulerAngles( cv2.Rodrigues(rotation_vector)[0] )
|
||||
pitch = np.clip ( pitch*1.25, -1.0, 1.0 )
|
||||
yaw = np.clip ( yaw*1.25, -1.0, 1.0 )
|
||||
return pitch, yaw
|
||||
return pitch, yaw
|
||||
|
|
|
@ -5,10 +5,10 @@ import cv2
|
|||
from pathlib import Path
|
||||
from nnlib import nnlib
|
||||
|
||||
class MTCExtractor(object):
|
||||
class MTCExtractor(object):
|
||||
def __init__(self):
|
||||
self.scale_to = 1920
|
||||
|
||||
|
||||
self.min_face_size = self.scale_to * 0.042
|
||||
self.thresh1 = 0.7
|
||||
self.thresh2 = 0.85
|
||||
|
@ -26,12 +26,12 @@ class MTCExtractor(object):
|
|||
x = Conv2D (32, kernel_size=(3,3), strides=(1,1), padding='valid', name="conv3")(x)
|
||||
x = PReLU (shared_axes=[1,2], name="PReLU3" )(x)
|
||||
prob = Conv2D (2, kernel_size=(1,1), strides=(1,1), padding='valid', name="conv41")(x)
|
||||
prob = Softmax()(prob)
|
||||
prob = Softmax()(prob)
|
||||
x = Conv2D (4, kernel_size=(1,1), strides=(1,1), padding='valid', name="conv42")(x)
|
||||
|
||||
PNet_model = Model(PNet_Input, [x,prob] )
|
||||
PNet_model = Model(PNet_Input, [x,prob] )
|
||||
PNet_model.load_weights ( (Path(__file__).parent / 'mtcnn_pnet.h5').__str__() )
|
||||
|
||||
|
||||
RNet_Input = Input ( (24, 24, 3) )
|
||||
x = RNet_Input
|
||||
x = Conv2D (28, kernel_size=(3,3), strides=(1,1), padding='valid', name="conv1")(x)
|
||||
|
@ -39,18 +39,18 @@ class MTCExtractor(object):
|
|||
x = MaxPooling2D( pool_size=(3,3), strides=(2,2), padding='same' ) (x)
|
||||
x = Conv2D (48, kernel_size=(3,3), strides=(1,1), padding='valid', name="conv2")(x)
|
||||
x = PReLU (shared_axes=[1,2], name="prelu2" )(x)
|
||||
x = MaxPooling2D( pool_size=(3,3), strides=(2,2), padding='valid' ) (x)
|
||||
x = MaxPooling2D( pool_size=(3,3), strides=(2,2), padding='valid' ) (x)
|
||||
x = Conv2D (64, kernel_size=(2,2), strides=(1,1), padding='valid', name="conv3")(x)
|
||||
x = PReLU (shared_axes=[1,2], name="prelu3" )(x)
|
||||
x = Lambda ( lambda x: K.reshape (x, (-1, np.prod(K.int_shape(x)[1:]),) ), output_shape=(np.prod(K.int_shape(x)[1:]),) ) (x)
|
||||
x = Dense (128, name='conv4')(x)
|
||||
x = Dense (128, name='conv4')(x)
|
||||
x = PReLU (name="prelu4" )(x)
|
||||
prob = Dense (2, name='conv51')(x)
|
||||
prob = Softmax()(prob)
|
||||
x = Dense (4, name='conv52')(x)
|
||||
RNet_model = Model(RNet_Input, [x,prob] )
|
||||
prob = Softmax()(prob)
|
||||
x = Dense (4, name='conv52')(x)
|
||||
RNet_model = Model(RNet_Input, [x,prob] )
|
||||
RNet_model.load_weights ( (Path(__file__).parent / 'mtcnn_rnet.h5').__str__() )
|
||||
|
||||
|
||||
ONet_Input = Input ( (48, 48, 3) )
|
||||
x = ONet_Input
|
||||
x = Conv2D (32, kernel_size=(3,3), strides=(1,1), padding='valid', name="conv1")(x)
|
||||
|
@ -58,20 +58,20 @@ class MTCExtractor(object):
|
|||
x = MaxPooling2D( pool_size=(3,3), strides=(2,2), padding='same' ) (x)
|
||||
x = Conv2D (64, kernel_size=(3,3), strides=(1,1), padding='valid', name="conv2")(x)
|
||||
x = PReLU (shared_axes=[1,2], name="prelu2" )(x)
|
||||
x = MaxPooling2D( pool_size=(3,3), strides=(2,2), padding='valid' ) (x)
|
||||
x = MaxPooling2D( pool_size=(3,3), strides=(2,2), padding='valid' ) (x)
|
||||
x = Conv2D (64, kernel_size=(3,3), strides=(1,1), padding='valid', name="conv3")(x)
|
||||
x = PReLU (shared_axes=[1,2], name="prelu3" )(x)
|
||||
x = MaxPooling2D( pool_size=(2,2), strides=(2,2), padding='same' ) (x)
|
||||
x = MaxPooling2D( pool_size=(2,2), strides=(2,2), padding='same' ) (x)
|
||||
x = Conv2D (128, kernel_size=(2,2), strides=(1,1), padding='valid', name="conv4")(x)
|
||||
x = PReLU (shared_axes=[1,2], name="prelu4" )(x)
|
||||
x = Lambda ( lambda x: K.reshape (x, (-1, np.prod(K.int_shape(x)[1:]),) ), output_shape=(np.prod(K.int_shape(x)[1:]),) ) (x)
|
||||
x = Lambda ( lambda x: K.reshape (x, (-1, np.prod(K.int_shape(x)[1:]),) ), output_shape=(np.prod(K.int_shape(x)[1:]),) ) (x)
|
||||
x = Dense (256, name='conv5')(x)
|
||||
x = PReLU (name="prelu5" )(x)
|
||||
prob = Dense (2, name='conv61')(x)
|
||||
prob = Softmax()(prob)
|
||||
prob = Softmax()(prob)
|
||||
x1 = Dense (4, name='conv62')(x)
|
||||
x2 = Dense (10, name='conv63')(x)
|
||||
ONet_model = Model(ONet_Input, [x1,x2,prob] )
|
||||
x2 = Dense (10, name='conv63')(x)
|
||||
ONet_model = Model(ONet_Input, [x1,x2,prob] )
|
||||
ONet_model.load_weights ( (Path(__file__).parent / 'mtcnn_onet.h5').__str__() )
|
||||
|
||||
self.pnet_fun = K.function ( PNet_model.inputs, PNet_model.outputs )
|
||||
|
@ -79,13 +79,13 @@ class MTCExtractor(object):
|
|||
self.onet_fun = K.function ( ONet_model.inputs, ONet_model.outputs )
|
||||
|
||||
def __enter__(self):
|
||||
faces, pnts = detect_face ( np.zeros ( (self.scale_to, self.scale_to, 3)), self.min_face_size, self.pnet_fun, self.rnet_fun, self.onet_fun, [ self.thresh1, self.thresh2, self.thresh3 ], self.scale_factor )
|
||||
|
||||
faces, pnts = detect_face ( np.zeros ( (self.scale_to, self.scale_to, 3)), self.min_face_size, self.pnet_fun, self.rnet_fun, self.onet_fun, [ self.thresh1, self.thresh2, self.thresh3 ], self.scale_factor )
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
return False #pass exception between __enter__ and __exit__ to outter level
|
||||
|
||||
|
||||
def extract_from_bgr (self, input_image):
|
||||
input_image = input_image[:,:,::-1].copy()
|
||||
(h, w, ch) = input_image.shape
|
||||
|
@ -95,7 +95,7 @@ class MTCExtractor(object):
|
|||
|
||||
detected_faces, pnts = detect_face ( input_image, self.min_face_size, self.pnet_fun, self.rnet_fun, self.onet_fun, [ self.thresh1, self.thresh2, self.thresh3 ], self.scale_factor )
|
||||
detected_faces = [ ( int(face[0]/input_scale), int(face[1]/input_scale), int(face[2]/input_scale), int(face[3]/input_scale)) for face in detected_faces ]
|
||||
|
||||
|
||||
return detected_faces
|
||||
|
||||
def detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
|
||||
|
@ -132,9 +132,9 @@ def detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
|
|||
out = pnet([img_y])
|
||||
out0 = np.transpose(out[0], (0,2,1,3))
|
||||
out1 = np.transpose(out[1], (0,2,1,3))
|
||||
|
||||
|
||||
boxes, _ = generateBoundingBox(out1[0,:,:,1].copy(), out0[0,:,:,:].copy(), scale, threshold[0])
|
||||
|
||||
|
||||
# inter-scale nms
|
||||
pick = nms(boxes.copy(), 0.5, 'Union')
|
||||
if boxes.size>0 and pick.size>0:
|
||||
|
@ -217,7 +217,7 @@ def detect_face(img, minsize, pnet, rnet, onet, threshold, factor):
|
|||
pick = nms(total_boxes.copy(), 0.7, 'Min')
|
||||
total_boxes = total_boxes[pick,:]
|
||||
points = points[:,pick]
|
||||
|
||||
|
||||
return total_boxes, points
|
||||
|
||||
|
||||
|
@ -235,7 +235,7 @@ def bbreg(boundingbox,reg):
|
|||
b4 = boundingbox[:,3]+reg[:,3]*h
|
||||
boundingbox[:,0:4] = np.transpose(np.vstack([b1, b2, b3, b4 ]))
|
||||
return boundingbox
|
||||
|
||||
|
||||
def generateBoundingBox(imap, reg, scale, t):
|
||||
"""Use heatmap to generate bounding boxes"""
|
||||
stride=2
|
||||
|
@ -261,7 +261,7 @@ def generateBoundingBox(imap, reg, scale, t):
|
|||
q2 = np.fix((stride*bb+cellsize-1+1)/scale)
|
||||
boundingbox = np.hstack([q1, q2, np.expand_dims(score,1), reg])
|
||||
return boundingbox, reg
|
||||
|
||||
|
||||
# function pick = nms(boxes,threshold,type)
|
||||
def nms(boxes, threshold, method):
|
||||
if boxes.size==0:
|
||||
|
@ -315,7 +315,7 @@ def pad(total_boxes, w, h):
|
|||
tmp = np.where(ex>w)
|
||||
edx.flat[tmp] = np.expand_dims(-ex[tmp]+w+tmpw[tmp],1)
|
||||
ex[tmp] = w
|
||||
|
||||
|
||||
tmp = np.where(ey>h)
|
||||
edy.flat[tmp] = np.expand_dims(-ey[tmp]+h+tmph[tmp],1)
|
||||
ey[tmp] = h
|
||||
|
@ -327,7 +327,7 @@ def pad(total_boxes, w, h):
|
|||
tmp = np.where(y<1)
|
||||
dy.flat[tmp] = np.expand_dims(2-y[tmp],1)
|
||||
y[tmp] = 1
|
||||
|
||||
|
||||
return dy, edy, dx, edx, y, ey, x, ex, tmpw, tmph
|
||||
|
||||
# function [bboxA] = rerec(bboxA)
|
||||
|
|
|
@ -3,35 +3,35 @@ from pathlib import Path
|
|||
import cv2
|
||||
from nnlib import nnlib
|
||||
|
||||
class S3FDExtractor(object):
|
||||
class S3FDExtractor(object):
|
||||
def __init__(self):
|
||||
exec( nnlib.import_all(), locals(), globals() )
|
||||
|
||||
|
||||
model_path = Path(__file__).parent / "S3FD.h5"
|
||||
if not model_path.exists():
|
||||
return None
|
||||
|
||||
self.model = nnlib.keras.models.load_model ( str(model_path) )
|
||||
self.model = nnlib.keras.models.load_model ( str(model_path) )
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
return False #pass exception between __enter__ and __exit__ to outter level
|
||||
|
||||
|
||||
def extract_from_bgr (self, input_image):
|
||||
input_image = input_image[:,:,::-1].copy()
|
||||
(h, w, ch) = input_image.shape
|
||||
|
||||
|
||||
d = max(w, h)
|
||||
scale_to = 640 if d >= 1280 else d / 2
|
||||
scale_to = max(64, scale_to)
|
||||
|
||||
|
||||
input_scale = d / scale_to
|
||||
input_image = cv2.resize (input_image, ( int(w/input_scale), int(h/input_scale) ), interpolation=cv2.INTER_LINEAR)
|
||||
|
||||
olist = self.model.predict( np.expand_dims(input_image,0) )
|
||||
|
||||
|
||||
detected_faces = []
|
||||
for ltrb in self.refine (olist):
|
||||
l,t,r,b = [ x*input_scale for x in ltrb]
|
||||
|
@ -42,7 +42,7 @@ class S3FDExtractor(object):
|
|||
detected_faces.append ( [int(x) for x in (l,t,r,b) ] )
|
||||
|
||||
return detected_faces
|
||||
|
||||
|
||||
def refine(self, olist):
|
||||
bboxlist = []
|
||||
for i, ((ocls,), (oreg,)) in enumerate ( zip ( olist[::2], olist[1::2] ) ):
|
||||
|
@ -51,7 +51,7 @@ class S3FDExtractor(object):
|
|||
s_m4 = stride * 4
|
||||
|
||||
for hindex, windex in zip(*np.where(ocls > 0.05)):
|
||||
score = ocls[hindex, windex]
|
||||
score = ocls[hindex, windex]
|
||||
loc = oreg[hindex, windex, :]
|
||||
priors = np.array([windex * stride + s_d2, hindex * stride + s_d2, s_m4, s_m4])
|
||||
priors_2p = priors[2:]
|
||||
|
@ -61,15 +61,15 @@ class S3FDExtractor(object):
|
|||
box[2:] += box[:2]
|
||||
|
||||
bboxlist.append([*box, score])
|
||||
|
||||
|
||||
bboxlist = np.array(bboxlist)
|
||||
if len(bboxlist) == 0:
|
||||
bboxlist = np.zeros((1, 5))
|
||||
|
||||
|
||||
bboxlist = bboxlist[self.refine_nms(bboxlist, 0.3), :]
|
||||
bboxlist = [ x[:-1].astype(np.int) for x in bboxlist if x[-1] >= 0.5]
|
||||
return bboxlist
|
||||
|
||||
|
||||
def refine_nms(self, dets, thresh):
|
||||
keep = list()
|
||||
if len(dets) == 0:
|
||||
|
@ -91,4 +91,4 @@ class S3FDExtractor(object):
|
|||
|
||||
inds = np.where(ovr <= thresh)[0]
|
||||
order = order[inds + 1]
|
||||
return keep
|
||||
return keep
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue