face_recognition实现人脸检测+活体检测(通过眨眼时的帧数判断)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import cv2
import face_recognition
import numpy as np
from PIL import ImageFont, ImageDraw, Image
 
 
class FaceRecognition(object):
    # 已知姓名的人脸编码
    known_face_encodings = []
    # 人脸对应名称
    known_face_names = []
    # 初始化眨眼的连续帧数
    blink_counter = 0
    # 初始化眨眼次数总和
    blink_total = 0
    # 眼长宽比例值
    EAR_THRESH = 0.15
    EAR_CONSEC_FRAMES_MIN = 1
    # 当EAR小于阈值时,接连多少帧一定发生眨眼动作
    EAR_CONSEC_FRAMES_MAX = 3
 
    def gel_konw_face_encodings(self):
        """
        获取已知姓名人脸照片的128维向量
        :return:
        """
        face_locations, fcj_face_encoding = self.get_face_info(file_path="小明.png")
        self.known_face_encodings = [fcj_face_encoding[0]]
        self.known_face_names = ["小明"]
 
    def get_face_info(self, img_ndarry=None, file_path="", only_first_face=True):
        """
        获取图片中第一个人脸位置和128位的人脸特征向量
        :param img_ndarry:
        :param file_path:
        :return:
        """
        if file_path:
            img_ndarry = face_recognition.load_image_file("付晨杰.png")
 
        # 缩小图片为原图的四分之一提高检测速度
        small_frame = cv2.resize(img_ndarry, (0, 0), fx=0.25, fy=0.25)
        rgb_small_frame = small_frame
        smaill_face_locations = face_recognition.face_locations(rgb_small_frame)
        if smaill_face_locations:
            if only_first_face:
                smaill_face_locations = [smaill_face_locations[0]]
            face_locations = [(top*4, right*4, bottom*4, left*4) for (top, right, bottom, left) in smaill_face_locations]
            face_encodings = face_recognition.face_encodings(rgb_small_frame, smaill_face_locations)
            return face_locations, face_encodings
        else:
            return [], []
 
    def compare_faces(self, known_face_encodings, unkonwn_face_encoding):
        """人脸对比"""
        results = face_recognition.compare_faces(known_face_encodings, unkonwn_face_encoding, tolerance=0.4)
        for index, item in enumerate(results):
            if item:
                return self.known_face_names[index]
        return "unkonwn"
 
    def cv2_img_add_text(self, img, text, left, top, textColor=(0, 255, 0), textSize=20):
        """
        cv2无法显示中文,因此需要通过PIL并定义字体库显示,然后重新转化为OpenCV格式
        :param img:
        :param text:
        :param left:
        :param top:
        :param textColor:
        :param textSize:
        :return:
        """
        if (isinstance(img, np.ndarray)):  # 判断是否OpenCV图片类型
            img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        # 创建一个可以在给定图像上绘图的对象
        draw = ImageDraw.Draw(img)
        # 字体的格式
        fontStyle = ImageFont.truetype(
            "font/Alimama_ShuHeiTi_Bold.ttf", textSize, encoding="utf-8")
        # 绘制文本
        draw.text((left, top), text, textColor, font=fontStyle)
        # 转换回OpenCV格式
        return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
 
    def eye_aspect_ratio(self, eye):
        """
        获取眼睛长宽比
        :param eye:
        :return:
        """
        # (|e1-e5|+|e2-e4|) / (2|e0-e3|)
        A = np.linalg.norm(eye[1] - eye[5])
        B = np.linalg.norm(eye[2] - eye[4])
        C = np.linalg.norm(eye[0] - eye[3])
        ear = (A + B) / (2.0 * C)
        return ear
 
    def biopsy_by_blink(self, img_ndarry, face_location, check_eye=True):
        """
        活体检测:眨眼检测
        :return:
        """
        face_landmarks_list = face_recognition.face_landmarks(img_ndarry, [face_location])[0]
        left_eyebrow = face_landmarks_list['left_eye']
        right_eyebrow = face_landmarks_list['right_eye']
 
        if check_eye:
            left_eyebrow = np.array(left_eyebrow)
            right_eyebrow = np.array(right_eyebrow)
            left_ear = self.eye_aspect_ratio(left_eyebrow)  # 计算左眼EAR
            right_ear = self.eye_aspect_ratio(right_eyebrow)  # 计算右眼EAR
            ear = (left_ear + right_ear) / 2.0  # 求左右眼EAR的均值
            # EAR低于阈值,有可能发生眨眼,眨眼连续帧数加一次
            if ear < self.EAR_THRESH:
                self.blink_counter += 1
 
            # EAR高于阈值,判断前面连续闭眼帧数,如果在合理范围内,说明发生眨眼
            else:
                if self.EAR_CONSEC_FRAMES_MIN <= self.blink_counter <= self.EAR_CONSEC_FRAMES_MAX:
                    self.blink_total += 1
                self.blink_counter = 0
 
            # # for item in  left_eyebrow:
            # cv2.circle(img_ndarry, left_eyebrow[0], 1, (255, 0, 0), 2)
            # cv2.circle(img_ndarry, left_eyebrow[1], 1, (255, 0, 0), 2)
 
    def test(self):
        capture = cv2.VideoCapture(0)
        while True:
            ret, frame = capture.read()
            if ret is False:
                print("未检测到摄像头")
                exit(0)
            face_locations, face_encodings = self.get_face_info(img_ndarry=frame)
            if face_locations:
                # 循环处理个个人脸
                for index, face_encoding in enumerate(face_encodings):
                    # 对比人脸显示人员名称
                    name = self.compare_faces(self.known_face_encodings, face_encoding)
 
                    # 活体检测-眨眼
                    self.biopsy_by_blink(frame, face_locations[index])
                    if self.blink_total > 1:
                        name += "检测到眨眼"
 
                    # 长方形显示人脸区域
                    top, right, bottom, left = face_locations[index]
                    cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
 
                    # 显示人员名称
                    frame = self.cv2_img_add_text(frame, name, left + 6, bottom - 6)
 
                cv2.imshow('Video', frame)
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
            else:
                print("未检测到人脸")
        # 释放摄像头
        capture.release()
        capture.destroyAllWindows()
 
 
if __name__ == "__main__":
    a = FaceRecognition()
    a.gel_konw_face_encodings()
    a.test()

  

posted @   你看起来真的很好吃  阅读(402)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示