人脸位置检测

face_locations = face_recognition.face_locations(frame)

检测图像中的人脸

参数说明

frame:按照RGB排列的彩色图像

返回值

face_locations:[(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置(左上角和右下角坐标)

人脸数据编码

face_encodings = face_recognition.face_encodings(frame, face_locations)

对检测到的人脸进行编码

参数说明

frame:按照RGB排列的彩色图像
face_locations:检测到的人脸位置

返回值

face_encodings:人脸编码数据形成的数组,数组大小与传入的face_locations一致

人脸比对

face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)

对检测到的人脸进行比对

参数说明

known_face_encodings:已有的人脸编码数组
face_encoding:待比对的人脸编码

返回值

face_distances:和已有人脸编码的距离,距离越小则说明越接近,低于某个阈值则可认为是相同的人脸

使用示例:打开摄像头,识别画面中的人脸

把人脸图片存放在~/Lepi_Data/ros/face_recognizer/known_face目录下,运行下面的程序就能识别到对应的人脸

  1. #!coding:utf-8
  2. import cv2
  3. from PIL import Image, ImageFont, ImageDraw
  4. import os
  5. import time
  6. import face_recognition
  7. import numpy as np
  8. Key_Esc = 27
  9. defaultFont = ImageFont.truetype(
  10. '/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc', 48)
  11. def putText(frame, text, pos, color, font=defaultFont):
  12. """
  13. 将文本显示在图片上
  14. Keyword arguments:
  15. frame: image 原图
  16. text: str 想要显示的文本
  17. pos: (x,y) 指定显示的初始坐标(左上角顶点)
  18. color: [r,g,b] 指定文本颜色的r、g、b值
  19. Returns:
  20. cv_img: image 叠加了文本的新图片(不改变原图)
  21. """
  22. if hasattr(text, "decode"):
  23. text = text.decode('utf-8')
  24. pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
  25. draw = ImageDraw.Draw(pil_image)
  26. draw.text(pos, text, font=font, fill=color)
  27. cv_img = cv2.cvtColor(np.asarray(pil_image), cv2.COLOR_RGB2BGR)
  28. return cv_img
  29. class FaceRecognizer(object):
  30. """
  31. FaceRecognizer类, 用来检测和识别人脸
  32. Attributes:
  33. data_dir: str 标记人脸图片的保存目录
  34. scale: int 缩放倍数,加快检测速度但会降低检测精度
  35. font: ImageFont 中文字体
  36. known_faces: dict 存放已标记人脸的字典
  37. threshold: float 检测阈值,小于该值才会进行相似度比较
  38. """
  39. def __init__(self, scale=5, threshold=0.45, fontSize=18):
  40. super(FaceRecognizer, self).__init__()
  41. self.data_dir = os.path.expanduser(
  42. '~') + "/Lepi_Data/ros/face_recognizer/known_face"
  43. self.scale = scale
  44. # self.font = ImageFont.truetype(
  45. # '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf', fontSize)
  46. self.known_faces = {}
  47. self.threshold = threshold
  48. self.face_locations = []
  49. self.face_names = []
  50. self.face_data = []
  51. self.add_label_success = False
  52. self.load_faces()
  53. def detect(self, frame, scale=None):
  54. """
  55. 人脸检测函数
  56. Keyword arguments:
  57. frame: image 原图
  58. scale: float 缩放倍数
  59. Returns:
  60. face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
  61. """
  62. if scale is None:
  63. scale = self.scale
  64. # Resize frame of video to 1/self.scale size for faster face recognition processing
  65. small_frame = cv2.resize(frame, (0, 0), fx=1.0/scale, fy=1.0/scale)
  66. # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
  67. rgb_small_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)
  68. self.face_locations = face_recognition.face_locations(rgb_small_frame)
  69. self.face_data = self.getFaceData(0)
  70. return self.rect_faces(frame, self.face_locations)
  71. def recognize(self, frame, scale=None):
  72. """
  73. 人脸识别函数
  74. Keyword arguments:
  75. frame: image 原图
  76. scale: float 缩放倍数
  77. Returns:
  78. face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
  79. face_names: ['未知'] 对应每个人脸的标签,未检测到用'未知'表示
  80. """
  81. if scale is None:
  82. scale = self.scale
  83. small_frame = cv2.resize(frame, (0, 0), fx=1.0/scale, fy=1.0/scale)
  84. rgb_small_frame = cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB)
  85. face_locations = face_recognition.face_locations(rgb_small_frame)
  86. start = time.time()
  87. face_encodings = face_recognition.face_encodings(
  88. rgb_small_frame, face_locations)
  89. face_names = []
  90. # python 3.9 默认生成dict_values 手动转成list进行比较
  91. known_face_encodings = list(self.known_faces.values())
  92. known_face_names = list(self.known_faces.keys())
  93. for face_encoding in face_encodings:
  94. name = "未知"
  95. if len(known_face_encodings) > 0:
  96. # matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
  97. # print(known_face_encodings, face_encoding)
  98. face_distances = face_recognition.face_distance(
  99. known_face_encodings, face_encoding)
  100. best_match_index = np.argmin(face_distances)
  101. # print(matches)
  102. print(face_distances)
  103. if face_distances[best_match_index] < self.threshold:
  104. name = known_face_names[best_match_index]
  105. face_names.append(name)
  106. end = time.time()
  107. print("recognized %d faces in %.2f ms" %
  108. (len(face_locations), (end - start)*1000))
  109. self.face_locations = face_locations
  110. self.face_names = face_names
  111. self.face_data = self.getFaceData(0)
  112. return self.label_faces(frame, self.face_locations, self.face_names)
  113. # return face_locations, face_names
  114. def getFaceData(self, index):
  115. if len(self.face_locations) > index:
  116. (top, right, bottom, left) = self.face_locations[index]
  117. top *= self.scale
  118. right *= self.scale
  119. bottom *= self.scale
  120. left *= self.scale
  121. return [int((right+left)/2), int((top+bottom)/2), int(right-left), int(bottom-top)]
  122. else:
  123. return [0, 0, 0, 0]
  124. def detectedFaceLabel(self, name):
  125. if name in self.face_names:
  126. index = self.face_names.index(name)
  127. self.face_data = self.getFaceData(index)
  128. return True
  129. else:
  130. self.face_data = []
  131. return False
  132. def rect_faces(self, frame, face_locations, scale=None):
  133. """
  134. 把检测到的人脸用矩形框出来
  135. Keyword arguments:
  136. frame: image 原图
  137. face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
  138. scale: float 缩放倍数
  139. Returns:
  140. frame: image 框出了人脸的新图像(改变原图)
  141. """
  142. if scale is None:
  143. scale = self.scale
  144. # Display the results
  145. for (top, right, bottom, left) in face_locations:
  146. # Scale back up face locations since the frame we detected in was scaled to 1/4 size
  147. top *= scale
  148. right *= scale
  149. bottom *= scale
  150. left *= scale
  151. # Draw a box around the face
  152. cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 4)
  153. # Draw a label with a name below the face
  154. return frame
  155. def label_faces(self, frame, face_locations, face_names, scale=None):
  156. """
  157. 给识别到的人脸添加标签
  158. Keyword arguments:
  159. frame: image 原图
  160. face_locations: [(x1,y1,x2,y2)] 表示所有检测到人脸的位置坐标数组,每个数组元素代表一个人脸的位置
  161. scale: float 缩放倍数,需要和检测时的一致,否则比例会出错
  162. Returns:
  163. frame: image 标记了人脸的新图像(不改变原图)
  164. """
  165. if scale is None:
  166. scale = self.scale
  167. frame = self.rect_faces(frame, face_locations, scale)
  168. # Display the results
  169. for (top, right, bottom, left), name in zip(face_locations, face_names):
  170. # Scale back up face locations since the frame we detected in was scaled to 1/4 size
  171. top *= scale
  172. right *= scale
  173. bottom *= scale
  174. left *= scale
  175. # Draw a box around the face
  176. # cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
  177. # Draw a label with a name below the face
  178. color = (0, 0, 255)
  179. frame = putText(frame, name, (left + 6, bottom ), color)
  180. return frame
  181. def load_faces(self):
  182. """
  183. 加载本地的人脸标签(默认存放在data_dir下)
  184. Keyword arguments:
  185. Returns:
  186. """
  187. if not os.path.exists(self.data_dir):
  188. os.makedirs(self.data_dir)
  189. files = os.listdir(self.data_dir)
  190. for file in files:
  191. try:
  192. name = file.split('.')[0]
  193. file_path = os.path.join(self.data_dir, file)
  194. print(self.add_face_label(cv2.imread(file_path), name, scale=1))
  195. except Exception as e:
  196. print(e)
  197. def add_face_label(self, frame, name, scale=None, save=False):
  198. """
  199. 动态添加人脸标签
  200. Keyword arguments:
  201. frame: image 原图
  202. name: str 标签名称
  203. scale: float 缩放倍数
  204. save: bool 是否保存,True则将保存图片至本地标签目录,每次启动会重新读取,False只在本次运行生效
  205. Returns:
  206. """
  207. if scale is None:
  208. scale = self.scale
  209. frame = cv2.resize(frame, (0, 0), fx=1.0/scale, fy=1.0/scale)
  210. rgb_frame = frame[:, :, ::-1]
  211. start = time.time()
  212. self.detect(frame, scale=1)
  213. face_locations = self.face_locations
  214. end = time.time()
  215. print("Found %d faces in %.2f ms" %
  216. (len(face_locations), (end - start)*1000))
  217. if len(face_locations) == 1:
  218. face_encoding = face_recognition.face_encodings(
  219. frame, face_locations)[0]
  220. self.known_faces[name] = face_encoding
  221. if save:
  222. file_path = os.path.join(self.data_dir, name+'.png')
  223. print(file_path)
  224. cv2.imwrite(file_path, frame)
  225. print('成功添加"%s"的标记' % (name))
  226. self.add_label_success = True
  227. return '标记成功'
  228. elif len(face_locations) > 1:
  229. self.add_label_success = False
  230. return '标记"%s"失败,检测到多余人脸' % (name)
  231. else:
  232. self.add_label_success = False
  233. return '标记"%s"失败,未检测到人脸' % (name)
  234. def remove_face_label(self, name):
  235. """
  236. 删除人脸标签
  237. Keyword arguments:
  238. name: str 标签名称
  239. Returns:
  240. """
  241. if self.known_faces.__contains__(name):
  242. self.known_faces.pop(name)
  243. try:
  244. os.system('rm '+os.path.join(self.data_dir, name+".*"))
  245. return "已删除"
  246. except Exception as e:
  247. print(e)
  248. return "删除出错"
  249. def test_recognize():
  250. """
  251. 测试函数,打开摄像头实时检测人脸, 按q退出
  252. Keyword arguments:
  253. Returns:
  254. """
  255. fr = FaceRecognizer()
  256. cap = cv2.VideoCapture(0)
  257. while True:
  258. # Grab a single frame of video
  259. ret, frame = cap.read()
  260. start = time.time()
  261. nframe = fr.recognize(frame)
  262. end = time.time()
  263. print("labeled %d faces in %.2f ms" %
  264. (len(fr.face_locations), (end - start)*1000))
  265. cv2.imshow("images", np.rot90(cv2.resize(nframe, (320, 240))))
  266. c = cv2.waitKey(1)
  267. if c == Key_Esc:
  268. break
  269. cap.release()
  270. cv2.destroyAllWindows()
  271. if __name__ == '__main__':
  272. test_recognize()