经过上一篇博客,我们已经获得了人脸分类的模型,这次我们只需要简单的调整,就可以获得提取人脸特征的模型,然后对比人脸之间的特征,就可以知道2张图片是否是同一个人了
代码步骤如下
- 从“olivettifaces.jpg”中截取人脸,将同一个人的人脸放在同一文件夹下
- 读取上一个博客的训练好的模型
- 修改模型为提取特征的模型
- 从同一个头像文件夹下读取图片,将第一张图片与其他图片的特征做对比,查看对比结果
代码如下
1.截取人脸,同一个人脸放在同一文件夹下
import cv2
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
import os
# 将同一个人的头像放在一个文件中
def split_to_dataset(img_path, save_path):
olive = cv2.imread(img_path)
lable = 0
if not os.path.exists(save_path):
os.mkdir(save_path)# 新建/data/verify文件夹 放人脸测试的图片
if olive is None:
raise Exception("can not open the olivettifaces dataset file.")
for row in range(20):
for column in range(20):
img = olive[row * 57:(row + 1) * 57, column * 47:(column + 1) * 47]
if column == 10 or column == 0:
lable = lable + 1
tmp_path = save_path + "/" +str(lable -1) + "/"
if not os.path.exists(tmp_path):
os.mkdir(tmp_path)
cv2.imwrite(img=img, filename=tmp_path + str(column) +".jpg")
split_to_dataset("./data/olivettifaces.jpg","./data/verify")
2.加载模型
from keras import backend as K
def load_model(filepath):
import keras.models
model = keras.models.load_model(filepath, custom_objects={"AMSoftmax": AMSoftmax, "amsoftmax_loss": amsoftmax_loss})
return model
model = load_model("./trained_models/best_face_recognition_model.hdf5")
3.修改模型
def rebuild_model(model, input_layer="input", output_layer="feature"):
# if model is not an instance of Model, then try to load_model it by filepath.
print("model", model)
if isinstance(model, str):
from model.amsoftmax import load_model
model = load_model(model)
__input_layer = model.get_layer(name=input_layer)
__output_layer = model.get_layer(name=output_layer)
func = K.function([__input_layer.input],
[__output_layer.output])
input_shape = __input_layer.input_shape[1:]
output_shape = __output_layer.output_shape[1:]
return func
model = rebuild_model(model)
4.加载图片,用模型提取特征,并用余弦距离对比
# 返回2个向量的余弦距离
def cosine_similarity(vector1, vector2):
# if vector1 and vector2 are column vector, we can use:
# vector1.T * vector2
val = np.dot(vector1, vector2)
# or:
# val = vector1.dot(vector2.T)
norm = linalg.norm(vector1) * linalg.norm(vector2) #矩阵或向量范数
cos = val / norm
sim = 0.5 + 0.5 * cos # normalization
return sim
用第一张图片和同一个文件夹下的其他文件做对比
from numpy import linalg
for j in range(0,40):
verify = load_img("./data/verify/" + str(j))
vector_verify = model([verify])[0]
for i in range(0,len(verify)):
score = cosine_similarity(vector_verify[0], vector_verify[i])
print(score)
print(mix_score)
结果如下:
其他代码(之前博客已经编写的代码,这里还要用,就不详细说了)
from keras.utils import to_categorical
def preprocess_image(input_shape, image):
assert len(input_shape) == 3
if input_shape[-1] == 1:
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
input_shape = input_shape[:2]
image = cv2.resize(image, input_shape)
image = np.asarray(image, dtype='float64')/256
if len(image.shape) < 3:
image = np.expand_dims(image, -1)
return image
# 加载所有的图片,并处理为模型能识别的
def load_img(img_path):
input_shape = (224,224,1)
img_dirs = os.listdir(img_path)
X = []
for img_name in img_dirs:
img = cv2.imread(img_path + "/" + img_name)
img = preprocess_image(input_shape,img)
X.append(img)
X = np.array(X)
return X
AMSoftmax类
import tensorflow as tf
from keras import backend as K
from keras.layers import Dropout
from keras.engine.topology import Layer
from keras.models import Model
class AMSoftmax(Layer):
def __init__(self, units, **kwargs):
self.units = units
self.kernel = None
super(AMSoftmax, self).__init__(**kwargs)
def build(self, input_shape):
assert len(input_shape) >= 2
self.kernel = self.add_weight(name='kernel',
shape=(input_shape[1], self.units),
initializer='uniform',
trainable=True)
super(AMSoftmax, self).build(input_shape)
def call(self, inputs, **kwargs):
# get cosine similarity
# cosine = x * w / (||x|| * ||w||)
inputs = K.l2_normalize(inputs, axis=1)
kernel = K.l2_normalize(self.kernel, axis=0)
cosine = K.dot(inputs, kernel)
return cosine
def compute_output_shape(self, input_shape):
return input_shape[0], self.units
def get_config(self):
config = {'units': self.units}
base_config = super(AMSoftmax, self).get_config()
return dict(list(base_config.items()) + list(config.items()))
def amsoftmax_loss(y_true, y_pred, scale=30.0, margin=0.35):
# make two constant tensors.
m = K.constant(margin, name='m') # 边缘
s = K.constant(scale, name='s') # 比例
# reshape the label
label = K.reshape(K.argmax(y_true, axis=-1), shape=(-1, 1))
label = K.cast(label, dtype=tf.int32)
pred_batch = K.reshape(tf.range(K.shape(y_pred)[0]), shape=(-1, 1))
# concat the two column vectors, one is the pred_batch, the other is label.
ground_truth_indices = tf.concat([pred_batch,
K.reshape(label, shape=(-1, 1))], axis=1)
# get ground truth scores by indices
ground_truth_scores = tf.gather_nd(y_pred, ground_truth_indices)
# if ground_truth_score > m, group_truth_score = group_truth_score - m
added_margin = K.cast(K.greater(ground_truth_scores, m),
dtype=tf.float32) * m
added_margin = K.reshape(added_margin, shape=(-1, 1))
added_embedding_feature = tf.subtract(y_pred, y_true * added_margin) * s
cross_entropy = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(labels=y_true,
logits=added_embedding_feature)
loss = tf.reduce_mean(cross_entropy)
return loss
总结
- 大部分图片的距离都非常小(接近于1),但是有非常少的几个是0.9以下的
- 你也尝试在同一文件夹下方另外一个人的,结果大约会在0.6左右
- 结果证明:模型在olivettifaces数据集下,还是非常优秀的
- 但是,我们目前只用在olivettifaces下训练和测试,我们的模型很可能过拟合,想了解如何解决过拟合,可以看我的延伸篇中的介绍
- 这次我们的数据集都是截取的比较好的人脸,但现实生活中,会有很多背景等其他的影响,所以,我们在处理图片的时候,还需要截取出人脸呢?下一篇博客将向你介绍人脸的截取