[深度学习-实践]人脸识别的例子-Tensorflow2.x Keras

系列文章目录

人脸检测的例子-Tensorflow2.x keras
人脸识别的例子-Tensorflow2.x Keras
实时人脸识别例子-Tensorflow2.x Keras


人脸识别的例子-Tensorflow2.x Keras

  • 系列文章目录
  • 1. 什么是人脸识别
  • 2. 环境准备
  • 3. 数据准备
    • 3.1. 模型准备
    • 3.2. 数据集准备
  • 4. 人脸识别代码讲解
    • 4.1 利用MTCNN抽取人脸图片。
    • 4.2 利用FaceNet提取人脸的特征
    • 4.3 利用SVM对人脸的分类
    • 4.4 预测

1. 什么是人脸识别

人脸识别是一项基于计算机的视觉任务,用于基于人脸的照片来识别和验证人。

FaceNet是由Google的研究人员于2015年开发的人脸识别系统,该系统在一系列人脸识别基准数据集上取得了当时的最新水平。 FaceNet系统可以广泛使用,这要归功于该模型的多个第三方开源实现和预先训练的模型的可用性。

FaceNet系统可用于从人脸中提取高质量特征(称为人脸嵌入),然后将其用于训练人脸识别系统。

2. 环境准备

pip install mtcnn
mtcnn 版本是0.1.0
Tensorflow 版本是2.10
Sklearn

import mtcnn
import tensorflow as tf
print(mtcnn.__version__)
print(tf.__version__)
0.1.0
2.1.0

3. 数据准备

3.1. 模型准备

下载Keras的FaceNet网络t的预训练模型
下载后把它放在你的工作目录。名字命名为‘facenet_keras.h5’

执行下面代码验证一下你的FaceNet网络吧

# example of loading the keras facenet model
from keras.models import load_model
# load the model
model = load_model('facenet_keras.h5')
# summarize input and output shape
print(model.inputs)
print(model.outputs)

我们可以看到FaceNet网络输入是160x160的彩色图片,输出是128维的向量,也就是提出出来的128个特征值。

[<tf.Tensor 'input_1:0' shape=(None, 160, 160, 3) dtype=float32>]
[<tf.Tensor 'Bottleneck_BatchNorm/cond/Identity:0' shape=(None, 128) dtype=float32>]

3.2. 数据集准备

下载人脸数据集

下载后解压到你的工作目录,解压后的结构如下:

5-celebrity-faces-dataset
├── train
│   ├── ben_afflek
│   ├── elton_john
│   ├── jerry_seinfeld
│   ├── madonna
│   └── mindy_kaling
└── val
    ├── ben_afflek
    ├── elton_john
    ├── jerry_seinfeld
    ├── madonna
    └── mindy_kaling

整个数据集包含训练集和验证集。打开训练集中的ben_afflek文件,可以看到14张如下的图片。
[深度学习-实践]人脸识别的例子-Tensorflow2.x Keras_第1张图片

4. 人脸识别代码讲解

4.1 利用MTCNN抽取人脸图片。

利用PIL加载图片,然后转换成RGB. 最后用Numpy转换成数组。

image = Image.open(filename)
image = image.convert('RGB')
pixels = np.asarray(image)

利用MTCNN 检测人脸的位置。

detector = MTCNN()
results = detector.detect_faces(pixels)

根据MTCNN检测出来的人脸的位置(人脸的左上角坐标,宽和高),计算出右上角的坐标。为什么要把左上角的坐标上个绝对值呢?因为有时输出来结果是负值,可能是Dug.

x1, y1, width, height = results[0]['box']
x1, y1 = abs(x1), abs(y1)
x2, y2 = x1 + width, y1 + height

剪出人脸

face = pixels[y1:y2, x1:x2]

利用PIL包,调整剪出的脸的大小到160x160,因为FaceNet的输入是固定大小160x160

# resize pixels to the model size
image = Image.fromarray(face)
image = image.resize((160, 160))
face_array = asarray(image)

下面是以上的完整代码。

from os import listdir
from PIL import Image
import numpy as np
from matplotlib import pyplot
from mtcnn.mtcnn import MTCNN

# extract a single face from a given photograph
def extract_face(filename, required_size=(160, 160)):
	# load image from file
	image = Image.open(filename)
	# convert to RGB, if needed
	image = image.convert('RGB')
	# convert to array
	pixels = np.asarray(image)
	# create the detector, using default weights
	detector = MTCNN()
	# detect faces in the image
	results = detector.detect_faces(pixels)
	# extract the bounding box from the first face
	x1, y1, width, height = results[0]['box']
	# bug fix
	x1, y1 = abs(x1), abs(y1)
	x2, y2 = x1 + width, y1 + height
	# extract the face
	face = pixels[y1:y2, x1:x2]
	# resize pixels to the model size
	image = Image.fromarray(face)
	image = image.resize(required_size)
	face_array = np.asarray(image)
	return face_array

# specify folder to plot
folder = r'5-celebrity-faces-dataset/train/ben_afflek/'
i = 1
# enumerate files
for filename in listdir(folder):
	# path
	path = folder + filename
	# get face
	face = extract_face(path)
	print(i, face.shape)
	# plot
	pyplot.subplot(2, 7, i)
	pyplot.axis('off')
	pyplot.imshow(face)
	i += 1
pyplot.show()
1 (160, 160, 3)
2 (160, 160, 3)
3 (160, 160, 3)
4 (160, 160, 3)
5 (160, 160, 3)
6 (160, 160, 3)
7 (160, 160, 3)
8 (160, 160, 3)
9 (160, 160, 3)
10 (160, 160, 3)
11 (160, 160, 3)
12 (160, 160, 3)
13 (160, 160, 3)
14 (160, 160, 3)

执行结果如下:可以看到已经把ben_afflek的所有的照片都剪去出来了。
[深度学习-实践]人脸识别的例子-Tensorflow2.x Keras_第2张图片
加载图片,抽取其中的人脸。返回给定目录的所有人脸数组。

def load_faces(directory):
	faces = list()
	for filename in listdir(directory):
		path = directory + filename
		face = extract_face(path)
		faces.append(face)
	return faces

根据指定的目录,获取数据集,这些数据里包含都是抽取的人脸的数组。

# load a dataset that contains one subdir for each class that in turn contains images
def load_dataset(directory):
	X, y = list(), list()
	# enumerate folders, on per class
	for subdir in listdir(directory):
		# path
		path = directory + subdir + '/'
		# skip any files that might be in the dir
		if not isdir(path):
			continue
		# load all faces in the subdirectory
		faces = load_faces(path)
		# create labels
		labels = [subdir for _ in range(len(faces))]
		# summarize progress
		print('>loaded %d examples for class: %s' % (len(faces), subdir))
		# store
		X.extend(faces)
		y.extend(labels)
	return asarray(X), asarray(y)

获取训练集与验证集的人脸数组,然后利用Numpy保存成5-celebrity-faces-dataset.npz,后面会用。

trainX, trainy = load_dataset('5-celebrity-faces-dataset/train/')
print(trainX.shape, trainy.shape)
testX, testy = load_dataset('5-celebrity-faces-dataset/val/')
print(testX.shape, testy.shape)
savez_compressed('5-celebrity-faces-dataset.npz', trainX, trainy, testX, testy)

下面是一个完整的代码。运行后会产生5-celebrity-faces-dataset.npz

from os import listdir
from os.path import isdir
from PIL import Image
from matplotlib import pyplot
import numpy as np
from mtcnn.mtcnn import MTCNN

def extract_face(filename, required_size=(160, 160)):
	image = Image.open(filename)
	image = image.convert('RGB')
	pixels = np.asarray(image)
	detector = MTCNN()
	results = detector.detect_faces(pixels)
	x1, y1, width, height = results[0]['box']
	x1, y1 = abs(x1), abs(y1)
	x2, y2 = x1 + width, y1 + height
	face = pixels[y1:y2, x1:x2]

	image = Image.fromarray(face)
	image = image.resize(required_size)
	face_array =  np.asarray(image)
	return face_array

# load images and extract faces for all images in a directory
def load_faces(directory):
	faces = list()
	for filename in listdir(directory):
		path = directory + filename
		face = extract_face(path)
		faces.append(face)
	return faces

# load a dataset that contains one subdir for each class that in turn contains images
def load_dataset(directory):
	X, y = list(), list()
	# enumerate folders, on per class
	for subdir in listdir(directory):
		path = directory + subdir + '/'
		if not isdir(path):
			continue
		faces = load_faces(path)
		labels = [subdir for _ in range(len(faces))]
		print('>loaded %d examples for class: %s' % (len(faces), subdir))
		X.extend(faces)
		y.extend(labels)
	return np.asarray(X),  np.asarray(y)

trainX, trainy = load_dataset('5-celebrity-faces-dataset/train/')
print(trainX.shape, trainy.shape)
testX, testy = load_dataset('5-celebrity-faces-dataset/val/')
np.savez_compressed('5-celebrity-faces-dataset.npz', trainX, trainy, testX, testy)

4.2 利用FaceNet提取人脸的特征

加载人脸的数据集。

data = load('5-celebrity-faces-dataset.npz')
trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']
print('Loaded: ', trainX.shape, trainy.shape, testX.shape, testy.shape)

加载FaceNet的模型。

model = load_model('facenet_keras.h5')
print('Loaded Model')

把人脸的数组转换为float32类型,标准化,外加一个维度。利用FaceNet模型提取人脸的128位特征。

def get_embedding(model, face_pixels):
	face_pixels = face_pixels.astype('float32')
	# standardize pixel values across channels (global)
	mean, std = face_pixels.mean(), face_pixels.std()
	face_pixels = (face_pixels - mean) / std
	# transform face into one sample
	samples = expand_dims(face_pixels, axis=0)
	yhat = model.predict(samples)
	return yhat[0]

下面是以上的一个完整的代码,加载人脸数据集5-celebrity-faces-dataset.npz,利用FaceNet提取人脸的128维特征。然后把所有人脸的特征保存到5-celebrity-faces-embeddings.npz文件中。

import numpy as np
import tensorflow as tf

def get_embedding(model, face_pixels):
	face_pixels = face_pixels.astype('float32')
	mean, std = face_pixels.mean(), face_pixels.std()
	face_pixels = (face_pixels - mean) / std
	samples = np.expand_dims(face_pixels, axis=0)
	yhat = model.predict(samples)
	return yhat[0]

data = np.load('5-celebrity-faces-dataset.npz')
trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']
print('Loaded: ', trainX.shape, trainy.shape, testX.shape, testy.shape)
model = tf.keras.models.load_model('acenet_keras.h5')
print('Loaded Model')
newTrainX = list()
for face_pixels in trainX:
	embedding = get_embedding(model, face_pixels)
	newTrainX.append(embedding)
newTrainX = np.asarray(newTrainX)
print(newTrainX.shape)
newTestX = list()
for face_pixels in testX:
	embedding = get_embedding(model, face_pixels)
	newTestX.append(embedding)
newTestX = np.asarray(newTestX)
print(newTestX.shape)
np.savez_compressed('5-celebrity-faces-embeddings.npz', newTrainX, trainy, newTestX, testy)
Loaded:  (93, 160, 160, 3) (93,) (25, 160, 160, 3) (25,)
Loaded Model
(93, 128)
(25, 128)

4.3 利用SVM对人脸的分类

加载人脸的特征的数据集,

# load dataset
data = load('5-celebrity-faces-embeddings.npz')
trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']
print('Dataset: train=%d, test=%d' % (trainX.shape[0], testX.shape[0]))

对人脸进行特征归一化,这里利用Sklearn做的。

in_encoder = Normalizer(norm='l2')
trainX = in_encoder.transform(trainX)
testX = in_encoder.transform(testX)

把图片的label也就是人名转换为数字。

out_encoder = LabelEncoder()
out_encoder.fit(trainy)
trainy = out_encoder.transform(trainy)
testy = out_encoder.transform(testy)

利用SVM支持向量机对人脸特征数据集进行训练。

model = SVC(kernel='linear')
model.fit(trainX, trainy)

对训练后的SVM模型评估.

# predict
yhat_train = model.predict(trainX)
yhat_test = model.predict(testX)
# score
score_train = accuracy_score(trainy, yhat_train)
score_test = accuracy_score(testy, yhat_test)
# summarize
print('Accuracy: train=%.3f, test=%.3f' % (score_train*100, score_test*100))

以上的完整的代码。

import numpy as np
import sklearn as sklearn
from sklearn import preprocessing
from sklearn import svm
data = np.load('5-celebrity-faces-embeddings.npz')
trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']
print('Dataset: train=%d, test=%d' % (trainX.shape[0], testX.shape[0]))

in_encoder = preprocessing.Normalizer(norm='l2')
trainX = in_encoder.transform(trainX)
testX = in_encoder.transform(testX)
# label encode targets
out_encoder = sklearn.preprocessing.LabelEncoder()
out_encoder.fit(trainy)
trainy = out_encoder.transform(trainy)
testy = out_encoder.transform(testy)
# fit model
model = sklearn.svm.SVC(kernel='linear', probability=True)
model.fit(trainX, trainy)
# predict
yhat_train = model.predict(trainX)
yhat_test = model.predict(testX)
# score
score_train = sklearn.metrics.accuracy_score(trainy, yhat_train)
score_test = sklearn.metrics.accuracy_score(testy, yhat_test)
# summarize
print('Accuracy: train=%.3f, test=%.3f' % (score_train*100, score_test*100))

执行结果,100%,全对。

Dataset: train=93, test=25
Accuracy: train=100.000, test=100.000

4.4 预测

利用5-celebrity-faces-dataset.npz抽取的人脸数据集,与人脸的特征数据集进行预测。

from random import choice
from numpy import load
from numpy import expand_dims
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import Normalizer
from sklearn.svm import SVC
from matplotlib import pyplot
# load faces
data = load('5-celebrity-faces-dataset.npz')
testX_faces = data['arr_2']
# load face embeddings
data = load('5-celebrity-faces-embeddings.npz')
trainX, trainy, testX, testy = data['arr_0'], data['arr_1'], data['arr_2'], data['arr_3']
# normalize input vectors
in_encoder = Normalizer(norm='l2')
trainX = in_encoder.transform(trainX)
testX = in_encoder.transform(testX)
# label encode targets
out_encoder = LabelEncoder()
out_encoder.fit(trainy)
trainy = out_encoder.transform(trainy)
testy = out_encoder.transform(testy)
# fit model
model = SVC(kernel='linear', probability=True)
model.fit(trainX, trainy)
# test model on a random example from the test dataset
selection = choice([i for i in range(testX.shape[0])])
random_face_pixels = testX_faces[selection]
random_face_emb = testX[selection]
random_face_class = testy[selection]
random_face_name = out_encoder.inverse_transform([random_face_class])
# prediction for the face
samples = expand_dims(random_face_emb, axis=0)
yhat_class = model.predict(samples)
yhat_prob = model.predict_proba(samples)
# get name
class_index = yhat_class[0]
class_probability = yhat_prob[0,class_index] * 100
predict_names = out_encoder.inverse_transform(yhat_class)
print('Predicted: %s (%.3f)' % (predict_names[0], class_probability))
print('Expected: %s' % random_face_name[0])
# plot for fun
pyplot.imshow(random_face_pixels)
title = '%s (%.3f)' % (predict_names[0], class_probability)
pyplot.title(title)
pyplot.show()
Predicted: mindy_kaling (92.835)
Expected: mindy_kaling

结果如下:
[深度学习-实践]人脸识别的例子-Tensorflow2.x Keras_第3张图片

你可能感兴趣的:(tensorflow,深度学习,tensorflow)