本周作业:
- 写技术博文(见:lesson2)
- 熟练使用dir、inspect查看一个module的成员、类、文档
- 了解每一句的输入输出
- 实现图片换脸,或者1:N的人脸识别
一、inspect方法;dir函数
inspect模块是针对模块,类,方法,功能等对象提供些有用的方法。例如可以帮助我们检查类的内容,检查方法的代码,提取和格式化方法的参数等。[1]
inspect模块主要提供了以下四种用处:
- 对是否是模块、框架、函数进行类型检查
- 获取源码
- 获取类或者函数的参数信息
- 解析堆栈
具体用法:https://www.cnblogs.com/taosiyu/p/12012261.html
dir()函数列出一个定义对象的标识符。例如,对于一个模块,包括在模块中定义的函数,类和变量。 dir()函数对任何对象都起作用。
例如,运行dir('print')来学习print函数的属性的更多知识,或运行dir(str)学习str类的属性的更多知识。
python模块是:自我包含并且有组织的代码片段为模块。
表现形式为:写的代码保存为文件。这个文件就是一个模块。sample.py 其中文件名smaple为模块名字。
python包是:包是一个有层次的文件目录结构,它定义了由n个模块或n个子包组成的python应用程序执行环境。
通俗一点:包是一个包含init.py 文件的目录,该目录下一定得有这个init.py文件和其它模块或子包。python库是参考其它编程语言的说法,就是指python中的完成一定功能的代码集合,供用户使用的代码组合。[2]
二、1:N的人脸识别

将问题简化为,在本地端实现被检测照片与已知多张照片的比对,匹配出最相似的结果。
版本1
仿照参考文章准备了图片素材:

step1:导入需要的库
import cv2
import face_recognition as fr
import numpy as np
import os
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
step2:读取已知人脸信息,即整合 train目录的图像中的人脸编码,姓名
path = "face-recognition-python-code/train/"
known_names = []
known_name_encodings = []
images = os.listdir(path) # 返回指定的文件夹包含的文件或文件夹的名字的列表。
# 遍历train 目录中的图像,提取图像中人的姓名,计算人脸编码。信息储存至列表
for _ in images:
image = fr.load_image_file(path + _)
image_path = path + _
encoding = fr.face_encodings(image)[0]
known_name_encodings.append(encoding)
known_names.append(os.path.splitext(os.path.basename(image_path))[0].capitalize())
step3:以同样的逻辑读取待检测照片
# 读取照片
test_image = "face-recognition-python-code/test/test4.jpg"
image = cv2.imread(test_image) #cv2.imread读取测试照片
# 定位图像中检测到的人脸坐标位置值
face_locations = fr.face_locations(image)
face_encodings = fr.face_encodings(image, face_locations)
step4:识别待检测照片中的人脸并完成匹配。即遍历;比对 ;使用CV2模块中的方法绘制一个带有面部位置坐标的矩形,并标注姓名
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
matches = fr.compare_faces(known_name_encodings, face_encoding)
name = ""
face_distances = fr.face_distance(known_name_encodings, face_encoding)
best_match = np.argmin(face_distances) # numpy.argmin表示最小值在数组中所在的位置
if matches[best_match]:
name = known_names[best_match]
cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
cv2.rectangle(image, (left, bottom - 5), (right, bottom), (0, 0, 255), cv2.FILLED)
font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(image, name, (left + 6, bottom - 6), font, 1, (255, 255, 255), 1)
这一段是程序运行的关键,同时也更难理解,现在还不太能理解的是cv2.putText的逻辑,按照这个逻辑 name(显示文本)这个参数是可迭代的?也导致后面换成PIL的draw.text(为了实现中文姓名的输出)出现了很多bug。
关于这段代码的其它小知识点:
- zip
zip()函数用于将可迭代的对象中对应元素打包成一个个元组,返回元组组成的列表,例子如下
a = [(1,2,2),(2,3,4),(4,5,6)]
b = [4,5,6]
c=zip(a,b)
list(c) # 元素个数与最短的列表一致
# [((1, 2, 2), 4), ((2, 3, 4), 5), ((4, 5, 6), 6)]
-
np.argmin(face_distances)
numpy.argmin表示最小值在数组中所在的位置。在这段代码中表示当face_distances最小时,该人脸编码在数组位置数,也就可以得到最匹配人的对应姓名。 -
if matches[best_match]:
即匹配成功运行下列代码,匹配成功时相当于if True:
step5:展示并保存图片
plt.imshow(image)
plt.axis('off')
plt.show()
# 保存图片至当前文件夹
img = np.asarray(image)
im = Image.fromarray(img)
im.save('try_english.jpg')

版本2
在上述代码的基础上更改并实现了中文的识别和没有对象可匹配的提示。

主要更改的就是上述step3部分的代码。
由于PILdraw.text与cv2并不相同,所以在卡了很多次bug后,建立了匹配姓名的list,将匹配上的姓名都加至列表中,若无匹配对象则加入“No person”提示语,之后再通过索引完成对应标注。
namelist=[]
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
# 画框
cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
cv2.rectangle(image, (left, bottom - 15), (right, bottom), (0, 0, 255), cv2.FILLED)
matches = fr.compare_faces(known_name_encodings, face_encoding)
name = ""
face_distances = fr.face_distance(known_name_encodings, face_encoding)
best_match = np.argmin(face_distances) # numpy.argmin表示最小值在数组中所在的位置
if matches[best_match]:
name = known_names[best_match]
namelist.append(name)
else:
namelist.append("No person")
#print(namelist)
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
txtimage=Image.fromarray(image)
draw= ImageDraw.Draw(txtimage)
font = ImageFont.truetype(r'C:\Windows\Fonts\msyhl.ttc',40,encoding="utf-8")
for (top, right, bottom, left), i in zip(face_locations, range(len(face_locations))):
txt=namelist[i]
draw.text((left+6,bottom - 6), txt,(0,0,255),font)

整体识别结果还是不错的,青年沈腾也被轻松识别了出来。但是还是出现了两个不理想的地方:1是将沈涛识别成了贾玲,匹配的精度不太高;2是杨洋的照片没有在数据库中。
就酱,至于上述的问题,通过将沈涛和杨洋的照片添至train文件夹中,暂时解决了下~


参考文章:
https://www.jb51.net/article/215653.htm
https://blog.csdn.net/wyx100/article/details/80428424