局部二值模式(LBP)

来源:互联网 发布:逍遥模拟器没网络 编辑:程序博客网 时间:2024/05/29 14:56

简要介绍

LBP(Local Binary Pattern,局部二值模式)是一种用来描述图像局部纹理特征的算子,具有旋转不变性和灰度不变性等显著优点。首先由T. Ojala, M.Pietikäinen, 和D. Harwood 在1994年提出,用于纹理特征提取。主要应用于人脸识别和目标检测中,OpenCV中有使用LBP特征进行人脸识别的接口,也有用LBP特征训练目标检测分类器的方法,Opencv实现了LBP特征的计算,但没有提供一个单独的计算LBP特征的接口。

原始LBP

3×3 的窗口中,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。可以产生8位二进制数,将其转化为十进制数便得到了LBP编码(256种),如下图左上角开始遍历组成2进制数,转化为10进制后为124。



具体的,其数学表达可写作:

LBP(xc,yc)=p=0p12ps(ipic)

其中,(xc,yc)为中心像素的坐标,p为邻域的第p个像素,ip为邻域像素的灰度值,ic为中心像素的灰度值,s(x)为符号函数,如下:
s(x)={1,0,x>=0x<0

代码实现如下:

void orLBP(Mat& src, Mat &dst){    // 循环处理图像数据    for (int i = 1; i < src.rows - 1; i++)    {        for (int j = 1; j < src.cols - 1; j++)        {            uchar center = src.at<uchar>(i, j);            unsigned char lbpCode = 0;            lbpCode |= (src.at<uchar>(i - 1, j - 1) > center) << 7;            lbpCode |= (src.at<uchar>(i - 1, j) > center) << 6;            lbpCode |= (src.at<uchar>(i - 1, j + 1) > center) << 5;            lbpCode |= (src.at<uchar>(i, j + 1) > center) << 4;            lbpCode |= (src.at<uchar>(i + 1, j + 1) > center) << 3;            lbpCode |= (src.at<uchar>(i + 1, j) > center) << 2;            lbpCode |= (src.at<uchar>(i + 1, j - 1) > center) << 1;            lbpCode |= (src.at<uchar>(i, j - 1) > center) << 0;            dst.at<uchar>(i - 1, j - 1) = lbpCode;        }    }}int main(){    int radius = 1;    Mat img = cv::imread("C:/Users/Administrator/Desktop/aa.png", 0);    imshow("原始图像", img);    //创建一个LBP,注意为了防止溢出,我们行列都在原有图像上减去2个半径    Mat dst = Mat(img.rows - 2 * radius , img.cols - 2 * radius , CV_8UC1, Scalar(0));    orLBP(img, dst);     imshow("原始LBP", dst);    cv::waitKey(0);}

不足:只覆盖了一个固定半径范围内的小区域,不能满足不同尺寸和频率纹理的需要。

圆形LBP

为适应不同尺度的纹理特征,并达到灰度和旋转不变性的要求,Ojala等做出了改进,将 3×3邻域扩展到任意邻域,并用圆形邻域代替正方形邻域,改进后的 LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点。从而得到了诸如半径为R的圆形区域内含有P个采样点的LBP算子。


这里写图片描述

其中,LBPrp表示在以r为半径的圆形领域内,含有p个采样点的LBP局部编码。这种LBP特征叫做Extended LBP,也叫Circular LBP,使用可变半径的圆对近邻像素进行编码,可以得到诸如下面的近邻描述(Spot / Line / Corner / Edge ) 。

这里写图片描述

要比较中心像素与周围领域的大小,首先要知道领域内点的坐标在哪吧,那周围这些点怎么确定呢?对于给定中心点(xc,yc),其邻域像素位置为(xp,yp)pP,其采样点(xp,yp)用如下公式计算:


这里写图片描述

xp=xc+Rcos(2pπP)yp=ycRsin(2pπP)

R是采样半径,p是第p个采样点,P是采样数目。


这里写图片描述

糟糕的是,我们将计算的坐标位置局部放大来看,并假设计算出的位置周围的图像坐标分别是从(0,0)(1,1),我们计算出来的坐标并不一定是整数,即计算出来的点不在图像上,怎么确定他的像素值呢?或者说他的像素值取多少比较合理?这种情况下,我们一般采用插值计算算出它的合理的像素值,目前的插值方法有很多,Opencv使用的是双线性插值,双线性插值的公式如下:

f(x,y)[1xx][f(0,0)f(1,0)f(0,1)f(1,1)][1yy]

或写作权重的形式如下:
f(P)=f(x,y)f(0,0)(1x)(1y)+f(1,0)x(1y)+f(0,1)(1x)y+f(1,1)xy

对该公式不理解的可以参考我的博客(双线性插值)。这里通过一个偏移,可以选择一个坐标系统使得f的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),P(x,y)是需要插值的点。


这里写图片描述

因此,按照上述分析,在C++中圆形LBP实现代码如下:

void elbp(Mat& src, Mat &dst, int radius, int neighbors){    for (int n = 0; n < neighbors; n++)    {        // 采样点的计算        float x = static_cast<float>(-radius * sin(2.0*CV_PI*n / static_cast<float>(neighbors)));        float y = static_cast<float>(radius * cos(2.0*CV_PI*n / static_cast<float>(neighbors)));        // 上取整和下取整的值        int fx = static_cast<int>(floor(x));        int fy = static_cast<int>(floor(y));        int cx = static_cast<int>(ceil(x));        int cy = static_cast<int>(ceil(y));        // 小数部分        float ty = y - fy;        float tx = x - fx;        // 设置插值权重        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++)            {                // 计算插值                float t = static_cast<float>(w1*src.at<uchar>(i + fy, j + fx) + w2*src.at<uchar>(i + fy, j + cx) + w3*src.at<uchar>(i + cy, j + fx) + w4*src.at<uchar>(i + cy, j + cx));                // 进行编码                dst.at<uchar>(i - radius, j - radius) += ((t > src.at<uchar>(i, j)) || (std::abs(t - src.at<uchar>(i, j)) < std::numeric_limits<float>::epsilon())) << n;            }        }    }}

旋转不变LBP

上述LBP特征已具有灰度、光照不变性,但还不具备旋转不变性,为了实现旋转不变性,研究人员又提出了具有旋转不变性的LBP特征。 其主要思想大致是:首先不断的旋转圆形邻域内的LBP特征,根据选择得到一系列的LBP特征值,从这些LBP特征值选择LBP特征值最小的作为中心像素点的LBP特征。具体做法如下图所示:


这里写图片描述

如图,通过对得到的LBP特征进行旋转,得到一系列的LBP特征值,最终将特征值最小的一个特征模式作为中心像素点的LBP特征。其实现的关键代码如下:

//进行旋转不变处理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;    }}

等价模式(Uniform Pattern)

上述LBP算子可以产生各种不同的二进制模式,对于半径为R圆形区域内含有P个采样点的LBP算子将会产生2P种模式。很显然,随着邻域集内采样点数的增加,二进制模式的种类是急剧增加的。例如:5×5邻域内20个采样点,有2201,048,576种二进制模式。如此多的模式无论对于纹理提取还是对于纹理识别、分类及信息存取都是不利的。同时,过多的模式种类对于纹理的表达是不利的。例如,将LBP算子用于纹理分类或人脸识别时,常采用LBP模式的统计直方图来表达图像的信息,而较多的模式种类将使得数据量过大,且直方图过于稀疏。因此,需要对原始的LBP模式进行降维,使得数据量减少的情况下能最好的代表图像的信息。

那么如何解决二进制模式过多的问题,对LBP算子进行降维呢?Ojala采用一种“等价模式”(Uniform Pattern)来对LBP算子进行降维。他认为在实际图像中,绝大多数LBP模式最多只包含两次从1到0或从0到1的跳变。因此,Ojala将“等价模式”定义为:

当某个LBP所对应的循环二进制数从0到1或从1到0最多有两次跳变时,该LBP所对应的二进制就称为一个等价模式类。


这里写图片描述

如上述左边,0次跳变和2次跳变为等价模式,除此之外其他模式的都是混合模式。那这种LBP模式与传统的LBP有何区别和联系呢?采样点数目为8个,即LBP特征值有28种,共256个值,正好对应灰度图像的0-255,因此原始的LBP特征图像是一幅正常的灰度图像,而等价模式LBP特征,根据0-1跳变次数,将这256个LBP特征值分为了59类,从跳变次数上划分如下表所示(需要注意的是不可能出现奇数次的跳变,因为有一次01,就相应的应该有一次10,因为它是循环的。):

跳变次数 模式个数 跳变次数 模式个数 0 2 1 0 2 56 3 0 4 140 5 0 6 56 7 0 8 2

共9种跳变情况,将这256个值进行分配,跳变小于2次的为等价模式类,共58个,他们对应的值按照从小到大分别编码为1—58,即它们在LBP特征图像中的灰度值为1—58,而除了等价模式类之外的混合模式类被编码为0,即它们在LBP特征中的灰度值为0,因此等价模式LBP特征图像整体偏暗。

好了,现在通过这样的改进,二进制模式的种类大大减少,而不会丢失任何信息。模式数量由原来的2P种减少为P(P1)+2种,其中P表示邻域集内的采样点数。对于3×3邻域内8个采样点来说,二进制模式由原始的256种减少为58种,即:它把值分为59类,58个uniform pattern为一类,其它的所有值为第59类。这样直方图从原来的256维变成59维。这使得特征向量的维数更少,并且可以减少高频噪声带来的影响。 现在解释下:

模式数量:2PP(P1)+3

很显然,在上面的图中叙述的很清楚,等价模式中0次跳变共两种,还要加一个混合模式,这是为什么在后面加3的原因,那么P(P1)呢,且看下图,


这里写图片描述

在2次跳变中,紫色的数字表示跳变的位置,假设有P个采样点,首先从P个位置中选一个01的跳变,即有C1P种选择,紧接着在剩下的P-1个位置中选一个位置进行10的跳变,即有C1P1,一共有多少种情况呢?

C1PC1P1=P(P1)

因此,一共有P(P1)+2种等价模式。

其他LBP

...

LBP统计直方图(LBPH)

LBP特征的统计直方图(Local Binary Patterns Histograms,LBPH)将LBP特征与图像的空间信息结合在一起。将LBP特征图像分成m个局部块,并提取每个局部块的直方图,然后将这些直方图依次连接在一起形成LBP特征的统计直方图,即LBPH。 一幅图像具体的计算LBPH的过程(以Opencv中的人脸识别为例):

  1. 计算图像的LBP特征图像,在上面已经讲过了。
  2. 将LBP特征图像进行分块,Opencv中默认将LBP特征图像分成8行8列64块区域
  3. 计算每块区域特征图像的直方图cell_LBPH,将直方图进行归一化,直方图大小为1*numPatterns
  4. 将上面计算的每块区域特征图像的直方图按分块的空间顺序依次排列成一行,形成LBP特征向量,大小为1*(numPatterns*64)
  5. 用机器学习的方法对LBP特征向量进行训练,用来检测和识别目标

举例说明LBPH的维度:


这里写图片描述

采样点为8个,如果用的是原始的LBP或Extended LBP特征,其LBP特征值的模式为256种,则一幅图像的LBP特征向量维度为:64*256=16384维, 而如果使用的UniformPatternLBP特征,其LBP值的模式为59种,其特征向量维度为:64*59=3776维,可以看出,使用等价模式特征,其特征向量的维度大大减少, 这意味着使用机器学习方法进行学习的时间将大大减少,而性能上没有受到很大影响。

...

参考

LBP特征原理及代码实现

原创粉丝点击