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() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律