summaryrefslogtreecommitdiff
path: root/megapixels/app/processors/face_landmarks.py
diff options
context:
space:
mode:
authoradamhrv <adam@ahprojects.com>2019-01-16 13:30:16 +0100
committeradamhrv <adam@ahprojects.com>2019-01-16 13:30:16 +0100
commit65cb506ca182272e2701136097fd00c55dc6bd69 (patch)
treecc5be8e61a8d5173745be1d331b210e967e146b5 /megapixels/app/processors/face_landmarks.py
parentfceeb3b7adbc8d522e9fe1c40e12e9a529199068 (diff)
change bbox to norm, refine face extractor
Diffstat (limited to 'megapixels/app/processors/face_landmarks.py')
-rw-r--r--megapixels/app/processors/face_landmarks.py31
1 files changed, 19 insertions, 12 deletions
diff --git a/megapixels/app/processors/face_landmarks.py b/megapixels/app/processors/face_landmarks.py
index 171fc666..231e378f 100644
--- a/megapixels/app/processors/face_landmarks.py
+++ b/megapixels/app/processors/face_landmarks.py
@@ -30,6 +30,9 @@ class Landmarks2D:
self.log.warn('Define landmarks() function')
pass
+ def to_str(self, vec):
+ return ','.join([','.join(list(map(str,[x,y]))) for x,y in vec])
+
def flatten(self, points):
'''Converts list of point-tupes into a flattened list for CSV
:param points: (list) of x,y points
@@ -69,9 +72,9 @@ class FaceAlignment2D_68(Landmarks2D):
# predict landmarks
points = self.fa.get_landmarks(im) # returns array of arrays of 68 2D pts/face
# convert to data type
- points = [list(map(int, p)) for p in points[0]]
- return points
-
+ w,h = im.shape[:2][::-1]
+ points = [tuple(x/w, y/h) for x,y in points[0]]
+ return points # normalized
class Dlib2D(Landmarks2D):
@@ -82,15 +85,16 @@ class Dlib2D(Landmarks2D):
self.predictor = dlib.shape_predictor(model)
self.log.info(f'loaded predictor model: {model}')
- def landmarks(self, im, bbox):
+ def landmarks(self, im, bbox_norm):
'''Generates 68-pt landmarks using dlib predictor
:param im: (numpy.ndarray) BGR image
:param bbox: (app.models.BBox) dimensioned
- :returns (list) of (int, int) for x,y values
+ :returns (list) of (float, float) for normalized x,y values
'''
- bbox = bbox.to_dlib()
+ dim = im.shape[:2][::-1]
+ roi_dlib = bbox_norm.to_dim(dim).to_dlib()
im_gray = cv.cvtColor(im, cv.COLOR_BGR2GRAY)
- points = [[p.x, p.y] for p in self.predictor(im_gray, bbox).parts()]
+ points = [[p.x/dim[0], p.y/dim[1]] for p in self.predictor(im_gray, roi_dlib).parts()]
return points
@@ -121,13 +125,13 @@ class MTCNN2D_5(Landmarks2D):
from mtcnn.mtcnn import MTCNN
self.detector = MTCNN()
- def landmarks(self, im, bbox):
+ def landmarks(self, im, bbox_norm):
'''Detects face using MTCNN and returns (list) of BBox
:param im: (numpy.ndarray) image
:returns list of BBox
'''
results = []
- dim_wh = im.shape[:2][::-1] # (w, h)
+ dim = im.shape[:2][::-1] # (w, h)
# run MTCNN to get bbox and landmarks
dets = self.detector.detect_faces(im)
@@ -138,7 +142,7 @@ class MTCNN2D_5(Landmarks2D):
#rect = det['box']
points = det['keypoints']
# convert to normalized for contain-comparison
- points_norm = [np.array(pt)/dim_wh for pname, pt in points.items()]
+ points_norm = [np.array(pt)/dim for pname, pt in points.items()]
contains = False not in [bbox.contains(pn) for pn in points_norm]
if contains:
results.append(points) # append original points
@@ -185,14 +189,17 @@ class FaceAlignment3D_68(Landmarks3D):
device = f'cuda:{gpu}' if gpu > -1 else 'cpu'
self.fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._3D, device=device, flip_input=flip_input)
- def landmarks(self, im, rect):
+ def landmarks(self, im, bbox_norm):
'''Calculates the 3D facial landmarks
:param im: (numpy.ndarray) BGR image
- :param rect: (list) of face (x1, y1, x2, y2)
+ :param bbox_norm: (BBox) of face roi
:returns (list) of 68 (int) (tuples) as (x,y, z)
'''
# predict landmarks
+ dim = im.shape[:2][::-1]
+ rect = bbox_norm.to_dim(dim).to_xyxy()
points = self.fa.get_landmarks(im, [rect]) # returns array of arrays of 68 3D pts/face
# convert to data type
+ # TODO normalize this, but how to norm 3D?
points = [list(map(int, p)) for p in points[0]]
return points \ No newline at end of file