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

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

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

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


1. 局部二值模式简介

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

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

image

关于灰度不变性:很明显,原始的局部图中如果灰度值都同时增加一个值或同时减去一个值,便相当于亮度增加或减少,但此时,得到的LBP编码不变,故而称为灰度不变性。需要注意的是:该灰度不变性仅仅适用于灰度值的单调变化。如下图

image

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

image

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

image

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

image

在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
image
image
image

故而需要从原始数据集中删除这三张图片以及对应的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

得到的结果分别为:

image
image
image

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

image

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

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

1,LBP直方图模型可以快速训练并快速识别,在人脸识别领域中有着比较广泛的应用。

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


注:本部分代码已经全部上传到(我的github)上,欢迎下载。

参考资料:

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,829评论 1 331
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,603评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,846评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,600评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,780评论 3 272
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,695评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,136评论 2 293
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,862评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,453评论 0 229
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,942评论 2 233
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,347评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,790评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,293评论 3 221
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,839评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,448评论 0 181
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,564评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,623评论 2 249

推荐阅读更多精彩内容