丰巢智能快递柜是目前中国市场上占有率最高的快递柜,在各城市居民小区中被广泛安装使用。还记得丰巢推出的“刷脸”取件功能却被一群小学生简单破解的事情吧,几位小学生在一次课外科学实验中发现:只要用一张打印照片就能代替真人刷脸,骗过小区里的丰巢智能柜,取出父母的包裹!
在现实场景中大多数人脸识别算法都会受到类似上述例子的攻击,找到算法的漏洞。常规的人脸识别算法在检测和识别人脸方面是非常有效,但是它们无法区分真实的人脸和照片的人脸。为解决类似问题,类似于苹果的FaceID的3D检测器应运而生,如果我们没有3D探测器该怎么办呢?
为解决上述问题,小编实现一种基于眨眼检测的人脸活体检测算法,该算法可以通过摄像头实时工作,来区分是正常人脸还是照片。
算法原理具体步骤如下:
在人脸的检测方面,需要安装face_recognition库。人脸定位方法可以用两种方法来检测人脸:方向梯度直方图(HoG)和卷积神经网络(CNN),我们选择选择了HoG方法。
face_encodings函数是一个预先训练的卷积神经网络,compare_faces计算两个嵌入向量之间的距离。它将允许算法识别从摄像头帧中提取的人脸,并将其嵌入向量与我们编码的人脸进行比较,距离最近的向量对应于同一个人。
我为每个人挑选了大约10张照片。下面是处理和编码已知人脸数据库的代码。
def process_and_encode(images):
known_encodings = []
known_names = []
print("[LOG] Encoding dataset ...")
for image_path in tqdm(images):
# 加载图片
image = cv2.imread(image_path)
# 将其从BGR转换为RGB
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# 检测图像中的脸并获取其位置(方框坐标)
boxes = face_recognition.face_locations(image, model='hog')
# 将人脸编码为128维嵌入向量
encoding = face_recognition.face_encodings(image, boxes)
# 人物名称是图像来源文件夹的名称
name = image_path.split(os.path.sep)[-2]
if len(encoding) > 0 :
known_encodings.append(encoding[0])
known_names.append(name)
return {
"encodings": known_encodings, "names": known_names}
现在我们知道了每个想识别的人的编码,下面我们需要区分一张人脸照片和一张活人的脸。
我们的目标是在某个点上检测出一个睁闭的睁眼模式。我训练了一个卷积神经网络来分类眼睛是闭着的还是睁着的,所选择的模型是LeNet-5,它已经在Closed Eyes In The Wild (CEW)数据集上进行了训练,它由大约4800张24x24大小的眼睛图像组成。
Closed Eyes In The Wild (CEW)数据集地址:
http://parnec.nuaa.edu.cn/xtan/data/ClosedEyeDatabases.html
from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import AveragePooling2D
from keras.layers import Flatten
from keras.layers import Dense
from keras.preprocessing.image import ImageDataGenerator
IMG_SIZE = 24
def train(train_generator, val_generator):
STEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size
STEP_SIZE_VALID=val_generator.n//val_generator.batch_size
model = Sequential()
model.add(Conv2D(filters=6, kernel_size=(3, 3), activation='relu', input_shape=(IMG_SIZE,IMG_SIZE,1)))
model.add(AveragePooling2D())
model.add(Conv2D(filters=16, kernel_size=(3, 3), activation='relu'))
model.add(AveragePooling2D())
model.add(Flatten())
model.add(Dense(units=120, activation='relu'))
model.add(Dense(units=84, activation='relu'))
model.add(Dense(units=1, activation = 'sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
print('[LOG] Training CNN')
model.fit_generator(generator=train_generator,
steps_per_epoch=STEP_SIZE_TRAIN,
validation_data=val_generator,
validation_steps=STEP_SIZE_VALID,
epochs=20
)
return model
每次我们检测到一只眼睛,我们就用我们的模型来预测它的状态,并跟踪每个人的眼睛状态,因此,检测眨眼变得非常容易,它试图在眼睛状态历史中找到一个闭眼-睁眼-闭眼的过程。
def isBlinking(history, maxFrames):
for i in range(maxFrames):
pattern = '1' + '0'*(i+1) + '1'
if pattern in history:
return True
return False
我们几乎拥有了建立“真实”人脸识别算法的所有要素,我们只需要一种实时检测人脸和眼睛的方法。建议你阅读openCV的这篇强大的文章。
https://docs.opencv.org/3.4.3/d7/d8b/tutorial_py_face_detection.html
def detect_and_display(model, video_capture, face_detector, open_eyes_detector, left_eye_detector, right_eye_detector, data, eyes_detected):
frame = video_capture.read()
# 调整框架大小
frame = cv2.resize(frame, (0, 0), fx=0.6, fy=0.6)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# 检测人脸
faces = face_detector.detectMultiScale(
gray,
scaleFactor=1.2,
minNeighbors=5,
minSize=(50, 50),
flags=cv2.CASCADE_SCALE_IMAGE
)
# 对于每个检测到的脸
for (x,y,w,h) in faces:
# 将人脸编码为128维嵌入向量
encoding = face_recognition.face_encodings(rgb, [(y, x+w, y+h, x)])[0]
# 将向量与所有已知的人脸编码进行比较
matches = face_recognition.compare_faces(data["encodings"], encoding)
# 目前我们不知道该人的名字
name = "Unknown"
# 如果至少有一次匹配:
if True in matches:
matchedIdxs = [i for (i, b) in enumerate(matches) if b]
counts = {
}
for i in matchedIdxs:
name = data["names"][i]
counts[name] = counts.get(name, 0) + 1
# 匹配次数最多的已知编码对应于检测到的人脸名称
name = max(counts, key=counts.get)
face = frame[y:y+h,x:x+w]
gray_face = gray[y:y+h,x:x+w]
eyes = []
# 眼睛检测
# 首先检查眼睛是否睁开(考虑到眼镜)
open_eyes_glasses = open_eyes_detector.detectMultiScale(
gray_face,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags = cv2.CASCADE_SCALE_IMAGE
)
# 如果open_eyes_glasses检测到眼睛,则眼睛睁开
if len(open_eyes_glasses) == 2:
eyes_detected[name]+='1'
for (ex,ey,ew,eh) in open_eyes_glasses:
cv2.rectangle(face,(ex,ey),(ex+ew,ey+eh),(0,255,0),2)
# 否则尝试使用left和right_eye_detector检测眼睛
# 以检测到睁开和闭合的眼睛
else:
# 将脸分成左右两边
left_face = frame[y:y+h, x+int(w/2):x+w]
left_face_gray = gray[y:y+h, x+int(w/2):x+w]
right_face = frame[y:y+h, x:x+int(w/2)]
right_face_gray = gray[y:y+h, x:x+int(w/2)]
# 检测左眼
left_eye = left_eye_detector.detectMultiScale(
left_face_gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags = cv2.CASCADE_SCALE_IMAGE
)
# 检测右眼
right_eye = right_eye_detector.detectMultiScale(
right_face_gray,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30),
flags = cv2.CASCADE_SCALE_IMAGE
)
eye_status = '1' # we suppose the eyes are open
# 检查每只眼睛是否闭合。
# 如果有人闭着眼睛,我们得出结论是闭着眼睛
for (ex,ey,ew,eh) in right_eye:
color = (0,255,0)
pred = predict(right_face[ey:ey+eh,ex:ex+ew],model)
if pred == 'closed':
eye_status='0'
color = (0,0,255)
cv2.rectangle(right_face,(ex,ey),(ex+ew,ey+eh),color,2)
for (ex,ey,ew,eh) in left_eye:
color = (0,255,0)
pred = predict(left_face[ey:ey+eh,ex:ex+ew],model)
if pred == 'closed':
eye_status='0'
color = (0,0,255)
cv2.rectangle(left_face,(ex,ey),(ex+ew,ey+eh),color,2)
eyes_detected[name] += eye_status
# 每次,我们都会检查该人是否眨眼
# 如果是,我们显示其名字
if isBlinking(eyes_detected[name],3):
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 显示名字
y = y - 15 if y - 15 > 15 else y + 15
cv2.putText(frame, name, (x, y), cv2.FONT_HERSHEY_SIMPLEX,0.75, (0, 255, 0), 2)
return frame
上面的功能是用于检测和识别真实人脸的代码,参数说明如下:
https://docs.opencv.org/3.4.3/d7/d8b/tutorial_py_face_detection.html
https://www.pyimagesearch.com/2018/06/18/face-recognition-with-opencv-python-and-deep-learning
欢迎转载、收藏本文,码字不易,有所收获点赞支持一下!
为方便进行学习交流,本号开通了技术交流群,添加方式如下:
直接添加小助手微信号:pythoner666,备注:CSDN+python,或者按照如下方式添加均可!