内容简介:(本文所使用的Python库和版本号: Python 3.6, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )在我前面的博文局部二值模式(Local Binary Pattern, 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特征图。
关于灰度不变性:很明显,原始的局部图中如果灰度值都同时增加一个值或同时减去一个值,便相当于亮度增加或减少,但此时,得到的LBP编码不变,故而称为灰度不变性。需要注意的是:该灰度不变性仅仅适用于灰度值的单调变化。如下图
上面的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著,陶俊杰,陈小莉译
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 直方图反向投影算法介绍与实现
- OpenCV灰度图像直方图算法实现
- OpenCV-图像处理(25、直方图比较)
- MySQL 8.0新特性之统计直方图
- MySQL 8.0 中统计信息直方图的尝试
- MySQL 8.0 中统计信息直方图的尝试
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Rust编程之道
张汉东 / 电子工业出版社 / 2019-1 / 128
Rust 是一门利用现代化的类型系统,有机地融合了内存管理、所有权语义和混合编程范式的编程语言。它不仅能科学地保证程序的正确性,还能保证内存安全和线程安全。同时,还有能与C/C++语言媲美的性能,以及能和动态语言媲美的开发效率。 《Rust编程之道》并非对语法内容进行简单罗列讲解,而是从四个维度深入全面且通透地介绍了Rust 语言。从设计哲学出发,探索Rust 语言的内在一致性;从源码分析入......一起来看看 《Rust编程之道》 这本书的介绍吧!