Source code for draugr.dlib_utilities.dlib_utilities

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

__author__ = "Christian Heider Nielsen"
__doc__ = r"""

           Created on 03-05-2021
           """

from enum import Enum
from typing import Sequence, Tuple, Union

import numpy
from scipy.spatial import distance

__all__ = [
    "mouth_aspect_ratio",
    "Dlib68faciallandmarksindices",
    "Dlib5faciallandmarksindices",
    "rect_to_bounding_box",
    "shape_to_ndarray",
    "eye_aspect_ratio",
]


class ExtendTuple(Tuple):
    def __or__(self, *other) -> "ExtendTuple":
        return ExtendTuple((*self, *other))

    def __and__(self, *other) -> "ExtendTuple":
        return ExtendTuple((*self, *other))


[docs]class Dlib68faciallandmarksindices(Enum): mouth = (48, 67 + 1) inner_mouth = (60, 67 + 1) right_eyebrow = (17, 21 + 1) left_eyebrow = (22, 26 + 1) right_eye = (36, 41 + 1) left_eye = (42, 47 + 1) nose = (27, 35 + 1) jaw = (0, 16 + 1)
[docs] @staticmethod def slice( seq: Sequence, ind: Union[ "Dlib68faciallandmarksindices", Tuple["Dlib68faciallandmarksindices"] ], ): """ :param seq: :param ind: :return: """ if isinstance(ind, Tuple): agg = [] for ( i ) in ( ind ): # Some flag implementation would probably be faster and more valid. agg.extend(Dlib68faciallandmarksindices.slice(seq, i)) return agg start, end = ind.value return seq[start:end]
def __or__( self, other: "Dlib68faciallandmarksindices" ) -> Tuple["Dlib68faciallandmarksindices"]: return ExtendTuple().__or__(self, other) def __and__( self, other: "Dlib68faciallandmarksindices" ) -> Tuple["Dlib68faciallandmarksindices"]: return ExtendTuple().__and__(self, other)
[docs]class Dlib5faciallandmarksindices(Enum): """ dlib_utilities’s 5-point facial landmark detector """ right_eye = (2, 3 + 1) left_eye = (0, 1 + 1) nose = (4, 4 + 1)
[docs] @staticmethod def slice( seq: Sequence, ind: Union["Dlib5faciallandmarksindices", Tuple["Dlib5faciallandmarksindices"]], ): """ :param seq: :param ind: :return: """ if isinstance(ind, Tuple): agg = [] for ( i ) in ( ind ): # Some flag implementation would probably be faster and more valid. agg.extend(Dlib5faciallandmarksindices.slice(seq, i)) return agg start, end = ind.value return seq[start:end]
def __or__( self, other: "Dlib5faciallandmarksindices" ) -> Tuple["Dlib5faciallandmarksindices"]: return ExtendTuple().__or__(self, other) def __and__( self, other: "Dlib5faciallandmarksindices" ) -> Tuple["Dlib5faciallandmarksindices"]: return ExtendTuple().__and__(self, other)
[docs]def rect_to_bounding_box(rect) -> Tuple[float, float, float, float]: """ # take a bounding predicted by dlib_utilities and convert it # to the format (x, y, w, h)""" x = rect.left() y = rect.top() return x, y, rect.right() - x, rect.bottom() - y # return a tuple of (x, y, w, h)
[docs]def shape_to_ndarray(shape, dtype: str = "int"): """ :param shape: :param dtype: :return: """ coordinates = numpy.zeros( (shape.num_parts, 2), dtype=dtype ) # initialize the list of (x, y)-coordinates # loop over all facial landmarks and convert them # to a 2-tuple of (x, y)-coordinates for i in range(0, shape.num_parts): coordinates[i] = (shape.part(i).x, shape.part(i).y) return coordinates # return the list of (x, y)-coordinates
[docs]def mouth_aspect_ratio(coordinates: Sequence[Sequence]) -> float: """ :param coordinates: :return:""" average = ( distance.euclidean(coordinates[3], coordinates[9]) + distance.euclidean(coordinates[2], coordinates[10]) + distance.euclidean(coordinates[4], coordinates[8]) ) / 3 return average / distance.euclidean(coordinates[0], coordinates[6])
[docs]def eye_aspect_ratio(coordinates: Sequence[Sequence]) -> float: """ :param coordinates: :return: """ # compute the euclidean distances between the two sets of # vertical eye landmarks (x, y)-coordinates A = distance.euclidean(coordinates[1], coordinates[5]) B = distance.euclidean(coordinates[2], coordinates[4]) # compute the euclidean distance between the horizontal # eye landmark (x, y)-coordinates C = distance.euclidean(coordinates[0], coordinates[3]) # compute the eye aspect ratio return (A + B) / (2.0 * C)
# To improve our blink detector, Soukupová and Čech recommend constructing a 13-dim feature vector of eye aspect ratios (N-th frame, N – 6 frames, and N + 6 frames), followed by feeding this feature vector into a Linear SVM for classification. if __name__ == "__main__": def asud(): """description""" a = list(range(99)) slices = ( Dlib68faciallandmarksindices.left_eye | Dlib68faciallandmarksindices.right_eye | Dlib68faciallandmarksindices.nose & Dlib68faciallandmarksindices.mouth ) print(Dlib68faciallandmarksindices.slice(a, slices)) def as34ud(): """description""" a = list(range(99)) print( Dlib68faciallandmarksindices.slice(a, Dlib68faciallandmarksindices.left_eye) ) asud() as34ud()