diff options
Diffstat (limited to 'megapixels/app/processors/face_landmarks.py')
| -rw-r--r-- | megapixels/app/processors/face_landmarks.py | 31 |
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 |
