#学习笔记 #OpenCV #人脸识别 #Python

开始

开始识别人脸了,记得准备几个有人脸的图

识别人脸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import cv2 as cv  
# 建立检测函数



if __name__ == '__main__':
# 读取图片
img = cv.imread('rickroll3.png')
# 检测函数
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
face_detect = cv.CascadeClassifier('C:/Users/1/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')
# gray图像,1,01每次遍历的缩放倍数,5检测几次再确定,0默认不管,最小有多小,最大有多大
face = face_detect.detectMultiScale(gray, 1.01, 5, 0, (105,105), (120,120))
for x, y, w, h in face:
cv.rectangle(img, (x, y), (x + w, y + h), color=(0, 0, 255), thickness=2)
cv.imshow('result', img)
# 等待
while True:
if ord('q') == cv.waitKey(0):
break
# 释放内存
cv.destroyAllWindows()
print(face)

里面提到一个 Classifier 分类器,这里选择用一个现成的分类器
下载 OpenCV 的文件
官网
下载 Windows 版本,双击解压操作

注意 OpenCV 这个解压后路径不要有中文,保证全英文路径

打开 opencv\sources\data\haarcascades 寻找其中的 haarcascade_frontalface_alt2.xml 填入

1
cv.CascadeClassifier('C:/Users/1/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')  

调用到这个分类器
注意:Windows 用户请注意,Windows 的路径是 \ 斜杠,而我们需要的是 / 斜杠,记得全改过来(或者考虑拿 Linux 做这个?)

具体识别流程

具体的人脸识别流程:

  1. 读取图像(或者是视频)
  2. 将图像转化为灰度图像
  3. 设置分类器
  4. 分类器识别人脸的信息
  5. 人脸位置上画矩形
  6. 显示图片

获得人脸位置

1
2
# gray图像,1,01每次遍历的缩放倍数,5检测几次再确定,0默认不管,最小有多小,最大有多大  
face = face_detect.detectMultiScale(gray, 1.01, 5, 0, (105,105), (120,120))

gray 图像,1,01 每次遍历的缩放倍数,5 检测几次再确定,0 默认不管,最小有多小,最大有多大
返回 face 是所有人脸的位置以及方框的长宽,即 x,y,w,h 的值

其实可以试着拿 print (face) 打印出来看看里面都是啥~,都是数值的

1
2
for x, y, w, h in face:  
cv.rectangle(img, (x, y), (x + w, y + h), color=(0, 0, 255), thickness=2)

遍历所有人脸的位置信息,画方框,两个点分别是 (x, y)(x + w, y + h),读取到几个脸就画几个方框

记得是在灰度图像里找坐标,在原图里标记矩形

执行效果如下:

给诈骗犯锁个头~

训练

下面就是给每个人头做一个标记了
比如看见 Rick 就在图片上显示 Rick,看到昊京就显示昊京

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import cv2 as cv  
import os
from PIL import Image
import numpy as np

def getImageAndLables(path):
# 人脸数据
faceSample = []
# 姓名数据
ids = []
# 储存图片信息 路径
imagePath = [os.path.join(path, filename) for filename in os.listdir(path)]
# 加载分类器
face_detector = cv.CascadeClassifier('C:/Users/1/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')
# 遍历图片
for singlePath in imagePath:
# PIL灰度化 有九种模式 分别为1 L P RGB RGBA CMYK YCbCr I F
# 这里采用L灰度化
PIL_img = Image.open(singlePath).convert('L')
# 图像转换为矩阵
img_np = np.array(PIL_img, 'uint8')
# 获取人脸特征
faces = face_detector.detectMultiScale(img_np)
# 获取id
id = int(os.path.split(singlePath)[1].split('.')[0])
# 装进去
for x,y,w,h in faces:
ids.append(id)
faceSample.append(img_np[y:y+h, x:x+h])
print('id: ', id)
print('face: ', faces)
return faceSample, ids

if __name__ == '__main__':
# 图片路径
path = './Images/'
# 获取图像
faces, ids = getImageAndLables(path)
# 加载识别器
recognizer = cv.face.LBPHFaceRecognizer_create()
# 训练
recognizer.train(faces, np.array(ids))
# 保存
recognizer.write('trainer/trainer.yml')

工作流程

  1. 将人脸的图片放进一个文件夹里,这里放的是 './Images/'
  2. 把每一个图片的脸都识别一下,放进一个列表里,再识别每个图片的 ID 也放到一个列表里
  3. 加载识别器进行训练
  4. 把识别器的文件保存

遍历文件

这里创建了三个列表

1
2
3
4
5
6
# 人脸数据  
faceSample = []
# 姓名数据
ids = []
# 储存图片信息 路径
imagePath = [os.path.join(path, filename) for filename in os.listdir(path)]

faceSample = [] 放置人脸的数据,也就是上一节里面识别人脸返回的那个 face
ids = [] 放置每个人脸的 id,用来识别谁是什么样的脸
imagePath = [os.path.join(path, filename) for filename in os.listdir(path)] 放置每个图片的路径信息,方便读取
其中:

  • os.path.join(path, filename) 用来拼接文件的路径
    • path 就是文件名之前的一大长串
    • filename 就是文件名
  • for filename in os.listdir(path) 就是遍历 path 这个路径的文件夹里每一个文件的文件名
    • os.listdir(path) 就是显示路径当中的所有文件名,返回一个列表
    • 利用 for 循环遍历这个列表

关于 PIL

为了把图像灰度化处理需要用到 PIL
但是都什么年代了还在用传统 PIL,现在都开始用 pillow,想睡觉来枕头,利用之前说的安装 pillow 这个包,import 一个叫 Image 的

1
PIL_img = Image.open(singlePath).convert('L')  

singlePath 是单个文件的路径,打开文件并转换,用 L 模式进行

PIL 有九种模式分别为 1 L P RGB RGBA CMYK YCbCr I F

光灰度处理还不够,还需要转化为 OpenCV 能看懂的格式

1
2
 # 图像转换为矩阵  
img_np = np.array(PIL_img, 'uint8')

将 PIL_img 转化为 uint8 格式

uint8 格式在 OpenCV 还挺常用的~

识别人脸存入列表

1
2
3
4
5
6
7
8
9
10
# 获取人脸特征  
faces = face_detector.detectMultiScale(img_np)
# 获取id
id = int(os.path.split(singlePath)[1].split('.')[0])
# 装进去
for x,y,w,h in faces:
ids.append(id)
faceSample.append(img_np[y:y+h, x:x+h])
print('id: ', id)
print('face: ', faces)

获取人脸 faces 没必要再说

我们每一个需要训练的图片都有这样一种命名规则,那就是 ID.姓名序号.png

id = int(os.path.split(singlePath)[1].split('.')[0])
获取单个 id
其中:

  • int(os.path.split(singlePath)[1] 是之前 os.path.join(path, filename) 相反的操作
    • 第一个 split 是分离文件夹的路径和文件名的,分离为两部分,[0]就是路径,同理[1]就是文件名,[2]就是……不存在的,会报错
    • 第二个 .split('.')[0] 是分离文件名,按照 '.' 分离,以 ID.姓名序号.png 文件为例, 总共能分三部分
      • 第一部分是 ID
      • 第二部分是姓名序号
      • 第三部分就是剩下的 png
1
2
print('id: ', id)  
print('face: ', faces)

控制台打印 id 和面部信息
所有信息准备好之后 return faceSample, ids 返回人脸和 id 的列表,进行训练。

训练开始

1
2
3
4
5
6
# 加载识别器  
recognizer = cv.face.LBPHFaceRecognizer_create()
# 训练
recognizer.train(faces, np.array(ids))
# 保存
recognizer.write('trainer/trainer.yml')

进行识别器的训练,需要注意的点是 id 的列表需要更改为 array 才可以正常进行
保存到 ./trainer/ 文件夹当中命名为 trainer.yml

进行识别

下面就是把以上的内容进行整合
首先确立一下我们要识别的人脸,训练的人脸

  1. Rick——国际诈骗犯
  2. HaoJing——战狠的主角昊京
  3. WuJing——战狼的主角吴京

训练好的保存为 trainer.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import cv2 as cv  
import os



if __name__ == '__main__':
# name list
name = ['Rick Astley', 'Hao Jing', 'Wu Jing']
# create recognizer
recognizer = cv.face.LBPHFaceRecognizer_create()
recognizer.read('./trainer/trainer.yml')
# import image and convert to GRAY
originalImg = cv.imread('PinTu2.png')
grayImg = cv.cvtColor(originalImg, cv.COLOR_BGR2GRAY)
# Load Classifier
face_detector = cv.CascadeClassifier('C:/Users/1/opencv/sources/data/haarcascades/haarcascade_frontalface_alt2.xml')
# detect face
# face = face_detector.detectMultiScale(grayImg, 1.1, 5, cv.CASCADE_SCALE_IMAGE, (100,100), (120,120))
# face = face_detector.detectMultiScale(grayImg)
face = face_detector.detectMultiScale(grayImg, 1.1, 5, cv.CASCADE_SCALE_IMAGE, (80, 80))
for x,y,w,h in face:
cv.rectangle(originalImg, (x, y),(x+w,y+h), color=(0,255,0),thickness=2)
# calculate the confidence
ids, confidence = recognizer.predict(grayImg[y:y+h,x:x+w])
if confidence > 80:
cv.putText(originalImg, 'unknown', (x+10,y-10), cv.FONT_HERSHEY_SIMPLEX, 0.75, (0,0,255), 1)
else:
cv.putText(originalImg, name[ids-1], (x+10,y-10), cv.FONT_HERSHEY_SIMPLEX, 0.75, (0,0,255), 2)
cv.imshow('result', originalImg)
cv.waitKey(0)
# cv.imwrite('HaoJing.png',originalImg)
cv.destroyAllWindows()

总体流程

  1. 创建名称列表
  2. 创建 recognizer,也就是之前训练好的那个
  3. 导入图像并转化为灰度
  4. 加载 Classifier,寻找人脸为 face
  5. 遍历找到的人脸 face,为每一个人脸画框框
  6. 用之前设置好的 recognizer,和找到的人脸比对得出 id 和 confidence
  7. 判断 confidence
    1. 如果 confidence 大于 80,在人脸位置的 (x+10, y-10) 写上 unknown
    2. 否则拿着识别出的 id 寻找列表 name 里的名字,把名字给写上去
  8. 显示图像
    结果:

总结

OpenCV 速通人脸识别这件事情结束了吗?还没呢,这只算入门,还有更进阶的内容需要学习,只算是做出个东西而已~

  1. 开摄像头实时的人脸识别
  2. 识别物体而不是人脸该怎么办
  3. 识别人脸总出毛病该怎么优化
    等等都需要进行考虑