0%

Python数据科学_33_案例:基于PCA和SVM的人脸识别系统【计算机视觉】

读取数据

1
2
3
4
import cv2
import os
from glob import glob
import numpy as np
1
2
3
4
5
6
7
8
9
10
11
12
import matplotlib.pyplot as plt

def plot_image(img, mode='bgr'):
if mode=='bgr':
img = img[:, :, ::-1]
plt.figure(dpi=200)
if mode=='gray':
plt.imshow(img, cmap='gray')
else:
plt.imshow(img)
plt.axis('off')
plt.show()
1
2
name_list = os.listdir('data/person_data/')
print(name_list)
['baijingting', 'jiangwen', 'pengyuyan', 'zhangziyi', 'zhaoliying']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
img_size = (100, 100)

imgs = []
labels = []
for i in range(len(name_list)):
name = name_list[i]
imgs_path = glob(f'data/person_data/{name}/*')
for img_path in imgs_path:
img = cv2.imread(img_path, 0)
img = cv2.resize(img, dsize=img_size) # 统一图像大小
img = cv2.equalizeHist(img) # 直方图均衡化,提高对比度
imgs.append(img)
labels.append(i)
# 将列表转化为数组
imgs = np.array(imgs)
labels = np.array(labels)
1
plot_image(img, mode='gray')

output_20240607231501

1
print(imgs.shape)
(526, 100, 100)

划分训练集和测试集

1
from sklearn.model_selection import train_test_split
1
imgs_train, imgs_test, labels_train, labels_test = train_test_split(imgs, labels, test_size=0.1, random_state=2024)
1
2
print(imgs_train.shape)
print(imgs_test.shape)
(473, 100, 100)
(53, 100, 100)

特征构造

使用PCA进行主成分提取

1
2
imgs_train_flatten = np.reshape(imgs_train, (len(imgs_train), -1))
imgs_test_flatten = np.reshape(imgs_test, (len(imgs_test), -1))
1
from sklearn.decomposition import PCA
1
2
# n_components: 主成分数量
pca = PCA(n_components=200)
1
2
# 使用训练集去训练PCA模型,并将训练集数据进行转化
imgs_train_pca = pca.fit_transform(imgs_train_flatten)
1
2
# 使用训练集训练好的模型去转化测试集
imgs_test_pca = pca.transform(imgs_test_flatten)

使用SVM模型进行人脸识别

1
from sklearn.svm import SVC
1
svm_model = SVC()
1
svm_model.fit(imgs_train_pca, labels_train)
1
svm_model.score(imgs_test_pca, labels_test)
0.7169811320754716

使用训练好的SVM模型去识别未知图片

测试一张测试集中的照片

1
2
3
4
5
6
7
8
9
10
11
12
13
# 读取测试图片
img_path = 'data/test_img.jpeg'
test_img_org = cv2.imread(img_path, 0)
test_img = cv2.resize(test_img_org, dsize=img_size) # 统一图像大小
test_img = cv2.equalizeHist(test_img) # 直方图均衡化,提高对比度

# 主成分特征提取
test_img_pca = pca.transform(test_img.reshape((1, -1)))
# 使用SVM进行预测
svm_pred = svm_model.predict(test_img_pca)[0]
# 获取到具体姓名
name_pred = name_list[svm_pred]
print(name_pred)
zhaoliying
1
plot_image(test_img_org, mode='gray')

output_20240607231502

当图片中出现多个目标时,现有模型无法预测

1
2
3
4
5
6
7
8
9
10
11
12
13
# 读取测试图片
img_path = 'data/test_img2.png'
test_img_org = cv2.imread(img_path, 0)
test_img = cv2.resize(test_img_org, dsize=img_size) # 统一图像大小
test_img = cv2.equalizeHist(test_img) # 直方图均衡化,提高对比度

# 主成分特征提取
test_img_pca = pca.transform(test_img.reshape((1, -1)))
# 使用SVM进行预测
svm_pred = svm_model.predict(test_img_pca)[0]
# 获取到具体姓名
name_pred = name_list[svm_pred]
print(name_pred)
jiangwen
1
plot_image(test_img_org, mode='gray')

output_20240607231503

当预测的目标在训练集中不包含时,还是会预测出一个人名

1
2
3
4
5
6
7
8
9
10
11
12
13
# 读取测试图片
img_path = 'data/test_img3.png'
test_img_org = cv2.imread(img_path, 0)
test_img = cv2.resize(test_img_org, dsize=img_size) # 统一图像大小
test_img = cv2.equalizeHist(test_img) # 直方图均衡化,提高对比度

# 主成分特征提取
test_img_pca = pca.transform(test_img.reshape((1, -1)))
# 使用SVM进行预测
svm_pred = svm_model.predict(test_img_pca)[0]
# 获取到具体姓名
name_pred = name_list[svm_pred]
print(name_pred)
zhaoliying
1
plot_image(test_img_org, mode='gray')

output_20240607231504

总结:

  1. 现有模型在经过PCA和SVM模型后,可以对测试图片进行预测,并且有一定的预测效果,但是总体精度只能达到71%。
  2. 现有模型无法对图片中出现多个目标时进行预测。
  3. 现有模型无法识别不包含在训练集中的照片目标。

提取人脸,然后再重复建模

提取人脸

1
2
3
# 载入人脸检测的级联分类器
faceCascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
faceCascade.load(r'haarcascade_frontalface_default.xml')
True
1
2
3
4
5
6
7
8
# 使用级联分类器去提取人脸
faces = faceCascade.detectMultiScale(img,
scaleFactor=1.15,
minNeighbors=10,
minSize=(5,5))
# 将人脸从图片中裁剪出来
for (x, y, w, h) in faces:
face = img[y:y + h, x:x + w] # 获取人脸区域框
1
plot_image(face, mode='gray')

output_20240607231505

读取数据

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
img_size = (64, 64)

imgs = []
labels = []
for i in range(len(name_list)):
name = name_list[i]
imgs_path = glob(f'data/person_data/{name}/*')
for img_path in imgs_path:
img = cv2.imread(img_path, 0)
# 使用级联分类器去提取人脸
faces = faceCascade.detectMultiScale(img,
scaleFactor=1.15,
minNeighbors=10,
minSize=(5,5))
if len(faces) == 1:
# 将人脸从图片中裁剪出来
for (x, y, w, h) in faces:
face = img[y:y + h, x:x + w] # 获取人脸区域框
face = cv2.resize(face, dsize=img_size) # 统一图像大小
face = cv2.equalizeHist(face) # 直方图均衡化,提高对比度
imgs.append(face)
labels.append(i)
# 将列表转化为数组
imgs = np.array(imgs)
labels = np.array(labels)
1
print(imgs.shape)
(333, 64, 64)

划分训练集和测试集

1
imgs_train, imgs_test, labels_train, labels_test = train_test_split(imgs, labels, test_size=0.1, random_state=2024)

特征构造

1
2
imgs_train_flatten = np.reshape(imgs_train, (len(imgs_train), -1))
imgs_test_flatten = np.reshape(imgs_test, (len(imgs_test), -1))
1
2
# n_components: 主成分数量
pca = PCA(n_components=50)
1
2
# 使用训练集去训练PCA模型,并将训练集数据进行转化
imgs_train_pca = pca.fit_transform(imgs_train_flatten)
1
2
# 使用训练集训练好的模型去转化测试集
imgs_test_pca = pca.transform(imgs_test_flatten)

使用SVM模型进行人脸识别

1
svm_model = SVC(probability=True)
1
svm_model.fit(imgs_train_pca, labels_train)
1
svm_model.score(imgs_test_pca, labels_test)
0.7647058823529411

使用训练好的SVM模型去识别未知图片

当图片中出现多个目标时,现有模型无法预测

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
45
46
47
48
49
# 读取测试图片
img_path = 'data/test_img2.png'
test_img = cv2.imread(img_path) # 彩色照片
test_img_org = cv2.imread(img_path, 0) # 黑白照片

# 使用级联分类器去提取人脸
faces = faceCascade.detectMultiScale(test_img_org,
scaleFactor=1.15,
minNeighbors=10,
minSize=(5,5))
if len(faces) >= 1:
# 将人脸从图片中裁剪出来
for (x, y, w, h) in faces:
face = test_img_org[y:y + h, x:x + w] # 获取人脸区域框

face = cv2.resize(face, dsize=img_size) # 统一图像大小
face = cv2.equalizeHist(face) # 直方图均衡化,提高对比度

# 主成分特征提取
test_img_pca = pca.transform(face.reshape((1, -1)))
# 使用SVM进行预测
svm_pred = svm_model.predict(test_img_pca)[0]
svm_max_P = svm_model.predict_proba(test_img_pca).max(axis=1)[0]
if svm_max_P < 0.8:
name_pred='NULL'
# 绘制人脸检测框
test_img = cv2.rectangle(test_img, (x, y), (x+w, y+h), (25, 57, 191), int(np.ceil(test_img_org.shape[0]/70)))
# 在人脸框上方填入人脸的预测结果
test_img = cv2.putText(test_img, # 绘制目标
name_pred, # 绘制文本
(x-5, y-5), # 绘制文本位置
fontFace=cv2.FONT_HERSHEY_SIMPLEX, # 绘制文本类型
fontScale=test_img_org.shape[0]/ 400, # 字体大小
color=(25, 57, 191), # 字体颜色
thickness=int(np.floor(test_img_org.shape[0]/200))) # 字体粗细
else:
# 获取到具体姓名
name_pred = name_list[svm_pred]

test_img = cv2.rectangle(test_img, (x, y), (x+w, y+h), (255,224, 93), int(np.ceil(test_img_org.shape[0]/70)))
test_img = cv2.putText(test_img, # 绘制目标
name_pred, # 绘制文本
(x-5, y-5), # 绘制文本位置
fontFace=cv2.FONT_HERSHEY_SIMPLEX, # 绘制文本类型
fontScale=test_img_org.shape[0]/ 400, # 字体大小
color=(255,224, 93), # 字体颜色
thickness=int(np.floor(test_img_org.shape[0]/200))) # 字体粗细
else:
print('未检测到人脸!')
1
plot_image(test_img)


output_20240607231506

当预测的目标在训练集中不包含时,还是会预测出一个人名

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
45
46
47
# 读取测试图片
img_path = 'data/test_img3.png'
test_img = cv2.imread(img_path)
test_img_org = cv2.imread(img_path, 0)

# 使用级联分类器去提取人脸
faces = faceCascade.detectMultiScale(test_img_org,
scaleFactor=1.15,
minNeighbors=10,
minSize=(5,5))
if len(faces) >= 1:
# 将人脸从图片中裁剪出来
for (x, y, w, h) in faces:
face = test_img_org[y:y + h, x:x + w] # 获取人脸区域框

face = cv2.resize(face, dsize=img_size) # 统一图像大小
face = cv2.equalizeHist(face) # 直方图均衡化,提高对比度

# 主成分特征提取
test_img_pca = pca.transform(face.reshape((1, -1)))
# 使用SVM进行预测
svm_pred = svm_model.predict(test_img_pca)[0]
svm_max_P = svm_model.predict_proba(test_img_pca).max(axis=1)[0]
if svm_max_P < 0.8:
name_pred='NULL'
test_img = cv2.rectangle(test_img, (x, y), (x+w, y+h), (25, 57, 191), int(np.ceil(test_img_org.shape[0]/70)))
test_img = cv2.putText(test_img, # 绘制目标
name_pred, # 绘制文本
(x-5, y-5), # 绘制文本位置
fontFace=cv2.FONT_HERSHEY_SIMPLEX, # 绘制文本类型
fontScale=test_img_org.shape[0]/ 400, # 字体大小
color=(25, 57, 191), # 字体颜色
thickness=int(np.floor(test_img_org.shape[0]/200))) # 字体粗细
else:
# 获取到具体姓名
name_pred = name_list[svm_pred]

test_img = cv2.rectangle(test_img, (x, y), (x+w, y+h), (255,224, 93), int(np.ceil(test_img_org.shape[0]/70)))
test_img = cv2.putText(test_img, # 绘制目标
name_pred, # 绘制文本
(x-5, y-5), # 绘制文本位置
fontFace=cv2.FONT_HERSHEY_SIMPLEX, # 绘制文本类型
fontScale=test_img_org.shape[0]/ 400, # 字体大小
color=(255,224, 93), # 字体颜色
thickness=int(np.floor(test_img_org.shape[0]/200))) # 字体粗细
else:
print('未检测到人脸!')
1
plot_image(test_img)

output_20240607231507

-------------本文结束感谢您的阅读-------------