你在互联网上找到的大多数人脸识别算法和研究论文都会遭受照片攻击。这些方法在检测和识别来自网络摄像头的图像、视频和视频流中的人脸方面是非常有效,但是他们无法区分现实生活中的面孔和照片上的面孔。这种无法区别现实人脸的现象是由于这些算法是在二维帧上工作的。

现在让我们去试想一下,我们实现一个人脸识别系统,该系统可以很好地区分已知面孔和未知面孔,以便只有授权人员才能访问,尽管如此,一个心怀不轨的人只要出示授权人的照片也能访问。至此一个3D人脸的识别系统,类似于苹果的FaceID,应运而生了,但如果我们没有3D探测器该怎么办呢?

本文的目标是实现一种基于眨眼检测的人脸活体检测算法,以抵抗照片攻击。该算法通过网络摄像头实时工作,通过检测眨眼来区分现实生活中的面孔和照片上的面孔。通俗地说,程序运行如下:

  1. 在网络摄像头生成的每个帧中检测人脸。

  2. 对于每个检测到的脸,检测眼睛。

  3. 对于每个检测到的眼睛,检测眼睛是否睁开或关闭。

  4. 如果在某个时候检测到眼睛是睁开的,然后是闭着的,然后是睁开的,我们就断定此人已经眨了眼睛,并且程序显示他的名字(如果是人脸识别开门器,我们将授权此人进入)。

对于人脸的检测和识别,你需要安装face_recognition库,它提供了非常有用的深度学习方法来查找和识别图像中的人脸,特别是,face_locations、face_encodings和compare_faces函数是最有用的3个函数。人脸定位方法可以用两种方法来检测人脸:方向梯度直方图(HoG)和卷积神经网络(CNN),由于时间限制,选择了HoG方法。

face_encodings函数是一个预先训练的卷积神经网络,能够将图像编码成128哥元素的一维特征向量,这个嵌入向量包含足够的特征信息来区分两个不同的人,最后,compare_faces计算两个嵌入向量之间的距离。它将允许算法识别从摄像头帧中提取的人脸,并将其嵌入向量与我们数据集中所有编码的人脸进行比较,距离最近的向量对应于同一个人。

1. 已知人脸数据集编码

在我的例子中,算法能够识别我和奥巴马,我为每个人挑选了大约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}

现在我们知道了每个想识别的人的编码,我们可以尝试通过网络摄像头识别人脸,然而,在转到这一部分之前,我们需要区分一张人脸照片和一张活人的脸。

2.人脸活体检测

我们的目标是在某个点上检测出一个睁闭的睁眼模式。我训练了一个卷积神经网络来分类眼睛是闭着的还是睁着的,所选择的模型是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 Sequentialfrom keras.layers import Conv2Dfrom keras.layers import AveragePooling2Dfrom keras.layers import Flattenfrom keras.layers import Densefrom keras.preprocessing.image import ImageDataGenerator
IMG_SIZE = 24def train(train_generator, val_generator):    STEP_SIZE_TRAIN=train_generator.n//train_generator.batch_size  STEP_SIZE_VALID=val_generator.n//val_generator.batch_sizemodel = 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

在评估模型时,我达到了94%的准确率。

每次我们检测到一只眼睛,我们就用我们的模型来预测它的状态,并跟踪每个人的眼睛状态,因此,检测眨眼变得非常容易,它试图在眼睛状态历史中找到一个闭眼-睁眼-闭眼的过程。

def isBlinking(history, maxFrames):    """ @history: A string containing the history of eyes status          where a '1' means that the eyes were closed and '0' open.        @maxFrames: The maximal number of successive frames where an eye is closed """    for i in range(maxFrames):        pattern = '1' + '0'*(i+1) + '1'        if pattern in history:            return True    return False

3.活体的人脸识别

我们几乎拥有了建立“真实”人脸识别算法的所有要素,我们只需要一种实时检测人脸和眼睛的方法。我使用openCV预先训练的Haar级联分类器来完成这些任务。有关Haar cascade人脸和眼睛检测的更多信息,我强烈建议你阅读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

上面的功能是用于检测和识别真实人脸的代码,它接受以下参数:

  • model:我们的睁眼/闭眼分类器

  • video_capture:流视频

  • face_detector:Haar级联的人脸分类器。我使用了haarcascade_frontalface_alt.xml

  • open_eyes_detector:Haar级联睁眼分类器。我使用了haarcascade_eye_tree_eyeglasses.xml

  • left_eye_detector:Haar级联的左眼分类器。我使用了haarcascade_lefteye_2splits.xml,它可以检测睁眼或闭眼。

  • right_eye_detector:Haar级联的右眼分类器。我使用了haarcascade_righteye_2splits.xml,它可以检测睁眼或闭眼。

  • data:已知编码和已知名称的字典

  • eyes_detected:包含每个名称的眼睛状态历史记录的字典。

第2-4行,我们从网络摄像头流中获取一个帧,然后调整其大小以加快计算速度。

第10行,我们从帧中检测人脸,然后在第21行,我们将其编码为128-d矢量。

第23-38行,我们将这个向量与已知的人脸编码进行比较,并通过计算匹配的次数来确定此人的姓名,选择匹配次数最多的一个。

第45行开始,我们试着探测眼睛进入人脸框。

首先,我们尝试用睁眼检测器来检测睁眼,如果探测器探测成功,则在第54行,将“1”添加到眼睛状态历史记录中,这意味着眼睛是睁开的,因为睁开的眼睛探测器无法检测到闭着的眼睛;否则,如果第一个分类器失败(可能是因为眼睛是闭着的,或者仅仅是因为它不能识别眼睛),则使用左眼和右眼检测器,人脸被分为左右两侧,以便对各个探测器进行分类。

第92行开始,提取眼睛部分,训练后的模型预测眼睛是否闭合,如果检测到一只眼睛闭着,则两眼都将被预测为闭着,并将“0”添加到眼睛状态历史记录中;否则就可以断定眼睛是睁开的。

最后,在第110行,is blinking()函数用于检测眨眼,如果该人眨眼,则显示姓名。整个代码都可以在我的github帐户上找到。

  • 我的github帐户

    • https://github.com/Guarouba/face_rec

使用眨眼检测功能阻止照片攻击的演示视频:https://youtu.be/arQN6w0fZw8

参考文献

  • 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/

原文链接:https://towardsdatascience.com/real-time-face-liveness-detection-with-python-keras-and-opencv-c35dc70dafd3

留言送书福利

为了感谢大家长期以来的支持,小编会坚持不定期开展“留言送书”活动!

在本文文末留言即可参与活动,留言内容需为主题相关,多多留言会提升中奖概率哟~

先前,小编遇到了读者咨询,能不能选一本自己想看的书的情况,所以这次给出了选择,挑选了Python方向的两本书Python网络爬虫开发从入门到精通》和《Python自动化测试实战》。共两本送给2位粉丝,中奖后选择你想要的其中一本即可。

/  留言主题 /

你希望咱们公众号出现在自己的订阅号常读列表里么?理由是什么?

本次“留言送书”活动截至到6月12号,抽留言走心的粉丝2名 免费赠送任一本书籍(走心留言将经过筛选)。届时会公布中奖者评论截图及福利领取方式(重大漏洞!此次留言发小编mthler)

基于Python+Keras+OpenCV实现实时人脸活体检测 | 文末送书相关推荐

  1. python 活体检测_基于Python+Keras+OpenCV实现实时人脸活体检测 | 文末送书

    你在互联网上找到的大多数人脸识别算法和研究论文都会遭受照片***.这些方法在检测和识别来自网络摄像头的图像.视频和视频流中的人脸方面是很是有效,可是他们没法区分现实生活中的面孔和照片上的面孔.这种没法 ...

  2. 基于Python、Keras和OpenCV的实时人脸活体检测

    作者|Jordan Van Eetveldt 编译|Flin 来源|towardsdatascience 你在互联网上找到的大多数人脸识别算法和研究论文都遭受照片攻击.这些方法在检测和识别来自网络摄像 ...

  3. 活体检测算法 python_基于Python、Keras和OpenCV的实时人脸活体检测

    作者|Jordan Van Eetveldt 编译|Flin 来源|towardsdatascience 你在互联网上找到的大多数人脸识别算法和研究论文都遭受照片攻击.这些方法在检测和识别来自网络摄像 ...

  4. 【Python零基础到入门】Python基础语法篇——数字(Number) 学习 【文末送书】

  5. 文末送书 | 当Python遇上高考,会发生什么?

    (文末送书哦!) 延期一个月之后,1071万考生终于熬出头了. 这届高考太难了,不仅考学生,更是考验疫情的防控能力. 但是说到难,2018年浙江省教育厅的一个决定,让不少人感叹真难! 原来早在2017 ...

  6. Python中处理字符串的常用函数汇总【文末送书】

    正式的Python专栏第23篇,同学站住,别错过这个从0开始的文章! 今天我们说了字符串的基础,格式化,这次我们讲解字符串的常用函数,不要错过! (文本送书,评论区抽取一位送书) 前两篇都在本文同个专 ...

  7. 讲讲Python中的函数传递问题 【文末送书】

    正式的Python专栏第25篇,同学站住,别错过这个从0开始的文章! 前篇讲了python中的函数和高阶函数,这篇再把函数的其他部分补充一下,稍微轻松简单一些. 文末评论送书,学委会用这个抽奖程序来进 ...

  8. 文末送书 | 手把手教你玩转,Python 会交互的超强绘图库 Plotly!

    作者:Will Koehrsen,译者:欧剃,编辑:肉松 原文:https://towardsdatascience.com/the-next-level-of-data-visualization- ...

  9. 用python偷偷给班级群女同学的颜值进行排名,排最后的大姐说开学要打爆我(文末送书)...

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 流水不腐,户枢不蠹.--吕氏春秋 ...

最新文章

  1. php函数scandir_使用PHP函数scandir排除特定目录
  2. JQUERY的size()与length
  3. mysql5.7.24怎么打开_mysql-5.7.24-winx64安装教程
  4. mysql replication延迟_深入mysql主从复制延迟问题的详解
  5. C++ 通讯录设计(二)
  6. bootstrap-table全选当前页所有数据checkAll
  7. 与MQ通讯的完整JAVA程序
  8. 为什么你必须了解云原生?!
  9. SQL Server读懂语句运行的统计信息 SET STATISTICS TIME IO PROFILE ON
  10. 动态规划--代码随想录
  11. 《python黑帽子 黑客与渗透测试编程之道》第二章-网络基础 tcp、udp客户端、服务端
  12. android studio jdy08,JDY-08蓝牙4.0模块+V3.3手册+.pdf
  13. 主流H5、Js 3D游戏引擎和框架
  14. GoogleEarth崩了!!除了重装还可以这样解决......
  15. VBS整人代码 很多 测试把我给整安逸了
  16. Win10家庭版添加不了新账户的解决方法
  17. 通过 http-proxy-middleware 跨域代理与添加自定义cookie
  18. Mac键盘部分数字键和字母键失灵,无法使用怎么办?
  19. 微信生态圈的发展分析
  20. Android 开发笔记2.0

热门文章

  1. csharp高级练习题:凡我字谜在哪里?【难度:3级】--景越C#经典编程题库,不同难度C#练习题,适合自学C#的新手进阶训练
  2. 数据分析--综合评价
  3. 新车可以无牌上路7天_新车可以无牌上路7天吗?新车没上牌可以上高速吗
  4. Linux驱动学习——入门
  5. Ebay买家号注册流程
  6. SQL索引概念(详解B+树)
  7. 大数据曝光了!80后年薪多少,才能摆脱中年危机?
  8. 根据MACD指标自动盯盘软件的使用开发
  9. 完整的微信公众平台开发2
  10. NBUT - 1457 Sona (莫队)