| |
| |
| |
| |
| |
|
|
| import numpy as np |
| import cv2 as cv |
|
|
| class FacialExpressionRecog: |
| def __init__(self, modelPath, backendId=0, targetId=0): |
| self._modelPath = modelPath |
| self._backendId = backendId |
| self._targetId = targetId |
|
|
| self._model = cv.dnn.readNet(self._modelPath) |
| self._model.setPreferableBackend(self._backendId) |
| self._model.setPreferableTarget(self._targetId) |
|
|
| self._align_model = FaceAlignment() |
|
|
| self._inputNames = 'data' |
| self._outputNames = ['label'] |
| self._inputSize = [112, 112] |
| self._mean = np.array([0.5, 0.5, 0.5])[np.newaxis, np.newaxis, :] |
| self._std = np.array([0.5, 0.5, 0.5])[np.newaxis, np.newaxis, :] |
|
|
| @property |
| def name(self): |
| return self.__class__.__name__ |
|
|
| def setBackendAndTarget(self, backendId, targetId): |
| self._backendId = backendId |
| self._targetId = targetId |
| self._model.setPreferableBackend(self._backendId) |
| self._model.setPreferableTarget(self._targetId) |
|
|
| def _preprocess(self, image, bbox): |
| if bbox is not None: |
| image = self._align_model.get_align_image(image, bbox[4:].reshape(-1, 2)) |
| image = cv.cvtColor(image, cv.COLOR_BGR2RGB) |
| image = image.astype(np.float32, copy=False) / 255.0 |
| image -= self._mean |
| image /= self._std |
| return cv.dnn.blobFromImage(image) |
|
|
| def infer(self, image, bbox=None): |
| |
| inputBlob = self._preprocess(image, bbox) |
|
|
| |
| self._model.setInput(inputBlob, self._inputNames) |
| outputBlob = self._model.forward(self._outputNames) |
|
|
| |
| results = self._postprocess(outputBlob) |
|
|
| return results |
|
|
| def _postprocess(self, outputBlob): |
| result = np.argmax(outputBlob[0], axis=1).astype(np.uint8) |
| return result |
|
|
| @staticmethod |
| def getDesc(ind): |
| _expression_enum = ["angry", "disgust", "fearful", "happy", "neutral", "sad", "surprised"] |
| return _expression_enum[ind] |
|
|
|
|
| class FaceAlignment(): |
| def __init__(self, reflective=False): |
| self._std_points = np.array([[38.2946, 51.6963], [73.5318, 51.5014], [56.0252, 71.7366], [41.5493, 92.3655], [70.7299, 92.2041]]) |
| self.reflective = reflective |
|
|
| def __tformfwd(self, trans, uv): |
| uv = np.hstack((uv, np.ones((uv.shape[0], 1)))) |
| xy = np.dot(uv, trans) |
| xy = xy[:, 0:-1] |
| return xy |
|
|
| def __tforminv(self, trans, uv): |
| Tinv = np.linalg.inv(trans) |
| xy = self.__tformfwd(Tinv, uv) |
| return xy |
|
|
| def __findNonreflectiveSimilarity(self, uv, xy, options=None): |
| options = {"K": 2} |
|
|
| K = options["K"] |
| M = xy.shape[0] |
| x = xy[:, 0].reshape((-1, 1)) |
| y = xy[:, 1].reshape((-1, 1)) |
| |
|
|
| tmp1 = np.hstack((x, y, np.ones((M, 1)), np.zeros((M, 1)))) |
| tmp2 = np.hstack((y, -x, np.zeros((M, 1)), np.ones((M, 1)))) |
| X = np.vstack((tmp1, tmp2)) |
| |
| |
|
|
| u = uv[:, 0].reshape((-1, 1)) |
| v = uv[:, 1].reshape((-1, 1)) |
| U = np.vstack((u, v)) |
| |
| |
|
|
| |
| if np.linalg.matrix_rank(X) >= 2 * K: |
| r, _, _, _ = np.linalg.lstsq(X, U, rcond=-1) |
| |
| r = np.squeeze(r) |
| else: |
| raise Exception("cp2tform:twoUniquePointsReq") |
|
|
| sc = r[0] |
| ss = r[1] |
| tx = r[2] |
| ty = r[3] |
|
|
| Tinv = np.array([[sc, -ss, 0], [ss, sc, 0], [tx, ty, 1]]) |
| T = np.linalg.inv(Tinv) |
| T[:, 2] = np.array([0, 0, 1]) |
|
|
| return T, Tinv |
|
|
| def __findSimilarity(self, uv, xy, options=None): |
| options = {"K": 2} |
|
|
| |
| |
|
|
| |
| trans1, trans1_inv = self.__findNonreflectiveSimilarity(uv, xy, options) |
|
|
| |
| xyR = xy |
| xyR[:, 0] = -1 * xyR[:, 0] |
| |
| trans2r, trans2r_inv = self.__findNonreflectiveSimilarity(uv, xyR, options) |
|
|
| |
| TreflectY = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, 1]]) |
| trans2 = np.dot(trans2r, TreflectY) |
|
|
| |
| xy1 = self.__tformfwd(trans1, uv) |
| norm1 = np.linalg.norm(xy1 - xy) |
| xy2 = self.__tformfwd(trans2, uv) |
| norm2 = np.linalg.norm(xy2 - xy) |
|
|
| if norm1 <= norm2: |
| return trans1, trans1_inv |
| else: |
| trans2_inv = np.linalg.inv(trans2) |
| return trans2, trans2_inv |
|
|
| def __get_similarity_transform(self, src_pts, dst_pts): |
| if self.reflective: |
| trans, trans_inv = self.__findSimilarity(src_pts, dst_pts) |
| else: |
| trans, trans_inv = self.__findNonreflectiveSimilarity(src_pts, dst_pts) |
| return trans, trans_inv |
|
|
| def __cvt_tform_mat_for_cv2(self, trans): |
| cv2_trans = trans[:, 0:2].T |
| return cv2_trans |
|
|
| def get_similarity_transform_for_cv2(self, src_pts, dst_pts): |
| trans, trans_inv = self.__get_similarity_transform(src_pts, dst_pts) |
| cv2_trans = self.__cvt_tform_mat_for_cv2(trans) |
| return cv2_trans, trans |
|
|
| def get_align_image(self, image, lm5_points): |
| assert lm5_points is not None |
| tfm, trans = self.get_similarity_transform_for_cv2(lm5_points, self._std_points) |
| return cv.warpAffine(image, tfm, (112, 112)) |
|
|