【火炉炼AI】机器学习055-使用LBP直方图创建人脸识别器

【火炉炼AI】机器学习055-使用LBP直方图创建人脸识别器

(本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )html

在我前面的博文【火炉炼AI】机器学习052-OpenCV构建人脸鼻子眼睛检测器中,讲到了人脸检测的方法和代码实现,但在不少实际场合,咱们须要作的是人脸识别,即判断图片中的那张脸是张三仍是李四,故而本篇文章咱们来看看如何使用LBP直方图来创建一我的脸识别器。git


1. 局部二值模式简介

局部二值模式(Local Binary Pattern, LBP)是一种用来描述图像局部纹理特征的算子,其最大优点在于旋转不变性,灰度不变性,可以多分辨分析。局部纹理分析有不少潜在的应用,好比工业表层检测,远程监控,图像分析等。github

LBP的基本思想是:原始的LBP算子是3*3的窗口,以中心像素为阈值,将相邻的8个像素的灰度值与中心像素进行比较,若是大于,则设为1,小于则为0,故而获得这9个像素的二值化图,故而名称为局部二值化,以下图所示。从二值化图的左边中心点像素为起点,逆时针方向为正方形,按顺序取该二值化数值,便获得图中Pattern的二进制数值,此数值就是一个LBP编码,此时,咱们称该中心像素点的LBP值为11110001。若是对一幅图像中的全部像素点都计算LBP值,获得的就是这幅图的LBP特征图。算法

关于灰度不变性:很明显,原始的局部图中若是灰度值都同时增长一个值或同时减去一个值,便至关于亮度增长或减小,但此时,获得的LBP编码不变,故而称为灰度不变性。须要注意的是:该灰度不变性仅仅适用于灰度值的单调变化。以下图app

上面的LBP算子有一个缺陷,它只覆盖一个固定半径范围内的小区域,这显然不能知足不一样尺寸和频率纹理的须要,故而有人对其进行改进,将3*3领域扩展到任意领域,并用圆形领域代替正方形领域,以下图为以中心像素点为圆心,R为半径,在圆上均匀的选取P个点做为采样点的状况。机器学习

上图中,R的大小决定了圆的大小,反映了二维空间的尺度;而P的大小决定了采样点数,反映了角度空间的分辨率。一样的,咱们还能够改变R和P的值,实现不一样的尺度和角度分辨率(以下图)。这也是之后“多分辨率分析”的理论基础。函数

上面的LBP算子虽然可以实现多分辨率,但却不是旋转不变性,图像的旋转会获得不一样的LBP值,故有人提出了具备旋转不变性的LBP算子,即不断旋转圆形领域获得一系列初始定义的LBP值,取其最小值做为该领域的LBP值。学习

在LBP的应用中,好比人脸识别,纹理分析中,咱们通常不将LBP图谱做为特征向量用于分类识别,而是采用LBP特征图的统计直方图来做为特征向量。测试

使用LBP直方图来进行特征提取的步骤通常为:编码

1) 首先将检测窗口划分为16×16的小区域(cell)

2) 对于每一个cell中的一个像素,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,不然为0。这样,3*3邻域内的8个点经比较可产生8位二进制数,即获得该窗口中心像素点的LBP值

3) 而后计算每一个cell的直方图,即每一个数字(假定是十进制数LBP值)出现的频率;而后对该直方图进行归一化处理

4)最后将获得的每一个cell的统计直方图进行链接成为一个特征向量,也就是整幅图的LBP纹理特征向量;而后即可利用SVM或者其余机器学习算法进行分类了。

关于LBP的深刻理论,能够参考博文:LBP(局部二值模式)特征提取原理局部二值模式(Local Binary Patterns)进行纹理分类


2. 准备数据集

本项目所用的数据集是脸部数据集的一个子集,此处我只选择三我的的脸部图片来进行测试。数据集有两部分,一个train的文件夹中有三个子文件夹,每一个子文件夹表明一我的的脸部图片,test的文件夹只含有各类人脸图片,没有子文件夹。因此首先咱们须要将这些图片加载到内存中,下面定义一个函数来加载图片。

# 定义一个函数来加载图片数据集
def load_train_set(imgs_folder,face_cascade):
    ''' 从imgs_folder中加载图片数据和标记,注意imgs_folder中包含有多个子文件夹,每一个子文件夹的名称就是label '''
    folders=glob(os.path.join(imgs_folder,'*'))
    imgs_paths=[]
    [imgs_paths.extend(glob(os.path.join(folder, '*.*'))) for folder in folders]
    
    face_imgs=[]
    labels=[]
    # 对每一张图片都检测画面上的人脸
    for img_path in imgs_paths:
        image = cv2.imread(img_path, 0) 
        label=os.path.split(img_path)[0]
        img_folder=os.path.split(img_path)[0]
        faces = face_cascade.detectMultiScale(image, 1.1, 2, minSize=(100,100))
        for (x, y, w, h) in faces:
            face_imgs.append(image[y:y+h, x:x+w])
            
            labels.append(os.path.split(img_folder)[1])
            # 此处有点不合理,本数据集中每张图片只有一我的脸,故而能够用这个方式,
            # 若是有多个不一样人的脸,则不能用折冲方式。
    # 将labels转换为数字
    label_encoder=LabelEncoder()
    encode_labels=label_encoder.fit_transform(labels)
    return face_imgs, encode_labels, label_encoder,labels
复制代码

测试下上面的函数是否正常,且显示下加载的脸部照片

# 测试上面函数是否正常
face_cascade=cv2.CascadeClassifier('E:\PyProjects\DataSet\FireAI\cascade_files/haarcascade_frontalface_alt.xml')
face_imgs, labels, label_encoder,labels=load_train_set('E:\PyProjects\/DataSet\FireAI\/faces_dataset/train',face_cascade)
print(len(face_imgs)) # 有53张脸,可是检测获得56个结果,显然有几张图片中检测了多张脸
# 显示任一张人脸
# 因为cv2读取的是BGR,而plt是RGB,故而须要转化一下
plt.imshow(face_imgs[3],cmap='gray')
复制代码

从打印的结果能够看出,多了三张图片,说明有三张不是脸部照片的图片混入,故而须要找出来删除。定义一个函数来找出错误图片

def find_false_faces(face_imgs):
    ''' 将全部脸部照片显示出来,若是发现有错误的,按d键,记录下错误的脸部照片 '''
    need_del_ids=[]
    for idx,face in enumerate(face_imgs):
        cv2.namedWindow('check', cv2.WINDOW_NORMAL)
        cv2.resizeWindow('check', 500, 500)
        cv2.imshow('check', face)
        key = cv2.waitKey(0)

        if key==27: # 若是输入时Esc,则退出循环
            print('esc to exit')
            break
        elif key==100: # 若是输入d键,则记录该脸对应的id
            need_del_ids.append(idx)
    cv2.destroyAllWindows()
    print('finished...')
    return need_del_ids
复制代码

故而须要从原始数据集中删除这三张图片以及对应的label信息

# 从数据集中删除这三张照片对应的信息
face_imgs=np.delete(np.array(face_imgs), need_del_ids, axis=0)
encode_labels=np.delete(np.array(encode_labels), need_del_ids, axis=0)
labels=np.delete(np.array(labels), need_del_ids,axis=0)
print(face_imgs.shape) # 53张图没错,元素已经变成了np.ndarray,故而只有行
复制代码

3. 构建LBP直方图识别器

此处的LBP直方图识别器至关于一个分类模型,cv2已经帮咱们封装好了这个分类模型,咱们只须要调用便可。

# 构建createLBPHFaceRecognizer分类模型
from cv2.face import LBPHFaceRecognizer_create
recognizer=LBPHFaceRecognizer_create()
recognizer.train(face_imgs, encode_labels) # 模型训练
复制代码

一旦人脸识别器模型训练好以后,就能够用来进行人脸识别了,下面看看识别新图片的人脸结果。

# 用训练好的模型预测新照片
def predict_imgs(new_imgs_folder, face_cascade,recognizer,label_encoder):
    ''' 用训练好的人脸识别器来识别人脸'''
    img_paths=glob(new_imgs_folder+'/*.*')
    predicted_imgs=[]
    for img_path in img_paths:
        image=cv2.imread(img_path)
        gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
        faces=face_cascade.detectMultiScale(gray,1.1, 2, minSize=(100,100))
        for (x, y, w, h) in faces:
            cv2.rectangle(image,(x,y),(x+w,y+h),(0,0,255),3)
            predicted_index, conf = recognizer.predict(gray[y:y+h, x:x+w])
            predicted_label=label_encoder.inverse_transform([predicted_index])[0]
            cv2.putText(image, predicted_label,(x,y-20), cv2.FONT_HERSHEY_SIMPLEX, 2, (0,0,255), 3)
        predicted_imgs.append(image)
    return predicted_imgs
复制代码

获得的结果分别为:

固然,这个识别器只能识别训练图片中已经有的人脸,对于训练集中没有的人脸,它会预测不许确。好比,拿凤姐的图片来预测一下试试。

估计凤姐这张照片和Person3长的比较像,因此本模型将其预测为Person3

########################小**********结###############################

1,LBP直方图模型能够快速训练并快速识别,在人脸识别领域中有着比较普遍的应用。

#################################################################


注:本部分代码已经所有上传到(个人github)上,欢迎下载。

参考资料:

1, Python机器学习经典实例,Prateek Joshi著,陶俊杰,陈小莉译

相关文章
相关标签/搜索