"""
Created on Wed Jul 29 19:47:08 2020
@author: hp
"""
import cv2
import numpy as np
import tensorflow as tf
from tensorflow import keras
def get_landmark_model(saved_model='models/pose_model'):
"""
Get the facial landmark model.
Original repository: https://github.com/yinguobing/cnn-facial-landmark
Parameters
----------
saved_model : string, optional
Path to facial landmarks model. The default is 'models/pose_model'.
Returns
-------
model : Tensorflow model
Facial landmarks model
"""
model = tf.saved_model.load(saved_model)
return model
def get_square_box(box):
"""Get a square box out of the given box, by expanding it."""
left_x = box[0]
top_y = box[1]
right_x = box[2]
bottom_y = box[3]
box_width = right_x - left_x
box_height = bottom_y - top_y
diff = box_height - box_width
delta = int(abs(diff) / 2)
if diff == 0:
return box
elif diff > 0:
left_x -= delta
right_x += delta
if diff % 2 == 1:
right_x += 1
else:
top_y -= delta
bottom_y += delta
if diff % 2 == 1:
bottom_y += 1
assert ((right_x - left_x) == (bottom_y - top_y)), 'Box is not square.'
return [left_x, top_y, right_x, bottom_y]
def move_box(box, offset):
"""Move the box to direction specified by vector offset"""
left_x = box[0] + offset[0]
top_y = box[1] + offset[1]
right_x = box[2] + offset[0]
bottom_y = box[3] + offset[1]
return [left_x, top_y, right_x, bottom_y]
def detect_marks(img, model, face):
"""
Find the facial landmarks in an image from the faces
Parameters
----------
img : np.uint8
The image in which landmarks are to be found
model : Tensorflow model
Loaded facial landmark model
face : list
Face coordinates (x, y, x1, y1) in which the landmarks are to be found
Returns
-------
marks : numpy array
facial landmark points
"""
offset_y = int(abs((face[3] - face[1]) * 0.1))
box_moved = move_box(face, [0, offset_y])
facebox = get_square_box(box_moved)
h, w = img.shape[:2]
if facebox[0] < 0:
facebox[0] = 0
if facebox[1] < 0:
facebox[1] = 0
if facebox[2] > w:
facebox[2] = w
if facebox[3] > h:
facebox[3] = h
face_img = img[facebox[1]: facebox[3],
facebox[0]: facebox[2]]
face_img = cv2.resize(face_img, (128, 128))
face_img = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB)
predictions = model.signatures["predict"](
tf.constant([face_img], dtype=tf.uint8))
marks = np.array(predictions['output']).flatten()[:136]
marks = np.reshape(marks, (-1, 2))
marks *= (facebox[2] - facebox[0])
marks[:, 0] += facebox[0]
marks[:, 1] += facebox[1]
marks = marks.astype(np.uint)
return marks
def draw_marks(image, marks, color=(0, 255, 0)):
"""
Draw the facial landmarks on an image
Parameters
----------
image : np.uint8
Image on which landmarks are to be drawn.
marks : list or numpy array
Facial landmark points
color : tuple, optional
Color to which landmarks are to be drawn with. The default is (0, 255, 0).
Returns
-------
None.
"""
for mark in marks:
cv2.circle(image, (mark[0], mark[1]), 2, color, -1, cv2.LINE_AA)