|
25 | 25 | from .. import helper
|
26 | 26 | from .. import data
|
27 | 27 |
|
| 28 | +TEMPLATE = np.float32([ |
| 29 | + (0.0792396913815, 0.339223741112), (0.0829219487236, 0.456955367943), |
| 30 | + (0.0967927109165, 0.575648016728), (0.122141515615, 0.691921601066), |
| 31 | + (0.168687863544, 0.800341263616), (0.239789390707, 0.895732504778), |
| 32 | + (0.325662452515, 0.977068762493), (0.422318282013, 1.04329000149), |
| 33 | + (0.531777802068, 1.06080371126), (0.641296298053, 1.03981924107), |
| 34 | + (0.738105872266, 0.972268833998), (0.824444363295, 0.889624082279), |
| 35 | + (0.894792677532, 0.792494155836), (0.939395486253, 0.681546643421), |
| 36 | + (0.96111933829, 0.562238253072), (0.970579841181, 0.441758925744), |
| 37 | + (0.971193274221, 0.322118743967), (0.163846223133, 0.249151738053), |
| 38 | + (0.21780354657, 0.204255863861), (0.291299351124, 0.192367318323), |
| 39 | + (0.367460241458, 0.203582210627), (0.4392945113, 0.233135599851), |
| 40 | + (0.586445962425, 0.228141644834), (0.660152671635, 0.195923841854), |
| 41 | + (0.737466449096, 0.182360984545), (0.813236546239, 0.192828009114), |
| 42 | + (0.8707571886, 0.235293377042), (0.51534533827, 0.31863546193), |
| 43 | + (0.516221448289, 0.396200446263), (0.517118861835, 0.473797687758), |
| 44 | + (0.51816430343, 0.553157797772), (0.433701156035, 0.604054457668), |
| 45 | + (0.475501237769, 0.62076344024), (0.520712933176, 0.634268222208), |
| 46 | + (0.565874114041, 0.618796581487), (0.607054002672, 0.60157671656), |
| 47 | + (0.252418718401, 0.331052263829), (0.298663015648, 0.302646354002), |
| 48 | + (0.355749724218, 0.303020650651), (0.403718978315, 0.33867711083), |
| 49 | + (0.352507175597, 0.349987615384), (0.296791759886, 0.350478978225), |
| 50 | + (0.631326076346, 0.334136672344), (0.679073381078, 0.29645404267), |
| 51 | + (0.73597236153, 0.294721285802), (0.782865376271, 0.321305281656), |
| 52 | + (0.740312274764, 0.341849376713), (0.68499850091, 0.343734332172), |
| 53 | + (0.353167761422, 0.746189164237), (0.414587777921, 0.719053835073), |
| 54 | + (0.477677654595, 0.706835892494), (0.522732900812, 0.717092275768), |
| 55 | + (0.569832064287, 0.705414478982), (0.635195811927, 0.71565572516), |
| 56 | + (0.69951672331, 0.739419187253), (0.639447159575, 0.805236879972), |
| 57 | + (0.576410514055, 0.835436670169), (0.525398405766, 0.841706377792), |
| 58 | + (0.47641545769, 0.837505914975), (0.41379548902, 0.810045601727), |
| 59 | + (0.380084785646, 0.749979603086), (0.477955996282, 0.74513234612), |
| 60 | + (0.523389793327, 0.748924302636), (0.571057789237, 0.74332894691), |
| 61 | + (0.672409137852, 0.744177032192), (0.572539621444, 0.776609286626), |
| 62 | + (0.5240106503, 0.783370783245), (0.477561227414, 0.778476346951)]) |
| 63 | + |
| 64 | +TPL_MIN, TPL_MAX = np.min(TEMPLATE, axis=0), np.max(TEMPLATE, axis=0) |
| 65 | +MINMAX_TEMPLATE = (TEMPLATE - TPL_MIN) / (TPL_MAX - TPL_MIN) |
28 | 66 |
|
29 | 67 | class NaiveDlib:
|
| 68 | + OUTER_EYES_AND_BOTTOM_LIP = np.array([39, 42, 57]) |
| 69 | + INNER_EYES_AND_NOSE = np.array([36, 45, 33]) |
30 | 70 |
|
31 |
| - def __init__(self, faceMean, facePredictor): |
| 71 | + def __init__(self, facePredictor): |
32 | 72 | """Initialize the dlib-based alignment."""
|
33 | 73 | self.detector = dlib.get_frontal_face_detector()
|
34 |
| - self.normMeanLandmarks = loadMeanPoints(faceMean) |
35 | 74 | self.predictor = dlib.shape_predictor(facePredictor)
|
36 | 75 |
|
37 |
| - def getAllFaceBoundingBoxes(self, img): |
38 |
| - return self.detector(img, 1) |
| 76 | + def getAllFaceBoundingBoxes(self, rgbImg): |
| 77 | + try: |
| 78 | + return self.detector(rgbImg, 1) |
| 79 | + except Exception as e: |
| 80 | + print("Warning: {}".format(e)) |
| 81 | + # In rare cases, exceptions are thrown. |
| 82 | + return [] |
39 | 83 |
|
40 |
| - def getLargestFaceBoundingBox(self, img): |
41 |
| - faces = self.detector(img, 1) |
| 84 | + def getLargestFaceBoundingBox(self, rgbImg): |
| 85 | + faces = self.getAllFaceBoundingBoxes(rgbImg) |
42 | 86 | if len(faces) > 0:
|
43 | 87 | return max(faces, key=lambda rect: rect.width() * rect.height())
|
44 | 88 |
|
45 |
| - def align(self, img, bb): |
46 |
| - points = self.predictor(img, bb) |
| 89 | + def align(self, rgbImg, bb): |
| 90 | + points = self.predictor(rgbImg, bb) |
47 | 91 | return list(map(lambda p: (p.x, p.y), points.parts()))
|
48 | 92 |
|
49 |
| - EYES_AND_NOSE = np.array([36, 45, 33]) |
50 |
| - def alignImg(self, method, size, img, bb=None, |
51 |
| - landmarks=None, landmarkIndices=EYES_AND_NOSE): |
| 93 | + def alignImg(self, method, size, rgbImg, bb=None, |
| 94 | + landmarks=None, landmarkIndices=INNER_EYES_AND_NOSE): |
52 | 95 | if bb is None:
|
53 |
| - try: |
54 |
| - bb = self.getLargestFaceBoundingBox(img) |
55 |
| - except Exception as e: |
56 |
| - print("Warning: {}".format(e)) |
57 |
| - # In rare cases, exceptions are thrown. |
58 |
| - return |
| 96 | + bb = self.getLargestFaceBoundingBox(rgbImg) |
59 | 97 | if bb is None:
|
60 |
| - # Most failed detection attempts return here. |
61 | 98 | return
|
62 | 99 |
|
63 | 100 | if landmarks is None:
|
64 |
| - landmarks = self.align(img, bb) |
| 101 | + landmarks = self.align(rgbImg, bb) |
65 | 102 |
|
66 | 103 | npLandmarks = np.float32(landmarks)
|
67 |
| - npNormMeanLandmarks = np.float32(self.normMeanLandmarks) |
68 | 104 |
|
69 | 105 | if method == 'affine':
|
70 | 106 | H = cv2.getAffineTransform(npLandmarks[landmarkIndices],
|
71 |
| - size*npNormMeanLandmarks[landmarkIndices]) |
72 |
| - thumbnail = cv2.warpAffine(img, H, (size, size)) |
| 107 | + size*MINMAX_TEMPLATE[landmarkIndices]) |
| 108 | + thumbnail = cv2.warpAffine(rgbImg, H, (size, size)) |
73 | 109 | else:
|
74 | 110 | raise Exception('Unrecognized method: {}'.format(method))
|
75 | 111 |
|
76 | 112 | return thumbnail
|
77 |
| - |
78 |
| -def transformPoints(points, bb, toImgCoords): |
79 |
| - if toImgCoords: |
80 |
| - def scale(p): |
81 |
| - (x, y) = p |
82 |
| - return (int((x * bb.width()) + bb.left()), |
83 |
| - int((y * bb.height()) + bb.top())) |
84 |
| - else: |
85 |
| - def scale(p): |
86 |
| - (x, y) = p |
87 |
| - return (float(x - bb.left()) / bb.width(), |
88 |
| - float(y - bb.top()) / bb.height()) |
89 |
| - return list(map(scale, points)) |
90 |
| - |
91 |
| - |
92 |
| -def loadMeanPoints(modelFname): |
93 |
| - def parse(line): |
94 |
| - (x, y) = line.strip().split(",") |
95 |
| - return (float(x), float(y)) |
96 |
| - with open(modelFname, 'r') as f: |
97 |
| - return [parse(line) for line in f] |
98 |
| - |
99 |
| - |
100 |
| -def annotate(img, box, points=None, meanPoints=None): |
101 |
| - a = np.copy(img) |
102 |
| - bl = (box.left(), box.bottom()) |
103 |
| - tr = (box.right(), box.top()) |
104 |
| - cv2.rectangle(a, bl, tr, color=(153, 255, 204), thickness=3) |
105 |
| - for p in points: |
106 |
| - cv2.circle(a, center=p, radius=3, color=(102, 204, 255), thickness=-1) |
107 |
| - for p in meanPoints: |
108 |
| - cv2.circle(a, center=p, radius=3, color=(0, 0, 0), thickness=-1) |
109 |
| - return a |
|
0 commit comments