目录

  • 目录
  • 前言
  • LBP算法概述
  • LBP算法原理
    • 原始LBP特征描述及计算方法
    • LBP特征的改进版本
      • 1 圆形LBP特征Circular LBP or Extended LBP
      • 2 旋转不变LBP特征
      • 3 Uniform Pattern LBP特征
      • 4 MB-LBP特征
    • LBPH图像的LBP特征向量
    • LBP特征的匹配与使用
      • 1 LBP特征用在目标检测中
      • 2 LBP用在人脸识别中
  • 参考链接

前言

LBP算法概述

LBP指局部二值模式,英文全称:Local Binary Pattern,是一种用来描述图像局部特征的算子,LBP特征具有灰度不变性旋转不变性等显著优点。LBP常应用于人脸识别和目标检测中,在OpenCV中有使用LBP特征进行人脸识别的接口,也有用LBP特征训练目标检测分类器的方法,OpenCV实现了LBP特征的计算,但没有提供一个单独的计算LBP特征的接口。也就是说OpenCV中使用了LBP算法,但是没有提供函数接口喽!

LBP是一种图像纹理特征提取算法,是一种局部特征,是照片分类人脸检索研究中采用较多的特征提取算法之一。在图像物体识别领域,常用的特征描述子包括:HOG、SIFT、SURT、Wavelet、Gabor、DCT等,具体选择哪种算子需要根据目标对象决定。人脸识别中,LBPGabor是效果较好的两组特征。gabor效果比LBP效果鲁棒,但是LBP运算速度快,编译在嵌入式等平台运行。一般如果条件允许,二者会进行结合,包括定义结合特征(比如LGBP,LGXP等),特征级融合和决策级融合。

特征提取是希望将资料中重要的资讯提取出来,去除杂讯的影响,以提升分类的准确率。不同的特征提取方法有不同的效果,LBP的特性是较能排除光照变化且运算速度快。LBP的核心观念是计算每个像素和周围像素间”相对”的关系。通常光照对图中的物件带来的影响是全局的,也就是说照片中的物体的明暗程度,都往同一个方向改变,可能是变亮或变暗,只是改变的幅度会因为距离光源的远近而有所不同。所以基本上局部相邻(Local)的像素间,受光照影响后数值也许会改变,但是相对大小不会改变。
图像识别在输入图像之后,先提取特征,然后进行分类。在特征提取的设计会直接影响图像辨识的好坏,不同数据所需要的特征也不尽相同,故此步骤通常是算法的瓶颈。
深度学习在训练过程中让深度神经自动从训练数据中学习最适合的特征获取方法,让算法应用更加广泛,准确率也获得大幅提升。但是深度学习模型训练时间较长,对训练资料的品质好坏即数量多寡非常敏感,以及预测计算时间较长等。

注意:一点不懂图像算法,都是大神们的解释,这里摘录下来,作为自己回顾的部分。

LBP算法原理

1.原始LBP特征描述及计算方法

原始的LBP算子定义在像素3*3的邻域内,以邻域中心像素为阈值,相邻的8个像素的灰度值与邻域中心的像素值进行比较,若周围像素大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,3*3邻域内的8个点经过比较可产生8为二进制数,将这8位二进制数依次排列形成一个二进制数字,这个二进制数字就是中心像素的LBP值,LBP值共有282^8中可能,因此LBP值有256种可能。中心像素的LBP值反映了该像素周围区域的纹理信息。
注意计算LBP特征的图像必须是灰度图,如果是彩色图,需要先转换成灰度图



- 原始LBP特征计算代码:

//原始LBP特征计算
template <typename _tp>
void getOriginLBPFeature(InputArray _src,OutputArray _dst)
{Mat src = _src.getMat();_dst.create(src.rows-2,src.cols-2,CV_8UC1);Mat dst = _dst.getMat();dst.setTo(0);for(int i=1;i<src.rows-1;i++){for(int j=1;j<src.cols-1;j++){_tp center = src.at<_tp>(i,j);unsigned char lbpCode = 0;lbpCode |= (src.at<_tp>(i-1,j-1) > center) << 7;lbpCode |= (src.at<_tp>(i-1,j  ) > center) << 6;lbpCode |= (src.at<_tp>(i-1,j+1) > center) << 5;lbpCode |= (src.at<_tp>(i  ,j+1) > center) << 4;lbpCode |= (src.at<_tp>(i+1,j+1) > center) << 3;lbpCode |= (src.at<_tp>(i+1,j  ) > center) << 2;lbpCode |= (src.at<_tp>(i+1,j-1) > center) << 1;lbpCode |= (src.at<_tp>(i  ,j-1) > center) << 0;dst.at<uchar>(i-1,j-1) = lbpCode;}}
}

在这份代码中,dst的数组大小是(src.rows-2,src.cols-2),这是因为src的外边缘像素无法计算8位的LBP值,也就是说出去外边缘像素的才能计算8位LBP值,而8位LBP值实际就是282^8中表示,也就是256,转化为灰度值图像的话,就是一幅大小为(src.rows-2,src.cols-2)的灰度图。

2.LBP特征的改进版本

在原始的LBP特征提出后,研究人员对LBP特征进行了很多改进,因此产生了许多LBP的改进版本。

2.1 圆形LBP特征(Circular LBP or Extended LBP)

由于原始LBP特征使用的是固定邻域内的灰度值,因此当图像的尺度(尺度是什么?)发生变化时,LBP特征的编码将会发生错误,LBP特征将不能正确返回像素点周围的纹理信息,因此研究人员对其进行了改进。基本的LBP算子的最大缺陷在于它只覆盖一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的需要(比较笨,知道的概念太少,不理解。。。)。为了适应不同尺度的纹理特征,并达到灰度和旋转不变性的要求,Ojala 等对 LBP 算子进行了改进,将 3×3 邻域扩展到任意邻域并用圆形邻域代替了正方形邻域,改进后的 LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点。从而得到了诸如半径为R的圆形区域内含有P个采样点的LBP算子:

这副图像中,第一幅图像的圆半径认为1,采样的点为8个;第二幅图像的圆半径为2,采样的点为16个;第三副图像的圆半径为2,采样的点为8个。
这种LBP特征叫做Extended LBP,也叫Circular LBP。使用可变半径的圆对近邻像素进行编码,可以得到如下的近邻:

对于给定中心点(xc,yc)(x_c,y_c),其邻域像素位置为(xp,yp)(x_p,y_p),p∈Pp \in P,其采样点(xp,yp)(x_p,y_p)用如下公式计算:

R是采样半径,p是第p个采样点,P是采样数目。由于计算的值可能不是整数,即计算的点不再图像上,我们使用计算出来的点的插值点。OpenCV使用的是双线性插值(讲真,数学太差),双线性插值如下:

参考计算机视觉学习初识LBP算法

通过LBP特征的定义,LBP特征对光照变化是鲁棒的,效果如图:

//圆形LBP特征计算,这种方法适于理解,但在效率上存在问题,声明时默认neighbors=8
template <typename _tp>
void getCircularLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors)
{Mat src = _src.getMat();//LBP特征图像的行数和列数的计算要准确_dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);Mat dst = _dst.getMat();dst.setTo(0);//循环处理每个像素for(int i=radius;i<src.rows-radius;i++){for(int j=radius;j<src.cols-radius;j++){//获得中心像素点的灰度值_tp center = src.at<_tp>(i,j);unsigned char lbpCode = 0;for(int k=0;k<neighbors;k++){//根据公式计算第k个采样点的坐标,这个地方可以优化,不必每次都进行计算radius*cos,radius*sinfloat x = i + static_cast<float>(radius * \cos(2.0 * CV_PI * k / neighbors));float y = j - static_cast<float>(radius * \sin(2.0 * CV_PI * k / neighbors));//根据取整结果进行双线性插值,得到第k个采样点的灰度值//1.分别对x,y进行上下取整int x1 = static_cast<int>(floor(x));int x2 = static_cast<int>(ceil(x));int y1 = static_cast<int>(floor(y));int y2 = static_cast<int>(ceil(y));//2.计算四个点(x1,y1),(x1,y2),(x2,y1),(x2,y2)的权重//下面的权重计算方式有个问题,如果四个点都相等,则权重全为0,计算出来的插值为0//float w1 = (x2-x)*(y2-y); //(x1,y1)//float w2 = (x2-x)*(y-y1); //(x1,y2)//float w3 = (x-x1)*(y2-y); //(x2,y1)//float w4 = (x-x1)*(y-y1); //(x2,y2)//将坐标映射到0-1之间float tx = x - x1;float ty = y - y1;//根据0-1之间的x,y的权重计算公式计算权重float w1 = (1-tx) * (1-ty);float w2 =    tx  * (1-ty);float w3 = (1-tx) *    ty;float w4 =    tx  *    ty;//3.根据双线性插值公式计算第k个采样点的灰度值float neighbor = src.at<_tp>(x1,y1) * w1 + src.at<_tp>(x1,y2) *w2 \+ src.at<_tp>(x2,y1) * w3 +src.at<_tp>(x2,y2) *w4;//通过比较获得LBP值,并按顺序排列起来lbpCode |= (neighbor>center) <<(neighbors-k-1);}dst.at<uchar>(i-radius,j-radius) = lbpCode;}}
}
//圆形LBP特征计算,效率优化版本,声明时默认neighbors=8
template <typename _tp>
void getCircularLBPFeatureOptimization(InputArray _src,OutputArray _dst,int radius,int neighbors)
{Mat src = _src.getMat();//LBP特征图像的行数和列数的计算要准确_dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);Mat dst = _dst.getMat();dst.setTo(0);for(int k=0;k<neighbors;k++){//计算采样点对于中心点坐标的偏移量rx,ryfloat rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));//为双线性插值做准备//对采样点偏移量分别进行上下取整int x1 = static_cast<int>(floor(rx));int x2 = static_cast<int>(ceil(rx));int y1 = static_cast<int>(floor(ry));int y2 = static_cast<int>(ceil(ry));//将坐标偏移量映射到0-1之间float tx = rx - x1;float ty = ry - y1;//根据0-1之间的x,y的权重计算公式计算权重,权重与坐标具体位置无关,与坐标间的差值有关float w1 = (1-tx) * (1-ty);float w2 =    tx  * (1-ty);float w3 = (1-tx) *    ty;float w4 =    tx  *    ty;//循环处理每个像素for(int i=radius;i<src.rows-radius;i++){for(int j=radius;j<src.cols-radius;j++){//获得中心像素点的灰度值_tp center = src.at<_tp>(i,j);//根据双线性插值公式计算第k个采样点的灰度值float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2 \+ src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4;//LBP特征图像的每个邻居的LBP值累加,累加通过与操作完成,对应的LBP值通过移位取得dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1);}}}
}

因为不太理解C++的模板这些东西,对LBP特征原理及代码实现的博主所使用的高级技术表示佩服,果然还有许多许多需要学习的东西。但是大概过程:
1. 根据半径的大小,设置LBP特征图像的行数和列数
2. 根据neighborsradius计算采样点,因为采样点的坐标可能不是整数,需要使用双线性插值来确定采样点
3. 中心点和采样点进行比较,得到对应中心点的LBP值,然后记录到目标数组中,最后得到的就是LBP值的灰度图了。

测试结果:

第一幅图是原图;
第二幅图是原始的LBP算法得到的LBP特征图;
第三幅图是radius = 3,neighbors = 8CircularLBP特征图;
第四幅图是radius = 1,neighbors = 8CircularLBP特征图。
实验结果:半径越小,图像纹理越精细。

第一幅图是原图;
第二幅图是原始的LBP算法得到的LBP特征图;
第三幅图是radius = 3,neighbors = 8CircularLBP特征图;
第四幅图是radius = 3,neighbors = 4CircularLBP特征图。
实验结果:刚开始以为第四幅图是黑的,但是仔细看还有有轮廓的,也就是相同半径下,领域数目越小,图像亮度越低,想想242^4之后16种灰度值,当然会显得很暗,区分度也会很差。

2.2 旋转不变LBP特征

上面的LBP特征具有灰度不变性,但是还不具备旋转不变性,因此研究人员又在上面的基础上进行了扩展,提出了具有旋转不变性的LBP。
首先,不断的旋转圆形邻域内的LBP特征,根据选择得到一系列的LBP特征值,从这些LBP特征值选择LBP特征值最小的作为中心像素点的LBP特征
如图,通过对得到的LBP特征进行旋转得到一系列的LBP特征值,最终将特征值最小的一个特征模式作为中心像素点的LBP特征。

//旋转不变圆形LBP特征计算,声明时默认neighbors=8
template <typename _tp>
void getRotationInvariantLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors)
{Mat src = _src.getMat();//LBP特征图像的行数和列数的计算要准确_dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);Mat dst = _dst.getMat();dst.setTo(0);for(int k=0;k<neighbors;k++){//计算采样点对于中心点坐标的偏移量rx,ryfloat rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));//为双线性插值做准备//对采样点偏移量分别进行上下取整int x1 = static_cast<int>(floor(rx));int x2 = static_cast<int>(ceil(rx));int y1 = static_cast<int>(floor(ry));int y2 = static_cast<int>(ceil(ry));//将坐标偏移量映射到0-1之间float tx = rx - x1;float ty = ry - y1;//根据0-1之间的x,y的权重计算公式计算权重,权重与坐标具体位置无关,与坐标间的差值有关float w1 = (1-tx) * (1-ty);float w2 =    tx  * (1-ty);float w3 = (1-tx) *    ty;float w4 =    tx  *    ty;//循环处理每个像素for(int i=radius;i<src.rows-radius;i++){for(int j=radius;j<src.cols-radius;j++){//获得中心像素点的灰度值_tp center = src.at<_tp>(i,j);//根据双线性插值公式计算第k个采样点的灰度值float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2 \+ src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4;//LBP特征图像的每个邻居的LBP值累加,累加通过与操作完成,对应的LBP值通过移位取得dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1);}}}//进行旋转不变处理for(int i=0;i<dst.rows;i++){for(int j=0;j<dst.cols;j++){unsigned char currentValue = dst.at<uchar>(i,j);unsigned char minValue = currentValue;for(int k=1;k<neighbors;k++){//循环左移unsigned char temp = (currentValue>>(neighbors-k)) | (currentValue<<k);if(temp < minValue){minValue = temp;}}dst.at<uchar>(i,j) = minValue;}}
}

测试结果:

第一幅图是原图;
第二幅图是原始的LBP算法得到的LBP特征图;
第三幅图是radius = 3,neighbors = 8CircularLBP特征图;
第四幅图是radius = 3,neighbors = 8的具有旋转不变性CircularLBP特征图。
过程就是:
1. 根据半径的大小,设置LBP特征图像的行数和列数
2. 根据neighborsradius计算采样点,因为采样点的坐标可能不是整数,需要使用双线性插值来确定采样点
3. 中心点和采样点进行比较,得到对应中心点的LBP值。
4. 中心点对应的邻域进行旋转,可以获得相对于中心点的最小的LBP值,这样作为图像的LBP特征。不管如何旋转,LBP值都是恒定的,也就是具有了旋转不变性。
总结来说,旋转不变性的LBP特征图亮度应该比较低,因为选取的是最小的LBP值

2.3 Uniform Pattern LBP特征

2.4 MB-LBP特征

3. LBPH——图像的LBP特征向量

4. LBP特征的匹配与使用

4.1 LBP特征用在目标检测中

人脸检测比较出名的是Haar+Adaboost方法,目前Opencv中也支持LBP+AdaboostHOG+Adaboost方法进行目标检测,据SnailTyan大神说,LBP+Adaboost方法用在目标检测中的效果比Haar特征、HOG特征都要好,而且LBP特征的训练速度比Haar和HOG都要快很多。(虽然并不知道Haar、HOG、Adaboost是什么。。。)

4.2 LBP用在人脸识别中

据大神说,人脸识别中LBPH的使用主要是用来进行直方图的比较,通过直方图的比较来判断目标的类别。在OpenCV中基于LBP的人脸识别的实现中使用的LBP特征是Extendes LBP,即圆形LBP特征。

参考链接

LBP特征原理及代码实现
LBP (Local Binary Pattern) 是目前流行的模式识别、人脸识别算法吗?
计算机视觉学习初识LBP算法

OpenCV之LBP算法学习相关推荐

  1. opencv学习之(三)-LBP算法的研究及其实现

    一,原始LBP算法 LBP的基本思想是对图像的像素和它局部周围像素进行对比后的结果进行求和.把这个像素作为中心,对相邻像素进行阈值比较.如果中心像素的亮度大于等于他的相邻像素,把他标记为1,否则标记为 ...

  2. LBP算法的研究及其实现

    原文:http://blog.csdn.net/dujian996099665/article/details/8886576 一,原始LBP算法 LBP的基本思想是对图像的像素和它局部周围像素进行对 ...

  3. OpenCV中基于LBP算法的人脸检测测试代码

    下面是OpenCV 3.3中基于CascadeClassifier类的LBP算法实现的人脸检测,从结果上看,不如其它开源库效果好,如libfacedetection,可参考 https://blog. ...

  4. python opencv 人脸比对_Python3.5+openCv在Windows下利用LBP算法进行人脸识别并匹配

    之前的人脸识别匹配需要大量图片进行建模,然后通过概率匹配,结果不是很准确,同时也不符合一般需求.一般需求是人员通过摄像头拍摄一张照片,然后将照片保存进行命名,之后如果再次通过摄像头进行验证时候,通过算 ...

  5. Opencv学习笔记 - OpenCV 4机器学习算法简介

    在机器学习中,一些比较流行方法的包括:支持向量机(SVM).人工神经网络(ANN).聚类.k-最近邻.决策树和深度学习.OpenCV支持并实现几乎所有这些方法,并有详细的文档说明(包含在Main mo ...

  6. lm opencv 算法_Levenberg–Marquardt算法学习(和matlab的LM算法对比)

    回顾高斯牛顿算法,引入LM算法 惩罚因子的计算(迭代步子的计算) 完整的算法流程及代码样例 1.      回顾高斯牛顿,引入LM算法 根据之前的博文:Gauss-Newton算法学习 假设我们研究如 ...

  7. 利用OpenCV实现基于深度学习的超分辨率处理

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 OpenCV是一个非常强大的计算机视觉处理的工具库.很多小伙伴在入 ...

  8. 在OpenCV中基于深度学习的边缘检测

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自:AI算法与图像处理 导读 分析了Canny的优劣,并给出 ...

  9. pythonopencv算法_python opencv之分水岭算法示例

    本文介绍了python opencv之分水岭算法示例,分享给大家,具体如下: 目标 使用分水岭算法对基于标记的图像进行分割 使用函数cv2.watershed() 原理: 灰度图像可以被看成拓扑平面, ...

  10. Surf算法学习心得(一)——算法原理

    Surf算法学习心得(一)--算法原理 写在前面的话: Surf算法是对Sift算法的一种改进,主要是在算法的执行效率上,比Sift算法来讲运行更快!由于我也是初学者,刚刚才开始研究这个算法,然而网上 ...

最新文章

  1. 蓝桥杯 基础练习 十进制转十六进制(水题,进制转换)
  2. maven学习(六)——在别的项目中引用通过Maven安装生成的项目的jar包
  3. jq修改导航栏样式(选中、使用两张图片替代的是否选中效果)
  4. baidu aistudio使用小结
  5. 03-instancing 工程分析详解
  6. Net窗体程序设计总结
  7. ad域推送软件_ManageEngine ADManager Plus(AD域管理工具) V7.0.1 官方中文版
  8. mp4安装Linux,linux centos mp4box 安装教程
  9. 偏振融合伪彩色图像(原理)
  10. bat编程和vbs编程入门
  11. Python数据可视化之随机点图
  12. VMWare:vSphere6 企业版参考序列号
  13. 移动硬盘无法识别解决办法
  14. 深入分析:代理游戏真的可以赚钱吗?
  15. 【电路收藏夹】AMS1117稳压电路
  16. vue中字典值存在多个进行反写
  17. php创建扑克牌,利用php模拟分发扑克牌(无聊之作)
  18. 某些Office 精简版 Win7运行问题(Windows7 不支持 Thinstall)
  19. Jenkins+Maven+Git自动部署流程(从环境搭建到部署超级全面)
  20. 8052单片机定时计数器T2的使用

热门文章

  1. tengine2.2.3报错502的The proxy server received an invalid response from an upstream server问题处理...
  2. alisql mysql_AliSQL 5.6.32 vs MySQL 5.7.15抢鲜测试
  3. [Cocos Creator] 制作简版消消乐(四):实现消除算法
  4. echarts地图map下钻到镇街、KMZ文件转GeoJson、合成自定义区域
  5. linux合并pdf命令,LINUX下合并PDF
  6. 2022张宇考研基础30讲 第九讲 一元函数积分学的几何应用
  7. 安装Oracle驱动ojdbc8到本地maven仓库
  8. jQuery EasyUI/TopJUI实现数据表格的增删改查功能(不写js,纯HTML实现!!!)
  9. 大华平台显示归属服务器离线,大华报警联网系统方案
  10. 800道Python习题,花了一个月终于整理出来了,挑战一下自己能做对多少题