简单易用;无需使用Pytorch或Tensorflow等机器学习框架,基于Face-Recognition库提取特征向量,使用scikit-learn的SVC作为分类方法
本项目受Github项目ageitgey/face_recognition启发,它使用到的卷积神经网络结构如下:
经过作者训练得到的深度卷积神经网络能够在人脸识别挑战Labeled Faces in the Wild Home中获得99.38%的准确率,该模型以C++库的形式封装在了Python库face-recognition中,保证了调用的高效性和效果的可复现性,使用者无需在大型训练集上重新训练,只需以Python API的形式直接在程序内调用。
经过该深度卷积神经网络的特征提取,将会产生一组128维的向量,由于类别数小于特征数,以及推理时间等因素,本项目使用到的方法就没有采用全连接层与Softmax的端对端结构,而是采用了一个类似于R-CNN(Girshick et al., 2014)的结构:特征提取与分类网络分离,其特征提取网络使用深度卷积神经网络、分类网络采用支持向量机(Support Vector Machine, SVM)。具体的,其约束目标可以写作:
其中,C取正数,其值越大表示对误差的关注程度越高。
为了验证识别方法的性能,本项目在实现的方法上使用了K折交叉验证,本项目设置的K值为5。经验证,本项目使用的方法兼顾了识别效率和识别准确率,5折交叉验证的准确率为98.66%,原始分辨率下的推理时间为120ms。总体识别网络的结构为:
本项目将数据集中的文件读入,使用face-recognition库中的深度卷积神经网络进行面部特征提取和编码,每张图片生成一个128维的向量。由于度卷积神经网络的推理速度较慢,读取一张图片返回结果的平均时间在1s以上,因此在读取数据集后将会对内存中的两个数组进行持久化处理,保存为data_list.p(pickle数据类型),方便分类模型下次直接读取训练。
如果使用自己的数据集或GT数据集,请将它放置在 /gt_db
目录下,该目录结构应当像这样:
gt_db
├─ s01 # 人名
│ ├─ 01.jpg
│ ├─ ...
│ └─ 15.jpg # 该人名下的照片
├─ ...
└─ s50
其人脸分类流程与训练过程一次正向传播类似:使用深度卷积神经网络进行面部特征提取→使用支持向量机对该128维特征向量进行分类,如果含有多张人脸,则按顺序依次进行分类→返回结果并传递给UI,显示在输出框中。
以下是程序运行效果图:
使用face-recognition库进行面部特征提取生成面部编码,遍历数据集得到对应姓名标签,并将编码-标签文件以pickle文件的形式存储在根目录下。
def generate_file(train_directory):
_encodings = []
_names = []
for person in tqdm.tqdm(train_directory):
if ".DS_Store" not in person: # .DS_Store文件是文件系统下的一类隐藏文件,遍历时需要排除
pix = os.listdir('gt_db/' + person) # 人名类似 s01, s02 etc.
# 遍历该层级下所有照片
for person_img in pix:
if ".DS_Store" not in person_img:
# 获取所有面部编码
face = face_recognition.load_image_file('gt_db/' + person + "/" + person_img)
face_bounding_boxes = face_recognition.face_locations(face, model="cnn")
# 可能会出现无法识别/超过一个人的情况,无法用作训练集
if len(face_bounding_boxes) == 1:
face_enc = face_recognition.face_encodings(face)[0]
# 加入编码
_encodings.append(face_enc)
_names.append(person)
else:
print('')
print('Warning: ' + person + "/" + person_img + " was skipped and can't be used for training")
data_file = open('data_list.p', 'wb')
pickle.dump([_encodings, _names], data_file)
data_file.close()
print('data_list.p has been generated in the root')
return _encodings, _names
def load_params(re_generate):
if not re_generate:
file = open('data_list.p', 'rb')
_lists = pickle.load(file)
_encodings = _lists[0]
_names = _lists[1]
else:
_encodings, _names = generate_file(train_dir)
return _encodings, _names
注意:在
/utils.py
的第25行,可以设置face-recognition库的识别方式,在本项目中,默认识别方式为CNN,即代码中表现为
face_locations = face_recognition.face_locations(_test_image, model="cnn")
,如果想要更快的识别效率,可以将model="cnn"
删去,此时使用的是HOG进行识别。更改之后pickle文件的生成速度也会快两倍以上。
对验证或测试图像使用face-recognition库进行面部特征提取生成面部编码,通过分类器得到最终识别结果 。
def test_image(_clf, image_loc='test.jpg'):
# 载入数据
_test_image = face_recognition.load_image_file(image_loc)
face_locations = face_recognition.face_locations(_test_image, model="cnn")
no = len(face_locations)
print("Number of faces detected: ", no)
print("Found:")
names = []
for i in range(no):
test_image_enc = face_recognition.face_encodings(_test_image)[i]
name = _clf.predict([test_image_enc])
names.append(name[0])
print(*name)
return names
def evaluate(encodings, names):
clf_all = svm.SVC(kernel='linear') # 创建线性核SVM
scores_cv = cross_val_score(clf_all, encodings, names, cv=5) # 5折交叉验证
clf_all.fit(encodings, names) # 拟合数据集
print("Cross validation scores: {}".format(scores_cv))
return scores_cv, clf_all
继承自Tkinter,用于完成主界面的显示
class MainWindows(tk.Tk):
def __init__(self):
super().__init__() # 初始化基类
self.title("Face Recognition Master")
self.resizable(width=False, height=False)
self.minsize(640, 320)
self.tabControl = ttk.Notebook(self) # 创建标签栏整体
self.tab1 = ttk.Frame(self.tabControl) # 创建标签栏1
self.tab2 = ttk.Frame(self.tabControl) # 创建标签栏2
self.tab3 = ttk.Frame(self.tabControl) # 创建标签栏3
self.menu_bar = Menu(self) # 创建菜单栏
self.init_ui()
self.selected_files = [] # 被选中的文件,获取识别结果被使用
self.photo_libs = [] # 本地图片库
self.feature_libs = [] # 本地特征向量库
self.lib_path = 'gt_db/' # 本地库文件路径
self.update_treeview()
def init_ui(self): # 初始化 UI界面
self.tabControl.add(self.tab1, text='model')
self.tabControl.add(self.tab2, text='face recognition')
self.tabControl.add(self.tab3, text='database')
self.tabControl.pack(expand=1, fill="both")
self.init_tab1()
self.init_tab2()
self.init_tab3()
self.config(menu=self.menu_bar)
self.init_menu()
def select_file(self): # 选择文件进行测试
self.selected_files = []
ftypes = [('Image Files', '*.tif *.jpg *.png')]
dlg = filedialog.Open(filetypes=ftypes, multiple=True)
filename = dlg.show()
self.selected_files = filename
return filename
def select_btn_tab2(self): # 交互操作反馈(返回图片)
self.show_img([self.label_rec], self.select_file())
def get_result2(self): # 返回结果(预测值),如果没有已训练模型将会提示
global clf
if clf is None:
self.name2.set('⚠️ Please train a model first! (go to page)' )
if len(self.selected_files) >= 1 and clf is not None:
file = self.selected_files[0]
names = test_image(clf, image_loc=file)
list_ = ' '.join(str(name) for name in names)
self.name2.set(list_)
Girshick, Ross, et al. “Rich feature hierarchies for accurate object detection and semantic segmentation.” Proceedings of the IEEE conference on computer vision and pattern recognition. 2014.
H.-W. Ng, S. Winkler. A data-driven approach to cleaning large face datasets. Proc. IEEE International Conference on Image Processing (ICIP), Paris, France, Oct. 27-30, 2014
O. M. Parkhi, A. Vedaldi, A. Zisserman Deep Face Recognition British Machine Vision Conference, 2015.
全部代码已上传至Github仓库
https://github.com/HaoyuCui/Simple-Face-Recognition
[ 此Repo遵守Apache-2.0协议 ]
如果我的工作对你有帮助,欢迎点赞及Star!