图像特征提取之LBP特征
原文站点:https://senitco.github.io/2017/06/12/image-feature-lbp/
该算子用于提取图像纹理特征的关键信息(Binary Pattern Local Binary Pattern, BPLB, LBP)。其主要优点在于具有灰度不变性和旋转不变性等显著特点(主要优点)。具体而言,在对图像进行分析时(关键步骤),该方法通过比较每个像素与其邻域区域中的像素值差异(核心操作),将这些差异信息转化为二进制序列形式(核心输出),并以该序列作为中心像素对应的特征码(最终结果)。这种表征方式不仅能够有效捕捉到像素间的相对关系模式(重要特性),而且还可以通过简单的计算过程实现对图像局部区域的独特特征信息的有效提取(核心功能)。基于此特点,在实际应用中该算法特别适合于那些对计算效率要求较高的场景(典型应用),如目标检测、人脸识别等实例说明(具体案例)
原始LBP特征
原始LBP算子定义在图像上采用3×3邻域区域,在该区域内设定中心像素的亮度值作为判定点基准,并对周边8个像素进行亮度对比操作:若相邻像素亮度高于中心基准,则对应位标记为1;反之则标记为0。经过对周边8个像素执行上述比较操作后会生成一个8位二进制码序列;将此二进制序列按顺序排列即可获得中心像素对应的LBP特征码值。该特征码数值共有2^8=256种可能取值范围与常规灰度图像具有相似特性因而可通过灰度图形式直观展示这些特征码信息:由于原始LBP特征主要反映纹理细节而不涉及色彩信息因此对于彩色图像必须先将其转换为灰度图形式再进行特征提取工作;如图所示即为基于LBP算法的具体提取流程

公式定义如下:
其中 (x_c,y_c)表示邻域窗口内的中心像素点坐标系中的位置及其对应的像素值为 i_c;而 i_p则表示该邻域窗口内所有其他像素点对应的像素值;此外,在计算过程中我们使用了符号函数 s(x)来进行特定的操作或转换步骤。
原始LBP特征的实现代码(OpenCV)如下:
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;
}
}
}
圆形LBP特征(Circular LBP or Extended LBP)
该方法主要关注在固定半径范围内相邻像素的情况,在处理不同尺寸和纹理频率时存在明显局限性。当图像尺寸发生变化时,该方法无法准确反映局部区域内的纹理特征信息,进而影响其表现效果。针对这一问题,Ojala等人提出了一种改进方案,他们将邻域窗口设计成任意形状,并采用圆形窗口替代传统的正方形窗口,从而实现了更大程度上的灵活性与适应性,这样就能在半径R范围内实现P个采样点的LBP特征提取

采样点的坐标可通过以下公式计算:
以某特定区域中心像素点(x_p, y_p)作为参考基准,在其邻域内选取某一特定位置p\in P作为研究对象;基于上一阶段的工作成果(即能够计算任意数量样本的空间位置),然而这些位置坐标的数值结果往往不具备整数特性;因此为了获得该采样点处的有效像素值信息,则采用双线性插值方法进行数据重构。
圆形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*sin
float 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特征的效率优化版本:
//圆形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,ry
float 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);
}
}
}
}
旋转不变LBP特征(Rotation Invariant LBP)
无论是原始型还是圆形型两种类型的LBP算子,在设计上都仅考虑了灰度信息而不具备方向特性(即无法保持方向特异性),因此无法满足对经过旋转变换后的图像产生一致描述的需求。为了克服这一缺陷,在后续研究中又提出了基于改进型的方向性增强策略——能够保持方向特异性的改进方案(Rotation Invariant LBP)。具体而言就是通过不断改变圆周上取样点的位置(即从各个起始点依次遍历取样位置),按顺时针顺序逐一访问每个取样位置,并计算出P个编码值(其中P表示取样的总数),将这些编码值中的最小者定义为该区域中心像素对应的LBPH特征码。

旋转不变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,ry
float 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等价模式(Uniform LBP)
在半径为R的圆形区域内包含有P个邻域采样点,则LBP算子可能产生2^P种模式。当采样点数量增加导致LBP值种类呈指数级扩展趋势,在实际应用中发现大量二进制模式可能导致纹理特征提取效率低下,并且可能降低分类精度。为此提出了一种改进方案以减少二进制模式的数量。

在具体实施过程中, 等价模式类别按照自增数值顺序进行编码, 而混合模式类则采用零作为编码基数, 这导致生成的LBP特征图呈现出整体偏低的亮度. LBP等价模式的具体实现代码如下:
//等价模式LBP特征计算
template <typename _tp>
void getUniformPatternLBPFeature(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);
//LBP特征值对应图像灰度编码表,直接默认采样点为8位
uchar temp = 1;
uchar table[256] = {0};
for(int i=0;i<256;i++)
{
if(getHopTimes(i)<3)
{
table[i] = temp;
temp++;
}
}
//是否进行UniformPattern编码的标志
bool flag = false;
//计算LBP特征图
for(int k=0;k<neighbors;k++)
{
if(k==neighbors-1)
{
flag = true;
}
//计算采样点对于中心点坐标的偏移量rx,ry
float 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);
//进行LBP特征的UniformPattern编码
if(flag)
{
dst.at<uchar>(i-radius,j-radius) = table[dst.at<uchar>(i-radius,j-radius)];
}
}
}
}
}
//计算跳变次数
int getHopTimes(int n)
{
int count = 0;
bitset<8> binaryCode = n;
for(int i=0;i<8;i++)
{
if(binaryCode[i] != binaryCode[(i+1)%8])
{
count++;
}
}
return count;
}
此外,在采用旋转对称性的前提下,该Uniform LBP算子能够生成P+1种不同的等价模式类型;当采用8个采样点时,在这种情况下仅产生9种独特的编码结果。这一特性适用于如前所述的Uniform LBP示例图中
多尺度LBP(Multiscale Block LBP)
基本LBP算子通过捕获每个像素及其周边区域所携带的纹理细节来描述图像特性,并归类于细粒度特征描述范畴。研究团队在此基础上开发了多尺度LBP方法,在图像处理中将其划分为多个块状区域(block),并进一步细分这些区域为若干个小连通单元(cell)结构体系。这种设计与HOG( Histogram of Oriented Gradients )特征具有相似性,在每个cell内部计算其灰度均值或总和作为该cell的标准基准值,并通过与邻域单元对比构建局部二进制模式(Locally Binary Patterns, LBP)序列以提取独特描述符。其中设定block尺寸参数未给出具体数值(需配合上下文补充完整),而当block大小与cell大小设定为1相匹配时,则还原至传统LBP特征配置模式;如图所示的具体实现中block尺寸设定为9 \times 9结构框架,默认情况下细胞划分尺寸设置为空缺状态待后续参数配置确定

MB-LBP特征的实现代码如下:
//MB-LBP特征的计算
void getMultiScaleBlockLBPFeature(InputArray _src,OutputArray _dst,int scale)
{
Mat src = _src.getMat();
Mat dst = _dst.getMat();
//定义并计算积分图像
int cellSize = scale / 3;
int offset = cellSize / 2;
Mat cellImage(src.rows-2*offset,src.cols-2*offset,CV_8UC1);
for(int i=offset;i<src.rows-offset;i++)
{
for(int j=offset;j<src.cols-offset;j++)
{
int temp = 0;
for(int m=-offset;m<offset+1;m++)
{
for(int n=-offset;n<offset+1;n++)
{
temp += src.at<uchar>(i+n,j+m);
}
}
temp /= (cellSize*cellSize);
cellImage.at<uchar>(i-cellSize/2,j-cellSize/2) = uchar(temp);
}
}
getOriginLBPFeature<uchar>(cellImage,dst);
}
多尺度模式下同样用到了降维,论文中是直接采样统计的方法对不同尺度的LBP算子的模式进行统计,选取占比例较高的模式,而不是利用跳变规则。具体来说,就是将得到的MB-LBP特征计算统计直方图,通过对bin中的数值进行排序以及权衡,将排序在前N(63)位的特征值看作是等价模式类,其余的为混合模式类,总共为N+1类,论文中称之为(SEMB-LBP, Statistically Effective MB-LBP)。
SEMB-LBP的实现代码如下:
//求SEMB-LBP
void SEMB_LBPFeature(InputArray _src,OutputArray _dst,int scale)
{
Mat dst=_dst.getMat();
Mat MB_LBPImage;
getMultiScaleBlockLBPFeature(_src,MB_LBPImage,scale);
//imshow("dst",dst);
Mat histMat;
int histSize = 256;
float range[] = {float(0),float(255)};
const float* ranges = {range};
//计算LBP特征值0-255的直方图
calcHist(&MB_LBPImage,1,0,Mat(),histMat,1,&histSize,&ranges,true,false);
histMat.reshape(1,1);
vector<float> histVector(histMat.rows*histMat.cols);
uchar table[256];
memset(table,64,256);
if(histMat.isContinuous())
{
//histVector = (int *)(histMat.data);
//将直方图histMat变为vector向量histVector
histVector.assign((float*)histMat.datastart,(float*)histMat.dataend);
vector<float> histVectorCopy(histVector);
//对histVector进行排序,即对LBP特征值的数量进行排序,降序排列
sort(histVector.begin(),histVector.end(),greater<float>());
for(int i=0;i<63;i++)
{
for(int j=0;j<histVectorCopy.size();j++)
{
if(histVectorCopy[j]==histVector[i])
{
//得到类似于Uniform的编码表
table[j]=i;
}
}
}
}
dst = MB_LBPImage;
//根据编码表得到SEMB-LBP
for(int i=0;i<dst.rows;i++)
{
for(int j=0;j<dst.cols;j++)
{
dst.at<uchar>(i,j) = table[dst.at<uchar>(i,j)];
}
}
}
图像的LBP特征向量(Local Binary Patterns Histograms)
对图像中的每个像素求取其LBP(Local Binary Patterns)特征值后能够生成完整的图像LBP特图谱。然而,在实际应用中通常不会直接将该特图谱作为单一的分类器或识别器使用。相反地,在本研究中我们借鉴HOG( Histogram of Oriented Gradients)的方法论基础,在每个局部区域上分别提取基于LBP特性的统计直方图,并将其整合形成完整的LBPH(Local Binary Patterns Histogram),这种全局化的表征方式能够有效提高分类识别性能。具体而言:首先根据预设算法实现对目标图像的整体空间分布特性和空间位置信息进行采集;其次将整个图像划分为若干均匀的小块(如8×8=64个区域),并对每一小块进行独立处理;然后针对每个分割后的子区域分别计算其空间分布特性的统计特性,并对其进行归一化处理;最后按照空间位置顺序整合各子区域的信息形成整体描述即为最终的目标物LBPH表征结果;最后从足够大的样本库中提取相应的LBPH描述符并利用机器学习算法建立分类器模型以实现目标物的有效识别与区分任务
关于LBP特征向量的空间维度,在取样的邻域数目是8个的情况下,在标准LBP方法下对应的模式总数是256,则该空间维度的计算结果为64 \times 256 = 16384;采用均匀化的LBP方法时对应的模式总数是59,则此时的空间维度计算结果为64 \times 59 = 3776。通过引入等价性处理后的模式结构能够实现有效的数据降维,在不影响模型性能的前提下显著降低了计算复杂度。
//计算LBP特征图像的直方图LBPH
Mat getLBPH(InputArray _src,int numPatterns,int grid_x,int grid_y,bool normed)
{
Mat src = _src.getMat();
int width = src.cols / grid_x;
int height = src.rows / grid_y;
//定义LBPH的行和列,grid_x*grid_y表示将图像分割成这么些块,numPatterns表示LBP值的模式种类
Mat result = Mat::zeros(grid_x * grid_y,numPatterns,CV_32FC1);
if(src.empty())
{
return result.reshape(1,1);
}
int resultRowIndex = 0;
//对图像进行分割,分割成grid_x*grid_y块,grid_x,grid_y默认为8
for(int i=0;i<grid_x;i++)
{
for(int j=0;j<grid_y;j++)
{
//图像分块
Mat src_cell = Mat(src,Range(i*height,(i+1)*height),Range(j*width,(j+1)*width));
//计算直方图
Mat hist_cell = getLocalRegionLBPH(src_cell,0,(numPattern-1),true);
//将直方图放到result中
Mat rowResult = result.row(resultRowIndex);
hist_cell.reshape(1,1).convertTo(rowResult,CV_32FC1);
resultRowIndex++;
}
}
return result.reshape(1,1);
}
//计算一个LBP特征图像块的直方图
Mat getLocalRegionLBPH(const Mat& src,int minValue,int maxValue,bool normed)
{
//定义存储直方图的矩阵
Mat result;
//计算得到直方图bin的数目,直方图数组的大小
int histSize = maxValue - minValue + 1;
//定义直方图每一维的bin的变化范围
float range[] = { static_cast<float>(minValue),static_cast<float>(maxValue + 1) };
//定义直方图所有bin的变化范围
const float* ranges = { range };
//计算直方图,src是要计算直方图的图像,1是要计算直方图的图像数目,0是计算直方图所用的图像的通道序号,从0索引
//Mat()是要用的掩模,result为输出的直方图,1为输出的直方图的维度,histSize直方图在每一维的变化范围
//ranges,所有直方图的变化范围(起点和终点)
calcHist(&src,1,0,Mat(),result,1,&histSize,&ranges,true,false);
//归一化
if(normed)
{
result /= (int)src.total();
}
//结果表示成只有1行的矩阵
return result.reshape(1,1);
}
除了以下几种经典的LBP特征之外(除了以上几种比较经典的LBP特征外),还有多种变体(还有诸多变种)。例如:
- TLBP通过将中心像素与其周围的全部像素进行对比,并非以采样点的数量来判断;
- DLBP通过编码每个方向上的灰度变化情况,并使用两个二进制位来表示;
- MLBP则通过将中心像素替换为其采样区域中所有 pixels 的平均值来计算;
以及VLBP和RGB-LPB等方法
LBP特征的应用
目标检测
在人脸检测领域中常用的模型主要包括Haar特征与AdaBoost结合使用的分类器。目前主流的人脸识别框架如OpenCV已经实现了基于LBP+AdaBoost技术和HOG+AdaBoost技术的目标检测方法,并且LBP特征因其较高的训练效率而特别适合应用于实时检测场景
人脸识别
人脸识别系统中的LBP特征向量主要用于直方图间的比较分析。具体而言,在这一过程中, 通过采用距离度量方法(如方差)来识别训练数据中与输入图像最接近的特征向量, 并将其所属的类别作为最终的人脸识别结果输出。
reference
- Paper: Gray Scale and Rotation Invariant Texture Classification with Local Binary Patterns
- Paper: Multiresolution Gray Scale and Rotation Invariant Texture Classification with Local Binary Patterns
- Paper: Face Recognition with Local Binary Patterns
- Paper: Learning Multi-scale Block Local Binary Patterns for Face Recognition
- http://www.voidcn.com/blog/quincuntial/article/p-4988349.html
- <>
- http://blog.jasonding.top/2014/11/04/Machine Learning/【计算机视觉】LBP纹理特征/
- <>
