【class13】
通过前面两课的学习,我们的程序已经实现了根据指定图片进行人脸识别的过程,即在图中标记人脸区域。
通过人脸检测接口detect(),我们得到了【调用接口信息】与【检测结果】,让我们快速的回顾一下这些数据。
调用接口信息
在存储检测结果的 ret_data 字典中:
ret_data['error_msg'] 记录错误信息,当它为'SUCCESS' 时表示检测成功。
我们可以利用这一点来判断检测的结果是否为空。
人脸检测结果
ret_data['result'] 用字典记录了人脸检测的结果,其中:
ret_data['result']['face_num'] 中记录识别到的人脸数量;
ret_data['result']['face_list'] 中用列表存储每张人脸的信息;
包含多个人脸信息的结果
当识别到多张人脸时,ret_data['result']['face_list'] 列表里会存储多个字典,且每个字典都对应了一个人脸的基本信息。
我们可以通过
ret_data['result']['face_list'][0]
ret_data['result']['face_list'][1]
ret_data['result']['face_list'][2]
的顺序依次取出。
人脸属性信息
检测到的面孔【位置信息】与配置options参数时添加的【检测质量】、【年龄预测】信息,也会依次记录在ret_data['result']['face_list']这些字典中。
若想同时获取所有的面孔信息,我们可以利用for遍历ret_data['result']['face_list'] 中的列表。
我们将遍历得到的面部信息字典存储在face_msg变量中,这样就可以快速方便的提取面孔数据。
比如face_msg['location']可以获得面部位置。
在今天的课程中,我们就利用location中存储的面部位置数据,在图形中用矩形标记出人脸位置。
我们把这个过程分为了两个部分:
在生活中,我们使用Photoshop、美图秀秀等图片工具对图片进行修改。
在Python中,这样的操作需要借助强大的图片处理模块Pillow(PIL)
PIL
PIL(Python Image Library)是python中功能强大的第三方 图像处理库。它提供了创建、裁剪、转换等几乎所有的图片处理功能。
我会在今天学习到PIL最常用的模块:
1. Image模块:打开、创建图片对象
2. ImageDraw模块:修改图片对象内容
先让我们学习一下第一个模块Image,完成第一个任务——在程序中打开一张图片。
代码结构
利用PIL打开图片的过程与open函数类似。
第一步,导入PIL中的Image模块;
第二步,确认要打开的图片路径;
第三步,利用Image.open函数打开路径下的图片并创建备份;
分析代码:
导入模块
从PIL库中,导入Image模块。注意,Image 中的字母‘I’需要大写。
图片的路径
img_path 变量中用来存储图片所在的路径。
Image.open
Image模块提供一个专用的open函数,即Image.open().
只需要以图片文件的路径作为参数,就可以将该图片创建为一个图片对象存储在变量中。
为图片创建备份
为了保证图片不会因为意外修改造成损坏,利用img.copy()方法可以拷贝一个与原图相同的文件。
在之后的修改操作中,只处理img_cp变量中拷贝文件,而不修改原图。
图片属性
输出img_cp 可以看到一些图片的属性信息。
包括图片的色彩模式、尺寸大小等。
利用PIL的Image模块打开图片需要以下几个步骤:
1. 从PIL中导入模块Image;
2. 设定图片存储的路径;
3. 使用with语句结合Image.open打开图片
4. 利用copy()方法创建图片。
到这里,我们完成了打开图片并创建图片对象的过程。接下来尝试我们对这个图片对象进行修改,在该图的脸部位置绘制一个矩形。
在PIL库中的ImageDraw模块能够帮我们实现这一过程。
代码结构
修改图片的代码结构如图所示,需要下面的步骤:
第1步,使用ImageDraw模块创建可修改的画布;
第2步,提取人脸检测结果中的位置信息,并格式化为画布可用的形式;
第3步,利用rectangle方法,在指定的位置绘制矩形。
分析代码:
ImageDraw
从PIL中导入ImageDraw模块,它能为程序提供一些修改图片的方法。
创建画布
通过ImageDraw.Draw()函数为图片创建画布。
把打开的图像对象 img_cp 作为参数传递给 ImageDraw.Draw()函数,该函数会返回一个画布对象,方便程序对图片进行修改。
绘制矩形
存储画布的draw_img变量提供一些列平面图形的绘制方法。其中,通过draw_img.rectangle可以在画布的指定位置中绘制一个矩形。
用列表存储绘图位置的变量xy,是rectangle函数必须传递的参数。
像素中的坐标
为了确定某个像素在图片中的位置,需要使用像素坐标系。
在像素坐标系中,图片的左上角为坐标轴的原点,x轴指向图片的右方,y轴指向图片的下方。
示意图:
确定一个矩形位置的方法
在图像中,我们通过两个点的位置来确定一个矩形区域。即:
1. 矩形左上角点的坐标
2. 矩形右下角点的坐标
提取出人脸的位置信息
location变量中存储着detect接口检测到的脸部位置信息。
它是一个字典,我们通过下面的键来获取数据:
1. 与左边界的距离 location['left']
2. 与上边界的距离 location['top']
3. 人脸的宽度 location['width']
4. 人脸的高度 location['height']
获得pos参数所需要的值
其中left与top对应的是矩形左上角的坐标x1与y1,对于右下角的坐标:
left 与 width 相加得到x2;
top 与 height 相加得到y2。
这样,我们就得到了xy参数所需的所有数据,即确定了一个矩形位置。
提取关键点坐标
从location变量中提取left、top、width、height的数值,生成矩形左上角与右下角点的坐标,分别存储在x1、y1、x2、y2变量中。
设置矩形的位置
将这些坐标数据存储在一个列表中,它将作为第一个参数传递给rectangle函数。
存储格式为[x1, y1, x2, y2]。
设定矩形的边框颜色
设定矩形的边框颜色为红色。
将表示颜色的单词以字符串的形式传递给rectangle的outline参数,可以设定矩形边框的颜色。
比如在这里,边框的颜色设定为红色。传递outline参数要以关键字参数形式,即 outline='red'。
设置边框粗细
设置边框粗细为2像素,作为rectangle的第三个参数width传递。
rectangle 的width参数用来控制矩形边框的粗细,单位为像素。该参数依旧以关键字参数的形式传递,即width= 2。
绘制矩形
所有参数准备完成后,使用draw_img画布的rectangle()方法来绘制矩形。需要注意,outline 与 width 参数都需要以关键字参数的形式传递。
存储修改后的图片
最后利用图像的img_cp.save()方法存储修改后的图片。将图片保存路径'/User/img/draw.png'作为参数传递给save。
口罩判断
通过位置信息与PIL模块的结合,我们标记出了图中所有的面孔。
那么通过【检测质量】数据,我们能实现那些功能呢?
下节预告
在下节课的学习中,我们将利用这些数据来判断画面中人物的口罩佩戴情况。
也就是项目的最后一步——口罩佩戴情况检测。
【class13】
在上节课的学习中,我们通过人脸检测接口detect得到了人脸【位置信息】,并结合PIL提供的 Image 与 ImageDraw这两个模块分别对图片进行了打开与修改。
完成了项目的第三部——在图中标记人脸区域。
在今天的课程中,我们将进入项目的最后一步口罩佩戴情况监测。
这个过程可以拆分为2个小步骤:
1. 五官遮挡信息检测
2. 使用PIL模块为图片添加文字
让我们先从第一步开始~
为什么需要五官检测
在佩戴口罩的过程中,人脸的鼻子、嘴等部位会被口罩遮挡。
利用这一特点,我们可以判断识别到的人脸是否佩戴了口罩。
获得数据的方法
在前面学到的内容中,遍历 面孔列表 时,除了location中存储的位置信息以外,我们还有 质量检测 与 年龄预测两类信息。
其中,质量信息(quality)中存储着识别到的面孔情况,包含光照强度、清晰度、五官是否被遮挡等信息。
接下来让我们尝试提取每个面孔的质量信息。利用这些信息判断识别到的人脸是否佩戴了口罩,并用不同的颜色把它们标注出来。
代码结构
首先,快速预览一下完整的代码结构。
第1部分,遍历面孔列表获取五官遮挡信息;
第2部分,设定不同情况下矩形的颜色;
第3部分,在人脸区域绘制指定颜色的矩形
# 判断检测是否成功
if ret_data['error_msg'] == 'SUCCESS':
# 遍历面孔信息列表
for face_msg in ret_data['result']['face_list']:
# 获取位置信息
location = face_msg['location']
# 获取人脸区域的坐标
x1 = location['left']
y1 = location['top']
x2 = x1 + location['width']
y2 = y1 + location['height']
# 获取五官遮挡情况
quality = face_msg['quality']['occlusion']
# 判断鼻子与嘴巴是否被遮挡
if quality['nose'] > 0.05 and quality['mouth'] == 1:
color = 'lightgreen'
else:
color = 'red'
# 绘制矩形图案
draw_img.rectangle([x1, y1, x2, y2], outline=color, width=2)
# 存储绘制的结果
img_cp.save('/User/img/draw.png')
else:
print('识别失败')
分析代码:
遍历面孔列表
遍历面孔列表获取识别到的每一张面孔信息face_msg。与上节课相同,我们需要利用for循环遍历面孔列表里的每一条信息。
获取面孔质量
在人脸识别检测结果中,face_msg['quality']用来存储检测到的面孔质量。
其中,对五官的遮挡检测存储在face_msg['quality']['occlusion'],数值范围为0~1。
我们需要这部分数据来判断嘴与鼻子的遮挡情况。
获取遮挡信息
从face_msg['quality']['occlusion']中获取五官遮挡情况,并存储在变量quality中。
变量quality是一个字典,我们需要从中获取quality['nose']与quality ['mouth']的值。
判断口罩佩戴的条件
正确佩戴口罩时,人的嘴巴会被口罩完全覆盖、鼻子部分会被大部分覆盖。
针对这一特点,我们可以设定判断条件:
当识别到的面孔嘴部的遮挡程度等于100%并且鼻子的遮挡程度大于50%时,即佩戴了口罩。
设定颜色区分
为了将判断结果在图中展示,我们使用颜色对未佩戴口罩的人进行区分。
如图所示,对已佩戴口罩的人使用绿色矩形标记,未佩戴口罩的人使用红色矩形标记。
判断遮挡情况
设定判断口罩是否佩戴的条件。
判断面孔是否佩戴口罩,需要同时满足下列条件:
条件1:鼻子的遮挡度大于0.5;
条件2:嘴巴的遮挡程度等与1。
即“if 条件1 and 条件2”。
佩戴口罩时
若判断结果为真,设定颜色为lightgreen(浅绿色),存储在color变量中。
该变量会作为绘制矩形的边框颜色(outline参数)。
未佩戴口罩时
否则(若判断结果为假),将color变量设置为红色。
标记人脸区域
根据判断结果确定颜色后,利用上节课学习的矩形绘制方法rectangle,在画布的人脸区域绘制矩形。
存储结果
最后将标记结束的图片保存在指定路径下。
总结下这个过程,根据面孔五官的遮挡信息,判断其是否佩戴口罩。分为下面的步骤:
1. 遍历面孔列表获取【质量信息】中关于五官遮挡的信息。
2. 利用if语句判断鼻子与嘴巴的遮挡程度,并根据不同的情况设置颜色。
3. 在人脸区域绘制指定颜色的矩形,并保存。
现在,我们完成了对口罩佩戴的检测,并使用了不同的颜色标记出图像中的面孔。
除了颜色以外,有没有更清晰的方式来区分佩戴口罩的面孔呢?
当然有!直接用文字标注出来就好了~
接下来,我们来开始今天的第二部分内容——使用PIL模块为图片添加文字。
除了上节课学到的绘制矩形方法,画布提供draw_img.text()方法用来在指定的位置添加文字。
不过为了正确显示中文,这个过程还需要额外的字体文件支持。
让我们来详细的学习下这个过程,尝试为图片的指定位置添加一段文字。
代码结构
首先,快速预览一下完整的代码结构。
第1部分,打开图片并为其创建画布;
第2部分,读取字体文件,并创建字体对象;
第3部分,在画布的指定位置添加文字。
分析代码:
绘制文字的方法
在画布中的指定位置绘制文字,需要使用draw_img.text()方法。
text需要传入位置、内容、颜色、字体等参数。
其中,字体参数font需要传递一个存储文字样式与大小的字体对象。
字体文件
字体指的是文字在程序中显示的样式,比如我们常用的宋体、黑体等。
在电脑中,字体以文件的形式存储,常见的格式有.ttf、.ttc、.fon等。
windows系统的所有字体可以在“C://windows/fonts”路径下查看。
字体对象
为图片添加文字时,需要提前设定文字的字体与大小。
PIL提供一个字体管理模块ImageFont,其中ImageFont. truetype() 函数用来创建一个字体对象,参数为:
1. 字体路径:字体文件的位置
2. 字号:绘制文字的大小
导入ImageFont
从PIL中导入ImageFont模块用来管理字体文件。
注意区分大小写~
创建字体对象
使用ImageFont.truetype函数创建字体对象。
第一个参数为字体路径“/User/img/yahei_consola.ttf”以字符串的形式传递。
第二个参数为字体显示的为50像素,该参数必须使用整数传递。
最后将生成的字体对象存储在font变量中。
导入字体文件后,就可以利用draw_img.text()方法为画布添加文字了。
接下来对它的参数进行依次说明。
文字位置
在坐标位置(0,0)绘制文字。
确定文字位置的方法:
如图设定该坐标时,我们只需要传递文字左上角要对齐的坐标位置(x,y)。
内容与颜色
text的第二、三参数分别为要显示的文字内容与文字颜色。
在这里,我们要显示的文字内容为“夜曲编程专用水印”,颜色为“red”红色。
字体
text的最后一个参数font用来传递已经创建好的字体对象。
我们把font变量传递给text,这样文字将会以“雅黑体”、50像素的大小绘制。
存储绘制结果
所有的参数都准备好后,draw_img.text()就会将指定的文字内容绘制在画布中。
最后将修改后的图片img_cp存储在路径'/User/img/draw.png'下。
总结下为图片添加文字的步骤:
1. 从PIL中导入ImageFont模块,用来提供字体支持。
2. 使用ImageFont.truetype()函数创建字体对象,参数为字体文件路径与字号。
3. 使用draw_img.text()方法为画布添加文字,参数为文字位置、内容、颜色与字体。
'''修改图片标记出人脸'''
# 导入图片修改模块
from PIL import ImageDraw
# 创建画布
draw_img = ImageDraw.Draw(img_cp)
# 从PIL中导入ImageFont模块
from PIL import ImageFont
# 创建字体对象,
# 文件路径为'img/yahei_consola.ttf', 字号为50
font = ImageFont.truetype('img/yahei_consola.ttf', 50)
# 绘制文字: 左上角位置、内容、颜色、字体
draw_img.text([0, 0], '夜曲编程专用水印', 'red', font)
# 存储绘制的结果
img_cp.save('/User/img/draw.png')
接下来我们将文字添加到每张面孔的下方,实现这过程需要两步:
1. 设置自动变化的字体大小
为了避免添加的文字过大或者过小,我们需要根据面孔的大小来适应文字的大小;
2. 为文字绘制背景
为了让文字清晰可见,需要在文字的位置下绘制矩形背景,突出文字内容。
代码结构(上)
先来快速预览下【自适应文字】部分的代码结构。
第一部分,遍历面孔列表获取位置数据。
第二部分,根据面孔区域的高度来设置文字大小。
自适应字体大小
在识别的过程中,不同的照片、不同人检测到的人脸区域大小也不相同。
为了让文字的大小适应面孔标记的大小,我们设定文字大小为区域高度的五分之一。
前面我们说过,文字的大小只能使用整数类型。所以需要使用整除来确保计算结果不是浮点数(小数)。
自适应文字大小
除了字体文件路径以外,设定文字的大小为面孔标记的五分之一即:
文字大小 = 面孔高度 整除 5,
并传递给ImageFont.trutype()函数用来创建字体对象。
代码结构(下)
接下来快速预览下【绘制背景与文字】部分的代码结构。
第一部分,根据判断结果设定要添加的文字内容。
第二部分,找到文字与背景的位置修改图片。
确定文字内容
根据佩戴情况将不同的文字内容存储在text变量中。
当判断面孔符合佩戴口罩的条件时,text变量为“已佩戴口罩”;
当判断面孔不符合佩戴口罩的条件时,text变量为“未佩戴口罩”;
文字位置
为了将文字放置在识别标记的正下方,我们需要获取红色标记点的坐标。
如图所示,该坐标的值为(x1, y2)。
文字背景
在文字的位置绘制一个矩形充当背景。
绘制矩形的方法rectangle需要两个坐标来确定位置,如图所示:
左上角
坐标与文字坐标相同,即(x1, y2);
右下角
坐标为(x2, y2+背景区域想要的高度)。
绘制文字背景
绘制一个矩形作为文字的背景。
矩形位置为[x1, y2, x2, y2 + font_size*2],其中 font_size*2的目的是设定背景的高度等于字体大小的二倍。将color变量中的颜色传递给fill参数中。
fill参数是rectangle的一个可选参数,用来为矩形填充颜色。
绘制文字
在人脸区域的下方绘制文字。
使用text变量中的字符串作为文字内容,白色(white)作为文字的颜色。
绘制结束后保存图片。
总结下这个过程:
1. 创建字体对象,设定文字大小为面孔高度的五分之一;
2. 设定不同情况下(是否佩戴口罩)要显示的文字内容;
3. 绘制一个矩形作为文字背景;
4. 绘制文字,并保存图片。
当然啦,我们能做到的远不止这些。
通过为options['face_field']添加更多的配置项,可以对人脸进行进一步检测。
比如我们同时检测人脸的质量、年龄、性别数据,并用同样的的方式标记到图片中。
大家可以试一试
总结项目步骤:
恭喜你!
到这里我们就完成了人脸识别项目的所有步骤,帮助小叶实现了【通过人脸识别进行口罩检测】的程序~
利用人脸识别接口,我们还可以做出更多好玩的程序、实现更多酷炫的功能。
在日常生活中,我们经常需要记录从图片中提取的文字。比如名片信息、证件信息扫描等。
在下节课,我们将学习从图片中智能提取文本内容的技术,即
光学字符识别技术(OCR)。