OpenCV3-Python人脸识别方法—人脸识别与标记
上接《OpenCV3-Python人脸识别方法—基于摄像头》,实际应用中,有时我们不仅需要检测人脸信息,可能还需对识别到的人脸进行判断(是否是某个特定的人)?接下来,本篇介绍基于opencv的人脸数据采集和人物识别。
1. 人脸信息采集
人脸信息可通过两种方式获取:本地图像采集或从人脸数据库中获取;本文介绍本地图像采集的方式。
(1)采集流程
a. 打开摄像头,读取一帧图像。
b. 检测人脸,若检测到人脸信息,则裁剪灰度帧的区域,大小为200x200像素;
c. 保存文件到以此人名称命名的文件夹下,文件名后缀为.pgm
(2)源码
调用generate()函数时,需要根据实际情况更改函数参数。(例程中此参数对应为所采集信息的人的名称)
整个采集过程中,软件识别出人脸会自动编号保存,一般采集20张左右即可。
import cv2
import os
def generate(dirname):
face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye.xml')
#eye_cascade = cv2.CascadeClassifier('./cascades/haarcascade_eye_tree_eyeglasses.xml')
#创建目录
if(not os.path.isdir(dirname)):
os.makedirs(dirname)
#打开摄像头进行人脸图像采集
camera = cv2.VideoCapture(0)
count = 0
while (True):
ret, frame = camera.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
img = cv2.rectangle(frame,(x,y),(x+w,y+h),(255,0,0),2)
#重设置图像尺寸 200*200
f = cv2.resize(gray[y:y+h, x:x+w], (200, 200))
cv2.imwrite(dirname+'/%s.pgm' % str(count), f)
print(count)
count += 1
cv2.imshow("camera", frame)
if cv2.waitKey(100) & 0xff == ord("q"):
break
camera.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
generate("./data/lu") #根据实际需要修改,需先创建对应文件夹(3)目录下保存的文件
2. 人脸识别
人脸识别一般有三种方法,它们分别基于不同算法:Eigenfaces、Fisherfaces 和 Local Binary Pattern Histogram(LBPH)。
Eigenfaces: 通过PCA来处理。PCA的本质是识别某个训练集上的主成分,并计算出训练集(图像或帧中的检测到的人脸)相对于数据库的发散程度,并输出一个值。该值越小,表明人脸数据库和检测到的人脸之间的差别就越小;0值表示完全匹配。
Fisherfaces: 是从PCA中衍生发展出来的,采用更复杂的逻辑;尽管计算更加密集,但比Eigenfaces更容易得到准确效果。
LBPH: 将检测到的人脸分为小单元,并将其与模型中的对应单元比较,对每个区域匹配值产生一个直方图。LBPH是唯一允许模型样本人脸和检测到的人脸在形状、大小上可以不同的人脸识别算法。
(1)识别流程
本文采用Eigenfaces模型进行人脸识别,流程如下:
a. 读取分类好的数据集,来进行“训练”;
b. 读取摄像头的实时人脸数据,通过算法进行人脸分析;
c. 若否识别到目标,则获取目标被识别到的置信度评分;
d. 根据置信度评分范围,判断目标是否被正确识别;
(2)源码
#!/usr/bin/env python
# Software License Agreement (BSD License)
#
# Copyright (c) 2012, Philipp Wagner <bytefish[at]gmx[dot]de>.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided
# with the distribution.
# * Neither the name of the author nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
import os
import sys
import cv2
import numpy as np
def read_images(path, sz=None):
"""Reads the images in a given folder, resizes images on the fly if size is given.
Args:
path: Path to a folder with subfolders representing the subjects (persons).
sz: A tuple with the size Resizes
Returns:
A list [X,y]
X: The images, which is a Python list of numpy arrays.
y: The corresponding labels (the unique number of the subject, person) in a Python list.
"""
c = 0
X,y = [], []
names=[]
for dirname, dirnames, filenames in os.walk(path):
for subdirname in dirnames:
subject_path = os.path.join(dirname, subdirname)
for filename in os.listdir(subject_path):
try:
if (filename == ".directory"):
continue
filepath = os.path.join(subject_path, filename)
im = cv2.imread(os.path.join(subject_path, filename), cv2.IMREAD_GRAYSCALE)
if (im is None):
print ("image " + filepath + " is none" )
# resize to given size (if given)
if (sz is not None):
im = cv2.resize(im, sz)
X.append(np.asarray(im, dtype=np.uint8))
y.append(c)
except:
print ("Unexpected error:", sys.exc_info()[0])
raise
c = c+1
names.append(subdirname) #添加对应的目录名称
return [names,X,y]
def face_rec():
read_dir = "./data";
# Now read in the image data. This must be a valid path!
[names,X,y] = read_images(read_dir)
# Convert labels to 32bit integers. This is a workaround for 64bit machines,
# because the labels will truncated else. This will be fixed in code as
# soon as possible, so Python users don't need to know about this.
# Thanks to Leo Dirac for reporting:
y = np.asarray(y, dtype=np.int32)
# Create the Eigenfaces model. We are going to use the default
# parameters for this simple example, please read the documentation
# for thresholding:
#https://docs.opencv.org/3.0-beta/modules/face/doc/facerec/facerec_api.html?highlight=eigenfacerecognizer#Ptr<FaceRecognizer> createEigenFaceRecognizer(int num_components , double threshold)
#注意:此函数新版发生变化
model = cv2.face_EigenFaceRecognizer.create()
#Fisherfaces的人脸识别
#model = cv2.face_FisherFaceRecognizer.create()
# Read
# Learn the model. Remember our function returns Python lists,
# so we use np.asarray to turn them into NumPy lists to make
# the OpenCV wrapper happy:
model.train(np.asarray(X), np.asarray(y))
camera = cv2.VideoCapture(0)
face_cascade = cv2.CascadeClassifier('./cascades/haarcascade_frontalface_default.xml')
while(True):
read, img = camera.read()
faces = face_cascade.detectMultiScale(img, 1.3, 5)
for (x,y,w,h) in faces:
img = cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),2)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
roi = gray[x:x+w, y:y+h]
try:
roi = cv2.resize(roi,(200,200),interpolation=cv2.INTER_LINEAR)
# model.predict is going to return the predicted label and the associated confidence:
[p_label, p_confidence] = model.predict(roi)
# Print it:
print ("Predicted label = %d (confidence=%.2f)" % (p_label, p_confidence))
#show name
cv2.putText(img,names[p_label],(x,y-20),cv2.FONT_HERSHEY_SIMPLEX,1,255,2)
except:
continue
cv2.imshow("camera", img)
if cv2.waitKey(100) & 0xff == ord("q"):
break
camera.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
face_rec()(3)识别结果
(4)置信度评分
predict()函数返回两个数组,第一个元素是识别个体的标签(之前提到的目录名称),第二个是置信度评分;实际应用中,可通过设置置信度评分的阈值,来达到更准确识别物体的目的。
Eigenfaces/Fisherfaces和LBPH的置信度评分值完全不同。Eigenfaces/Fisherfaces将产生0-20000的值,而任意低于4000到5000的评分都可认为是相当可靠的。
