原文链接
dlib 使用OpenCV,Python和深度学习进行人脸识别 源代码
在 2019年7月7日 上张贴 由 hotdog发表回复
dlib 在今天的博客文章中,您将学习如何使用以下方法在图像和视频流中执行人脸识别:
OpenCV
python
深度学习
正如我们将要看到的,我们今天在这里使用的基于深度学习的面部嵌入都是(1)高度准确 和(2)能够实时执
使用OpenCV,Python和深度学习进行人脸识别
在本教程中,您将学习如何使用OpenCV,Python和深度学习进行面部识别。
我们将首先简要讨论基于 深度 学习的面部识别,包括“深度量学习”的概念。
我将帮助您安装实际执行面部识别所需的库。
最后,我们将对静止图像和视频流实现面部识别。
正如我们将要发现的那样,我们的面部识别实现将能够实时运行。
了解深度学习人脸识别嵌入
那么,深度学习+面部识别是如何工作的呢?
秘密是一种称为深度量学习的技术。
如果您有任何深度学习经验,您知道我们通常会训练网络:
接受单个输入图像
并输出该图像的分类/标签
但是,深度量度学习是不同的。
相反,我们尝试输出单个标签(甚至是图像中对象的坐标/边界框),而是输出实值特征向量。
对于dlib面部识别网络,输出特征向量是128-d(即,128个实数值的列表),其用于量化面部。使用三元组训练网络:
图1:通过深度量学习进行的面部识别涉及“三重训练步骤”。三胞胎由3个独特的面部图像组成 – 3个中的2个是同一个人。NN为3个面部图像中的每一个生成128-d矢量。对于同一个人的2个面部图像,我们调整神经网络权重以通过距离度量使矢量更接近。
在这里,我们为网络提供三个图像:
这些图像中的两个是同一个人的示例面部。
第三个图像是来自我们数据集的随机面,与其他两个图像不是同一个人。
作为一个例子,让我们再次考虑上面的图1,我们提供了三个图像:一个是Chad Smith,两个是Will Ferrell。
通过我们的网络量化,为每个面构建128-d嵌入(量化)。
从那里,一般的想法是我们将调整我们的神经网络的权重,以便两个Will Ferrel的128-d测量将彼此更接近并且远离Chad Smith的测量。
我们用于人脸识别的网络架构基于He等人的深度残留学习图像识别论文中的ResNet-34 ,但是层数较少,滤波器数量减少了一半。
该网络本身由Davis King在大约300万张图像的数据集上进行了训练。在野外标记面(LFW)数据集中,网络与其他最先进的方法进行比较,准确率达到99.38%。
Davis King(dlib的创建者)和Adam Geitgey( 我们将很快使用的face_recognition模块的作者)都写了详细的文章,介绍了基于学习的面部识别的深度:
深度度量学习的高质量人脸识别(戴维斯)
深度学习的现代人脸识别(Adam)
我强烈建议您阅读上述文章,了解有关深度学习面部嵌入的工作原理的详细信息。
安装面部识别库
为了使用Python和OpenCV执行面部识别,我们需要安装两个额外的库:
dlib
face_recognition
由Davis King维护的 dlib 库包含我们实现的“深度量学习”,用于构建用于实际识别过程的面部嵌入。
由Adam Geitgey创建的 face_recognition 库包含了dlib的面部识别功能,使其更易于使用。在2018年的PyImageConf上向Adam Geitgey和Davis King学习
我假设您已在系统上安装了OpenCV。如果没有,不用担心 – 只需访问我的OpenCV安装教程页面,然后按照适合您系统的指南进行操作。
从那里,让我们安装 dlib 和 face_recognition 包。
注意:对于以下安装,请确保您使用的是虚拟环境。我强烈推荐用于隔离项目的虚拟环境 – 这是Python的最佳实践。如果您已经按照我的OpenCV安装指南(并安装了 virtualenv +virtualenvwrapper ),那么您可以 在安装dlib 和 face_recognition之前使用 workon命令 。
dlib无GPU支持安装
如果您没有GPU,可以按照本指南使用pip 安装 dlib
$ workon # optional
$ pip install dlib
或者您可以从源代码编译
$ workon # optional
$ git clone https://github.com/davisking/dlib.git
$ cd dlib
$ mkdir build
$ cd build
$ cmake .. -DUSE_AVX_INSTRUCTIONS=1
$ cmake --build .
$ cd ..
$ python setup.py install --yes USE_AVX_INSTRUCTIONS
dlib使用GPU支持进行安装(可选)
如果你这样做有一个兼容CUDA的GPU可以安装 dlib 与GPU的支持,使得面部识别速度更快,效率更高。
为此,我建议 您从源代码安装 dlib,因为您可以更好地控制构建
$ workon # optional
$ git clone https://github.com/davisking/dlib.git
$ cd dlib
$ mkdir build
$ cd build
$ cmake .. -DDLIB_USE_CUDA=1 -DUSE_AVX_INSTRUCTIONS=1
$ cmake --build .
$ cd ..
$ python setup.py install --yes USE_AVX_INSTRUCTIONS --yes DLIB_USE_CUDA
安装face_recognition包
所述face_recognition模块是通过一个简单的PIP命令安装
$ workon # optional
$ pip install face_recognition
安装imutils
你还需要我的便利功能包,imutils。您可以通过pip在Python虚拟环境中安装它
$ workon # optional
$ pip install imutils
我们的人脸识别数据集
图2:使用Python和Bing Image Search API以编程方式创建了一个示例人脸识别数据集。显示了侏罗纪公园电影系列中的六个角色。
由于侏罗纪公园(1993)是我有史以来最喜欢的电影,为了纪念侏罗纪世界:堕落王国(2018年)本周五在美国发行,我们将对电影中角色的样本进行人脸识别。 :
艾伦·格兰特,古生物学家(22图像)
克莱尔·迪林,园区运营管理(53张图片)
Ellie Sattler,古植物学家(31图像)
伊恩马尔科姆,数学家(41图像)
约翰哈蒙德,商人/侏罗纪公园所有者(36张图片)
Owen Grady,恐龙研究员(35图像)
使用我的如何(快速)构建深度学习图像数据集教程中讨论的方法,在<30分钟内构建此数据集。鉴于此图像数据集,我们将:
为数据集中的每个面创建128-d嵌入
使用这些嵌入来识别图像和视频流中字符的面部
人脸识别项目结构
通过检查tree 命令的输出可以看到我们的项目结构
我们的项目有4个顶级目录:
dataset / :包含基于各自名称组织到子目录中的六个字符的面部图像。
examples / :有三个用于测试的图像不在数据集中。
output / :您可以在此处存储已处理的面部识别视频。我的一个文件夹 – 来自原侏罗纪公园电影的经典“午餐场景” 。
videos / :输入视频应存储在此文件夹中。此文件夹还包含“午餐场景”视频,但尚未经过人脸识别系统。
我们在根目录中还有6个文件:
search_bing_api .py :第1步是建立一个数据集(我已经为你完成了这个)。要了解如何使用Bing API使用我的脚本构建数据集,请参阅此博客文章。
encode_faces .py :使用此脚本构建面部的编码(128-d向量)。
recognize_faces_image.py :识别单个图像面(根据编码从数据集)。
recognize_faces_video.py :识别人脸从您的网络摄像头和输出视频的实时视频流。
recognize_faces_video_file.py :识别人脸在驻留在磁盘上并输出所处理的视频到硬盘的视频文件。我今天不会讨论这个文件,因为框架来自与视频流文件相同的骨架。
encodings .pickle :面部识别编码是通过 encode_faces .py从数据集生成的 ,然后序列化存储磁盘。
在创建图像数据集(使用 search_bing_api .py )之后,我们将运行 encode_faces .py 来构建嵌入。
从那里,我们将运行识别脚本来实际识别面部。
使用OpenCV和深度学习对面部进行编码
图3:通过深度学习的面部识别和使用face_recognition模块方法的Python 生成每面的128-d实值数字特征向量。
在我们识别图像和视频中的面部之前,我们首先需要量化训练集中的面部。请记住,我们实际上并没有在这里训练网络 –网络已经过训练,可以在大约300万张图像的数据集上创建128-d嵌入。
我们当然可以从头开始训练网络,甚至可以微调现有模型的权重,但这很可能对许多项目来说都是过度杀伤力。此外,您需要大量图像来从头开始训练网络。
相反,使用预先训练的网络更容易,然后使用它为我们的数据集中的218个面中的每一个构建128-d嵌入。
然后,在分类过程中,我们可以使用简单的k-NN模型+ votes 来进行最终的人脸分类。其他传统的机器学习模型也可以在这里使用。
要构建我们的面部嵌入,请 从与此博客帖子相关联的“下载”中打开 encode_faces .py
首先,我们需要导入所需的包。再次注意,此脚本需要安装 imutils , face_recognition 和OpenCV。向上滚动到“安装您的面部识别库”,以确保您的库已准备就绪。
让我们处理在运行时使用 argparse处理的命令行参数
如果您是PyImageSearch的新手,请允许我将您的注意力转移到上面的代码块,当您阅读更多我的博客文章时,您会对此熟悉。我们使用 argparse 来解析命令行参数。在命令行中运行Python程序时,可以在不离开终端的情况下向脚本提供其他信息。第10-17行不需要修改,因为它们解析来自终端的输入。如果这些行看起来不熟悉,请查看我关于命令行参数的博文。
让我们列出参数标志并讨论它们:
– dataset :我们数据集的路径(我们使用上周博客文章的方法#2中描述的search_bing_api .py创建了一个数据集 )。
– encodings :我们的面部编码被写入该参数所指向的文件中。
– encodings :在我们对图像中的面部进行编码之前 ,我们首先需要检测它们。或者两种面部检测方法包括 hog 或 cnn 。这两个标志是唯一适用于 – 检测- 方法的标志 。
现在我们已经定义了参数,让我们抓住数据集中文件的路径(以及执行两次初始化)
第21行使用输入数据集目录的路径来构建 其中包含的所有imagePath的列表 。
我们还需要在循环之前初始化两个列表, knownEncodings 和 knownNames 。这两个列表将包含数据集中每个人的面部编码和相应的名称(第24和25行)。
现在是时候开始循环我们的侏罗纪公园角色面孔了!
该循环将循环218次,对应于数据集中的218个面部图像。我们循环到第28行的每个图像的路径 。
从那里,我们将 在第32行从imagePath中提取人物 的 名称(因为我们的子目录被恰当地命名) 。
然后让我们 在将imagePath传递 给 cv2的同时 加载 图像。imread (第36行)。
OpenCV在BGR中订购颜色通道,但 dlib 实际上需要RGB。该 face_recognition 模块使用 dlib,所以我们开始之前,让我们交换的色彩空间上的37号线,命名新的图像 RGB 。
接下来,让我们本地化面部和计算编码
这是脚本的有趣部分!
对于循环的每次迭代,我们将检测一个面(或可能是多个面并假设它是图像的多个位置中的同一个人 – 这个假设在您自己的图像中可能或可能不成立,所以要小心这里)。
例如,假设 rgb 包含Ellie Sattler脸部的图片。
第41和42行实际上找到/本地化她的面部,从而产生一个面部框列表 。我们将两个参数传递给face_recognition 。face_locations 方法:
rgb :我们的RGB图像。
model : cnn 或 hog (此值包含在与 “detection_method” 键关联的命令行参数字典中 )。CNN方法更准确但更慢。HOG更快但准确度更低。
然后,我们将把 Ellie Sattler脸部的边界 框变成45行的128个数字列表 。这被称为将面部编码为矢量, face_recognition 。face_encodings 方法为我们处理它。
从那里我们只需要将Ellie Sattler 编码 和 名称附加 到适当的列表( knownEncodings 和knownNames )。
我们将继续为数据集中的所有218个图像执行此操作。
除非我们可以 在另一个处理识别的脚本中使用编码,否则编码图像的重点是什么 ?
现在让我们来处理
第56行构造了一个带有两个键的字典 – “编码” 和 “名称” 。
从那里57-59号线将名称和编码转储到磁盘上以备将来召回。
我该如何 在终端中运行 encode_faces .py脚本?
要创建面部嵌入,请打开终端并执行以下命令
1$ python encode_faces.py --dataset dataset --encodings encodings.pickle
从输出中可以看出,我们现在有一个名为encodings.pickle 文件 – 此文件包含数据集中每个面的128维面嵌入。
在我的Titan X GPU上,处理整个数据集只花了一分多钟,但如果您使用的是CPU,请准备好等待一段时间才能完成此脚本!
在我的Macbook Pro(无GPU)上,编码218张图片需要21分20秒。
如果您拥有GPU并使用GPU支持编译dlib,您应该期望更快的速度。
识别图像中的面孔
图4:使用Adam Geitgey的深度学习face_recognitionPython模块识别John Hammond的脸。
现在我们已经为数据集中的每个图像创建了128维面嵌入,现在我们可以使用OpenCV,Python和深度学习来识别图像中的面部。
打开 recognize_faces_image .py (从这个博客帖子 “下载” 相关的文件和图像数据)
此脚本在第2-5行只需要四次导入 。该 face_recognition 模块将做繁重和OpenCV将帮助我们来加载,转换和显示处理后的图像。
我们将在第8-15行解析三个命令行参数:
– encodings :包含我们的面部编码的pickle文件的路径。
– image :这是正在进行面部识别的图像。
– detection-method :你现在应该熟悉这个 – 我们要么 根据系统的能力使用 hog 或 cnn方法。对于速度,选择 hog 要准确,选择 cnn 。
重要! 如果你是:
在CPU上运行面部识别代码
或者你使用Raspberry Pi
……你要设置 –detection-method 为 hog, 作为CNN脸检测器(1)不具有GPU和(2)的树莓派将没有足够的内存来运行任意的CNN缓慢。
从那里,让我们加载预先计算的编码+面部名称,然后为输入图像构建128维面部编码
第19行从磁盘加载我们的腌制编码和面名。我们稍后将在实际的面部识别步骤中需要这些数据。
然后,在第22行和第23行,我们加载并将输入图像转换 为 rgb 颜色通道排序(就像我们在encode_faces .py 脚本中所做的 那样)。
然后我们继续检测输入图像中的所有面,并 在第29-31行计算它们的128-d 编码(这些线也应该看起来很熟悉)。
现在是初始化 检测到的每个面的名称列表的好时机 – 该列表将在下一步填充。
接下来,让我们循环遍历面部 编码
在第37行,我们开始循环从输入图像计算的面部编码。
然后面部识别魔法发生了!
我们尝试 使用face_recognition. ompare_faces 将输入图像(编码 )中的每个面匹配 到我们已知的编码数据集(保存在 data[“encodings”] 中)(第40和41行)。
此函数返回True / False 值列表, 每个图像对应一个数据集。对于我们的侏罗纪公园示例,数据集中有218个图像,因此返回的列表将具有218个布尔值。
在内部, compare_faces 函数计算候选嵌入与数据集中所有面之间的欧几里德距离:
如果距离低于某个公差(公差越小,我们的面部识别系统就越严格),那么我们返回True ,表示面部匹配。
否则,如果距离高于容差阈值,则返回 False, 因为面不匹配。
基本上,我们正在使用“更花哨的”k-NN模型进行分类。请务必参考compare_faces实现以获取更多详细信息。
该 变量将最终持有人的姓名串-现在,我们把它作为 “未知” 的情况下有没有“票”(42号线)。
根据我们的 匹配 列表,我们可以计算每个名称的“投票数”( 与每个名称相关联的真值的数量 ),计算投票数,并选择具有最多相应投票的人名
如果匹配中有任何 True 投票 (第45行),我们需要确定这些True 值 匹配的位置 的索引。我们在第49行上就是这样做的 ,我们构造了一个简单的matchedIdx列表, 对于example_01 .png可能看起来像这样
1
(Pdb) matchedIdxs[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, 71, 72, 73, 74, 75]
然后我们初始化一个名为counts的字典 ,它将字符名称作为键 票数作为值(第50行)。
从那里开始,让我们遍历 matchesIdxs 并设置与每个名称相关联的值,同时根据需要递增 计数 。该 计数 字典可能是这样的一个高投得分为伊恩·马尔科姆
1(Pdb) counts{‘ian_malcolm’: 40}
回想一下,我们在数据集中只有41张Ian图片,因此对于其他任何人而言没有投票的40分是非常高的。
第61行从计数中提取得票最多的名称 ,在这种情况下,它将是 “ian_malcolm” 。
我们的循环的第二次迭代(因为在我们的示例图像中有两个面)主面部编码循环产生以下计数
1(Pdb) counts{‘alan_grant’: 5}
这绝对是一个较小的投票得分,但仍然,字典中只有一个名字所以我们可能找到了Alan Grant。
注意: PDB Python调试器用于验证计数 字典的值 。PDB的使用超出了本篇博文的范围; 但是,您可以在Python文档页面上发现如何使用它。
如 下面的图5 所示,Ian Malcolm和Alan Grant都已被正确识别,因此脚本的这一部分运行良好。
让我们继续并循环遍历每个人的边界框和标记名称,并在输出图像上绘制它们以进行可视化
在第67行,我们开始在检测到的面部边界框和预测名称上循环。要创建一个可迭代对象,以便我们可以轻松遍历值,我们调用zip(框,名称),从而生成元组,我们可以从中提取框坐标和名称。我们使用框坐标在第69行上绘制一个绿色矩形。我们还使用坐标来计算我们应该在哪里绘制人名的文本(第70行),然后在图像上实际放置名称文本(第71和72行) )。如果面部边界框位于图像的最顶部,我们需要将文本移动到框顶部下方(在第70行处理),否则文本将被截断。然后我们继续显示图像,直到按下一个键(第75和76行)。你应该如何运行面部识别Python脚本?使用您的终端,首先使用workon命令确保您在各自的Python正确的虚拟环境中(当然,如果您使用的是虚拟环境)。然后运行脚本,同时至少提供两个命令行参数。如果您选择使用HoG方法,请务必同时传递–detection-method hog(否则它将默认为深度学习检测器)。
我们去吧!
要使用OpenCV和Python识别面部,请打开终端并执行我们的脚本
1
$ python recognize_faces_image.py --encodings encodings.pickle \ --image examples/example_01.png
Figure 5:Alan Grant and Ian Malcom’s faces are recognized using our Python + OpenCV + deep learning method.
第二个例子
$ python recognize_faces_image.py --encodings encodings.pickle \ --image examples/example_03.png
Figure 6:Face recognition with OpenCV and Python.
识别视频中的面孔
图7:通过Python,OpenCV和深度学习在视频中进行面部识别。
既然我们已经对图像应用了人脸识别,那么我们也可以对视频进行人脸识别(实时)。
重要性能注意:如果您正在使用GPU,则只能实时使用CNN人脸识别器(您可以将其与CPU一起使用,但期望小于0.5 FPS,这会导致视频不稳定)。或者(您使用的是CPU),您应该使用HoG方法(甚至是未来博客文章中介绍的OpenCV Haar级联)并期望足够的速度。
下面的脚本与以前 recognize_faces_image.py 脚本 有许多相似之处 。因此,我将轻松略过已经涵盖的内容,只需查看视频组件,以便了解正在发生的事情
我们在第2-8行导入包,然后继续解析第11-20行的命令行参数。
我们有四个命令行参数,其中两个你应该从上面识别(–encodings和–detection-method)。 另外两个 参数 是:
–output:输出视频的路径。
–display:一个标志,指示脚本将帧显示在屏幕上。 值1显示,值0不显示输出帧到我们的屏幕。
从那里我们将加载我们的编码并启动我们的VideoStream:
要访问我们的相机,我们正在使用imutils的VideoStream类。 第29行启动流。 如果系统上有多个摄像头(例如内置网络摄像头和外部USB摄像头),则可以将src = 0更改为src = 1,依此类推。
我们可以选择将处理后的视频帧写入磁盘,因此我们将writer初始化 为 None (第30行)。睡眠2秒钟可使我们的相机预热(第31行)。
从那里我们将开始一个 while 循环并开始抓取并处理帧
我们的循环从第34行开始,我们采取的第一步是从视频流中获取一个帧(第36行)。
上述代码块中的剩余行40-50几乎与前一个脚本中的行相同,只是这是视频帧而不是静态图像。基本上我们读取 帧 ,预处理,然后检测面边界 框 +计算 每个边界框的编码。
接下来,让我们循环遍历 与我们刚刚找到的面部相关的面部 编码
在这个代码块中,我们遍历每个 编码 并尝试匹配面部。如果找到匹配项,我们会计算数据集中每个名称的投票数。然后,我们提取最高投票数,这是与面部相关联的名称。这些行与我们前面提到的脚本相同,所以让我们继续。
在下一个块中,我们在已识别的面上循环,然后围绕面部绘制一个框,并在面部上方绘制人物的显示名称
这些行也是相同的,所以让我们关注视频相关代码。
或者,我们将把帧写入磁盘,让我们看看如何使用OpenCV将视频写入磁盘:
假设我们在命令行参数中提供了输出文件路径,并且我们尚未初始化视频编写器(第99行),让我们继续并初始化它。
在第100行,我们初始化VideoWriter_fourcc。 FourCC是一个4字符的代码,在我们的例子中,我们将使用“MJPG”4字符代码。
从那里,我们将该对象与我们的输出文件路径,每秒帧数目标和帧尺寸(第101和102行)一起传递到VideoWriter中。
最后,如果编写器存在,我们可以继续将帧写入磁盘(第106-107行)。
让我们来处理是否应该在屏幕上显示人脸识别视频帧
如果我们的显示命令行参数已设置,我们继续显示框架(第112行)并检查是否已按下退出键(“q”)(第113-116行),此时我们将突破 循环(第117行)。
最后,让我们履行我们的内务管理职责
在第120-125行中,我们清理并释放显示器,视频流和视频编写器。
你准备好看脚本了吗?
要演示使用OpenCV和Python实时进行人脸识别,请打开终端并执行以下命令
$ python recognize_faces_video.py --encodings encodings.pickle \ --output output/webcam_face_recognition_output.avi --display
视频文件中的人脸识
此文件与我们刚刚为网络摄像头查看的文件基本相同,但它会采用输入视频文件并生成输出视频文件(如果您愿意)。
我将人脸识别代码应用于最初的侏罗纪公园电影中的流行“午餐场景”,演员坐在桌子旁,分享他们对公园的关注
$ python recognize_faces_video_file.py –encodings encodings.pickle \ –input videos/lunch_scene.mp4 –output output/lunch_scene_output.avi \ –display
注意:回想一下,我们的模型是由原始演员的四名成员训练的:Alan Grant,Ellie Sattler,Ian Malcolm和John Hammond。该模型没有接受Donald Gennaro(律师)的培训,这就是为什么他的脸被标记为“未知”。这种行为是设计(不是偶然),以表明我们的面部识别系统可以识别它所训练的面部,同时留下它无法识别为“未知”的面部。
在下面的视频中,我将侏罗纪公园和侏罗纪世界剪辑的“精彩卷轴”放在一起,主要来自预告片:
我们可以看到,我们可以看到,我们的人脸识别和OpenCV代码运行良好!
我可以在Raspberry Pi上使用这个面部识别器代码吗?
可以。但是有一些限制:
Raspberry Pi没有足够的内存来使用更准确的基于CNN的人脸检测器……
……所以我们仅限于HOG
除了HOG在Pi上用于实时人脸检测的速度太慢……
…所以我们需要利用OpenCV的Haar级联
一旦你运行它,你只能期望1-2 FPS,甚至达到FPS的水平需要一些技巧。
好消息是我将在下周回来讨论如何在Raspberry Pi上运行我的人脸识别器,敬请期待!
摘要
在本教程中,您学习了如何使用OpenCV,Python和深度学习进行人脸识别。
此外,我们使用了Davis King的dlib库和Adam Geitgey的face_recognition模块,该模块包含了dlib的深度度量学习,使面部识别更容易实现。
我们发现,我们的面部识别实现是:
准确
能够使用GPU实时执行
我希望你喜欢今天关于人脸识别的博客文章!
安装OpenCV。可以参考我的教程 pip 安装 opencv
代码下载
请看 这篇文章 https://hotdog29.com/?p=620
原文链接
dlib 使用OpenCV,Python和深度学习进行人脸识别 源代码
我希望你喜欢今天关于OpenCV人脸识别的教程!
文章转自 Adrian Rosebrock ,OpenCV Face Recognition,PyImageSearch,https://www.pyimagesearch.com/2018/09/24/opencv-face-recognition/,2009年7月6日访问
相关文章
代码下载
OpenCV 人脸识别 源代码
文本检测 OpenCV EAST文本检测器 源代码
YOLO 对象检测 OpenCV 源代码
OpenCV教程 ,资源和指南
张贴在技术博客、opencv标签:opencv、人脸识别、深度学习、dlib、python编辑