Dlib is a popular library. It can be used for face detection or face recognition. In this article I will use it for facial landmark detection. Facial landmarks are fecial features like nose, eyes, mouth or jaw.
Start with installing Dlib library. Dlib requires Lib Boost.
Now we can install Dlib.
Following example uses PIL and numpy packages. Instead of Pillow it is possible to use skimage package.
Note that in order to detect facial landmarks, a previously trained model file is needed. You can download one from Dlib site.
Download model file from this link : http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
After download is completed, extract the archive and make sure its location is correctly referenced in the source file.
The application first tries to detect faces in the given image. After that for each face it tries to detect landmarks. For each face an image file is created and landmarks are drawn to that file.
Landmarks are returned in a shape object. Shape object contains part objects which are two dimensional points each of which corresponds to a landmark point. To understand which point corresponds to which landmark point look at the following image. It is taken from dlib link https://sourceforge.net/p/dclib/discussion/442518/thread/c038d05f/
Given application also contains sample methods to add circles around eyes to simulate glasses.
Start with installing Dlib library. Dlib requires Lib Boost.
sudo apt-get install libboost-all-dev
Now we can install Dlib.
sudo pip install dlib
Following example uses PIL and numpy packages. Instead of Pillow it is possible to use skimage package.
pip install Pillow pip install numpy
Note that in order to detect facial landmarks, a previously trained model file is needed. You can download one from Dlib site.
Download model file from this link : http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
After download is completed, extract the archive and make sure its location is correctly referenced in the source file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""You can download a trained facial shape predictor from: | |
http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2)""" | |
import dlib | |
from PIL import Image | |
from PIL import ImageDraw | |
import numpy as np | |
import os | |
predictor_path = "shape_predictor_68_face_landmarks.dat" | |
faces_image_path = "iamages/00000005.png" | |
def detect_landmarks(image): | |
# obtain detector and predictor | |
detector = dlib.get_frontal_face_detector() | |
predictor = dlib.shape_predictor(predictor_path) | |
# convert image to numpy array | |
img = np.asanyarray(image) | |
img.flags.writeable =True | |
#output list | |
face_landmark_tuples=[] | |
# Ask the detector to find the bounding boxes of each face. The 1 in the | |
# second argument indicates that we should upsample the image 1 time. This | |
# will make everything bigger and allow us to detect more faces. | |
dets = detector(img, 1) | |
print("Number of faces detected: {}".format(len(dets))) | |
for k, rect in enumerate(dets): | |
print("Detection {}: Left: {} Top: {} Right: {} Bottom: {}" | |
.format(k, rect.left(), rect.top(), rect.right(), rect.bottom())) | |
# Get the landmarks/parts for the face in box rect. | |
shape = predictor(img, rect) | |
face_landmark_tuples.append((k,rect,shape)) | |
return face_landmark_tuples | |
def draw_landmarks(image,shape): | |
parts = [] | |
radius=1 | |
# copy original image, do not touch it | |
out_image=image.copy() | |
# extract parts to list | |
for i in xrange(0, shape.num_parts, 1): | |
print("Part {}".format(shape.part(i))) | |
parts.append(shape.part(i)) | |
# for each part, draw a circle | |
draw = ImageDraw.Draw(out_image) | |
for part in parts: | |
x = part.x | |
y = part.y | |
draw.ellipse((x - radius, y - radius, x + radius, y + radius), fill=(255, 0, 0, 255)) | |
return out_image | |
def draw_glasses(image,shape): | |
# obtain image dimensions and calculate offsets | |
width,height=image.size | |
offset_vertical=height/50 | |
offset_horizontal=width/50 | |
# add left and right glass points | |
parts_left=obtain_left_glass(offset_horizontal, offset_vertical, shape) | |
parts_right=obtain_right_glass(offset_horizontal, offset_vertical, shape) | |
# copy original image, do not touch at | |
out_image=image.copy() | |
# draw lines | |
draw = ImageDraw.Draw(out_image) | |
draw.line(parts_left, fill=(0, 0, 0), width=10) | |
draw.line(parts_right, fill=(0, 0, 0), width=10) | |
return out_image | |
def obtain_right_glass(offset_horizontal, offset_vertical, shape): | |
parts_right=[] | |
# add tip of nose | |
part = shape.part(28) | |
_x = part.x + 2 * offset_horizontal | |
_y = part.y | |
parts_right.append((_x, _y)) | |
# add eyebrow | |
for i in xrange(22, 27, 1): | |
_x = shape.part(i).x | |
_y = shape.part(i).y - offset_vertical | |
parts_right.append((_x, _y)) | |
# right top of face | |
part = shape.part(16) | |
_x = part.x | |
_y = part.y - offset_vertical | |
parts_right.append((_x, _y)) | |
# right nostril | |
part = shape.part(35) | |
_x = part.x + 4 * offset_horizontal | |
_y = part.y - 4 * offset_vertical | |
parts_right.append((_x, _y)) | |
# add tip of nose again to close loop | |
part = shape.part(28) | |
_x = part.x + 2 * offset_horizontal | |
_y = part.y | |
parts_right.append((_x, _y)) | |
return parts_right | |
def obtain_left_glass(offset_horizontal, offset_vertical, shape): | |
parts_left = [] | |
# left top of face | |
part = shape.part(0) | |
_x = part.x | |
_y = part.y + offset_vertical | |
parts_left.append((_x, _y)) | |
# add eyebrow | |
for i in xrange(17, 22, 1): | |
_x = shape.part(i).x | |
_y = shape.part(i).y - offset_vertical | |
parts_left.append((_x, _y)) | |
# add tip of nose | |
part = shape.part(28) | |
_x = part.x - 2 * offset_horizontal | |
_y = part.y | |
parts_left.append((_x, _y)) | |
# left nostril | |
part = shape.part(31) | |
_x = part.x - 4 * offset_horizontal | |
_y = part.y - 4 * offset_vertical | |
parts_left.append((_x, _y)) | |
# left top of face to close the loop | |
part = shape.part(0) | |
_x = part.x | |
_y = part.y + offset_vertical | |
parts_left.append((_x, _y)) | |
return parts_left | |
def main(): | |
# read image and convert to np array | |
image=Image.open(faces_image_path) | |
# detect faces and landmarks | |
face_landmark_tuples = detect_landmarks(image=image) | |
for k,rect,shape in face_landmark_tuples: | |
# draw landmarks to image | |
out_image=draw_landmarks(image,shape=shape) | |
# determine file name and save new image | |
path_name=os.path.dirname(faces_image_path) | |
output_file=os.path.join(path_name,str(k)+os.path.basename(faces_image_path)) | |
out_image.save(output_file) | |
if __name__ =='__main__':main() |
Landmarks are returned in a shape object. Shape object contains part objects which are two dimensional points each of which corresponds to a landmark point. To understand which point corresponds to which landmark point look at the following image. It is taken from dlib link https://sourceforge.net/p/dclib/discussion/442518/thread/c038d05f/
Given application also contains sample methods to add circles around eyes to simulate glasses.
Comments
Post a Comment