148 lines
4.9 KiB
Python
148 lines
4.9 KiB
Python
|
import face_recognition
|
|||
|
import numpy as np
|
|||
|
import cv2
|
|||
|
import base64
|
|||
|
from PIL import Image, ImageDraw, ImageFont
|
|||
|
|
|||
|
|
|||
|
class FaceNotFoundException(Exception):
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
err = "Compare Failure,cannot find face in image"
|
|||
|
Exception.__init__(self, err)
|
|||
|
|
|||
|
|
|||
|
# base64转opencv图片
|
|||
|
def convert_to_image(base64_code):
|
|||
|
"""
|
|||
|
base64转图片
|
|||
|
:param base64_code:需要转换的base64码:
|
|||
|
:return image:openCv格式图片:
|
|||
|
"""
|
|||
|
# base64解码
|
|||
|
b64image = base64.b64decode(base64_code)
|
|||
|
# 转opencv图片
|
|||
|
image_array = np.frombuffer(b64image, np.uint8)
|
|||
|
result_image = cv2.imdecode(image_array, cv2.COLOR_BGR2RGB)
|
|||
|
# 返回opencv图片对象
|
|||
|
if result_image is None:
|
|||
|
return "error"
|
|||
|
else:
|
|||
|
return result_image
|
|||
|
|
|||
|
|
|||
|
# 缩放图片
|
|||
|
def resize_image(image, multiple=1.0):
|
|||
|
"""
|
|||
|
缩放图片
|
|||
|
:param image:需要缩放的图片:
|
|||
|
:param multiple:可选缩放倍数,默认120大小基准缩放:
|
|||
|
:return:
|
|||
|
"""
|
|||
|
# 默认以基准120大小等比例缩小
|
|||
|
base_height = 120
|
|||
|
image_height = image.shape[0]
|
|||
|
if multiple == 1:
|
|||
|
multiple = base_height / image_height
|
|||
|
result_image = cv2.resize(image, (0, 0), fx=multiple, fy=multiple)
|
|||
|
|
|||
|
return result_image
|
|||
|
|
|||
|
|
|||
|
# 获取单个人脸128特征点向量
|
|||
|
def get_face_encoding(face_image, face_locations=None):
|
|||
|
"""
|
|||
|
获取人脸特征点向量
|
|||
|
:param face_image: 已知人脸图片:
|
|||
|
:param face_locations: 人脸位置:
|
|||
|
:return face_encoding: 人脸特征点向量:
|
|||
|
"""
|
|||
|
face_encoding = face_recognition.face_encodings(face_image, face_locations)
|
|||
|
return face_encoding
|
|||
|
|
|||
|
|
|||
|
# 人脸比对
|
|||
|
def contrast_faces(known_image, unknown_image, tolerance=0.4):
|
|||
|
"""
|
|||
|
人脸比对
|
|||
|
:param known_image: 已知图片:
|
|||
|
:param unknown_image: 待检测图片:
|
|||
|
:param tolerance: 检测基准,数值越小结果越精确,同时可能出现比对失败:
|
|||
|
:return result:检测结果:
|
|||
|
"""
|
|||
|
# noinspection PyBroadException
|
|||
|
try:
|
|||
|
known_image_encoding = get_face_encoding(resize_image(known_image))[0]
|
|||
|
unknown_image_encoding = get_face_encoding(resize_image(unknown_image))[0]
|
|||
|
result = face_recognition.compare_faces([known_image_encoding], unknown_image_encoding, tolerance)
|
|||
|
return result
|
|||
|
except IndexError:
|
|||
|
return "nf"
|
|||
|
except Exception as e:
|
|||
|
print("recognition error!\n cause:{}".format(e))
|
|||
|
return "err"
|
|||
|
|
|||
|
|
|||
|
# 摄像头实时比对
|
|||
|
def real_time_comparison(known_face_encodings, known_face_names, tolerance=0.4):
|
|||
|
"""
|
|||
|
人脸实时比对
|
|||
|
:param known_face_encodings:已知人脸特征点向量数组:
|
|||
|
:param known_face_names:已知人脸名称:
|
|||
|
:param tolerance:检测基准,数值越小结果越精确,同时可能出现比对失败:
|
|||
|
:return None:无返回值:
|
|||
|
"""
|
|||
|
process_this_frame = True
|
|||
|
face_locations = ""
|
|||
|
face_names = ""
|
|||
|
cap = cv2.VideoCapture(0)
|
|||
|
while True:
|
|||
|
ret, frame = cap.read()
|
|||
|
|
|||
|
frame = cv2.flip(frame, 1, dst=None)
|
|||
|
|
|||
|
small_frame = resize_image(frame, 0.5)
|
|||
|
|
|||
|
rgb_small_frame = small_frame[:, :, ::-1]
|
|||
|
|
|||
|
if process_this_frame:
|
|||
|
face_locations = face_recognition.face_locations(rgb_small_frame)
|
|||
|
face_encodings = get_face_encoding(rgb_small_frame, face_locations)
|
|||
|
|
|||
|
face_names = []
|
|||
|
for face_encoding in face_encodings:
|
|||
|
matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance)
|
|||
|
name = "Unknown"
|
|||
|
mmax_index = matches.index(max(matches))
|
|||
|
mmax_value = matches[mmax_index]
|
|||
|
for index, match in enumerate(matches):
|
|||
|
matches[index] = False
|
|||
|
if mmax_value > (1-tolerance)*100:
|
|||
|
matches[mmax_index] = True
|
|||
|
if True in matches:
|
|||
|
first_match_index = matches.index(True)
|
|||
|
name = known_face_names[first_match_index]
|
|||
|
face_names.append(name)
|
|||
|
|
|||
|
process_this_frame = not process_this_frame
|
|||
|
for (top, right, bottom, left), name in zip(face_locations, face_names):
|
|||
|
top *= 2
|
|||
|
right *= 2
|
|||
|
bottom *= 2
|
|||
|
left *= 2
|
|||
|
cv2.rectangle(frame, (left - 10, top - 10), (right + 10, bottom + 10), (0, 0, 255), 1)
|
|||
|
cv2.rectangle(frame, (left, bottom - 20), (right, bottom), (0, 0, 100), cv2.FILLED)
|
|||
|
cv2_im = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
|||
|
pil_im = Image.fromarray(cv2_im)
|
|||
|
draw = ImageDraw.Draw(pil_im)
|
|||
|
font = ImageFont.truetype("simhei.ttf", 25, encoding="utf-8")
|
|||
|
draw.text((left + 20, bottom - 25), name, (255, 255, 255), font=font)
|
|||
|
frame = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
|
|||
|
|
|||
|
cv2.imshow('Video', frame)
|
|||
|
|
|||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
|||
|
break
|
|||
|
cap.release()
|
|||
|
cv2.destroyAllWindows()
|