(借鉴于网课,和大家共勉)
from sklearn import neighbors
import os
import os.path
import pickle
from PIL import Image, ImageDraw
import face_recognition as fr
from face_recognition.cli import image_files_in_folder
#=========
#2.函数定义
#=========
def train(train_dir, model_save_path=‘trained_knn_model.clf’, n_neighbors=3,
knn_algo=‘ball_tree’):
“”"
训练一个KNN分类器.
:param train_dir: 训练目录.其下对每个已知的人,分别以其名字,建立一个文件夹.
:param model_save_path: (optional)
:param n_neighbors:
有默认值.
:param knn_algo: (optional) 支持KNN的数据结构.
:return: KNN分类器.
“”"
#生成训练集
X = []
y = []
#遍历训练集中的每一个人
for class_dir in os.listdir(train_dir):
if not os.path.isdir(os.path.join(train_dir, class_dir)):
continue #结束当前循环, 进入下一个循环
# 遍历这个人的每一张照片
for img_path in image_files_in_folder(os.path.join(train_dir, class_dir)):
image = fr.load_image_file(img_path)
boxes = fr.face_locations(image)
# 对于当前图片,增加编码到训练集
X.append(fr.face_encodings(image,
known_face_locations=boxes)[0])
y.append(class_dir)
# 决定k值for weighting in the KNN classifier
if n_neighbors is None:
#n_neighbors = int(round(math.sqrt(len(X))))
n_neighbors = 3
# 创建并训练分类器
knn_clf = neighbors.KNeighborsClassifier(n_neighbors=n_neighbors)
knn_clf.fit(X, y)
# 保存训练好的分类器
if model_save_path is not None:
with open(model_save_path, 'wb') as f:
pickle.dump(knn_clf, f)
return knn_clf
def predict(X_img_path, knn_clf=None, model_path=None, distance_threshold=0.45):
“”"
利用KNN分离器识别给定照片中的人脸
:return: [(人名1, 边界盒子1), …]
“”"
if knn_clf is None and model_path is None:
raise Exception(“必须提供KNN分类器:可选方式为 knn_clf 或 model_path”)
# 加载训练好的KNN模型(如果有)
# rb 表示要读入二进制数据
if knn_clf is None:
with open(model_path, 'rb') as f:
knn_clf = pickle.load(f)
# 加载图片,发现人脸的位置
X_img = fr.load_image_file(X_img_path)
X_face_locations = fr.face_locations(X_img)
# 对测试图片中的人脸编码
encodings = fr.face_encodings(X_img,
known_face_locations=X_face_locations)
# 利用KNN model 找出与测试人脸最匹配的人脸
# encodings: 128个人脸特征构成的向量
closest_distances = knn_clf.kneighbors(encodings, n_neighbors=1)
are_matches = [closest_distances[0][i][0] <= distance_threshold
for i in range(len(X_face_locations))]
# 预言类别,并 remove classifications that aren't within the threshold
return [(pred, loc) if rec else ("unknown", loc)
for pred, loc, rec in zip(knn_clf.predict(encodings),
X_face_locations, are_matches)]
def show_names_on_image(img_path, predictions):
“”"
人脸识别可视化.
:param img_path: 待识别图片的位置
:param predictions:预测的结果
“”"
pil_image = Image.open(img_path).convert(“RGB”)
draw = ImageDraw.Draw(pil_image)
for name, (top, right, bottom, left) in predictions:
# 用Pillow模块画出人脸边界盒子
draw.rectangle(((left, top), (right, bottom)), outline=(255, 0, 255))
# pillow里可能生成非UTF-8格式,所以这里做如下转换
name = name.encode("UTF-8")
name = name.decode("ascii") #L add
# 在人脸下写下名字,作为标签
text_width, text_height = draw.textsize(name)
draw.rectangle(((left, bottom - text_height - 10), (right, bottom)),
fill=(255, 0, 255), outline=(255, 0, 255))
draw.text((left + 6, bottom - text_height - 5), name, fill=(255, 255, 255))
# 追加名字到列表li_names
li_names.append(name)
# 从内存删除draw
del draw
# 显示结果图
pil_image.show()
#========
#统计分析
#========
li_names = []
def count(train_dir):
“”"
Counts the total number of the set.
“”"
path = train_dir
count = 0
for fn in os.listdir(path): #fn 表示的是文件名
count = count + 1
return count
def list_all(train_dir):
“”"
Determine the list of all names.
“”"
path = train_dir
result = []
for fn in os.listdir(path): #fn 表示的是文件名
result.append(fn)
return result
def stat_output():
s_list = set(li_names)
s_list_all = set(list_all(“examples/train”))
if “unknown” in s_list:
s_list.remove(“unknown”)
tot_num = count("examples/train")
s_absent = set(s_list_all - s_list)
print("\n")
print("*******************************************\n")
print("全体名单:",s_list_all)
print("已到名单:",s_list)
print("应到人数:",tot_num)
print("已到人数:",len(s_list))
print("出勤率:{:.2f}".format(float(len(s_list))/float(tot_num)))
print("未到:",s_absent)
if name == “main”:
# 1 训练KNN分类器(它可以保存,以便再用)
print(“正在训练KNN分类器…”)
classifier = train(“examples/train”, model_save_path=“trained_knn_model.clf”,
n_neighbors=2)
print(“完成训练!”)
# 2 利用训练好的分类器,对新照片进行预测
for image_file in os.listdir("examples/test"):
full_file_path = os.path.join("examples/test", image_file)
print("在{}中寻找人脸...".format(image_file))
# 利用分类器,找出所有的人脸;
# 要么传递一个classifier文件名,要么一个classifier模型实例
predictions = predict(full_file_path, model_path="trained_knn_model.clf")
# 打印结果
for name, (top, right, bottom, left) in predictions:
print("发现{}, 位置: ({}, {},{},{})".format(name, top, right, bottom, left))
# 在图片上显示预测结果
show_names_on_image(os.path.join("examples/test", image_file), predictions)
# 3.输出统计结果
stat_output()