http://blog.youkuaiyun.com/dujian996099665/article/details/8886576
一,原始LBP算法
LBP的基本思想是对图像的像素和它局部周围像素进行对比后的结果进行求和。把这个像素作为中心,对相邻像素进行阈值比较。如果中心像素的亮度大于等于他的相邻像素,把他标记为1,否则标记为0。你会用二进制数字来表示每个像素,比如11001111。因此,由于周围相邻8个像素,你最终可能获取2^8个可能组合,被称为局部二值模式,有时被称为LBP码。第一个在文献中描述的LBP算子实际使用的是3*3的邻域
一个更加正式的LBP操作可以被定义为
其中 是中心像素,亮度是
;而
则是相邻像素的亮度。s是一个符号函数:
这种描述方法使得你可以很好的捕捉到图像中的细节。实际上,研究者们可以用它在纹理分类上得到最先进的水平。正如刚才描述的方法被提出后,固定的近邻区域对于尺度变化的编码失效。所以,使用一个变量的扩展方法,在文献[AHP04]中有描述。主意是使用可变半径的圆对近邻像素进行编码,这样可以捕捉到如下的近邻:
对一个给定的点 ,他的近邻点
可以由如下计算:
其中,R是圆的半径,而P是样本点的个数。
这个操作是对原始LBP算子的扩展,所以有时被称为扩展LBP(又称为圆形LBP)。如果一个在圆上的点不在图像坐标上,我们使用他的内插点。计算机科学有一堆聪明的插值方法,而OpenCV使用双线性插值。
二.原始LBP算法的实现
附上代码:
- // LBP.cpp : 定义控制台应用程序的入口点。
- //
- /***********************************************************************
- * OpenCV 2.4.4 测试例程
- * 杜健健 提供
- ***********************************************************************/
- #include "stdafx.h"
- #include <opencv2/opencv.hpp>
- #include <cv.h>
- #include <highgui.h>
- #include <cxcore.h>
- using namespace std;
- using namespace cv;
- //原始的LBP算法
- //使用模板参数
- template <typename _Tp> static
- void olbp_(InputArray _src, OutputArray _dst) {
- // get matrices
- Mat src = _src.getMat();
- // allocate memory for result
- _dst.create(src.rows-2, src.cols-2, CV_8UC1);
- Mat dst = _dst.getMat();
- // zero the result matrix
- dst.setTo(0);
- cout<<"rows "<<src.rows<<" cols "<<src.cols<<endl;
- cout<<"channels "<<src.channels();
- getchar();
- // calculate patterns
- for(int i=1;i<src.rows-1;i++) {
- cout<<endl;
- for(int j=1;j<src.cols-1;j++) {
- _Tp center = src.at<_Tp>(i,j);
- //cout<<"center"<<(int)center<<" ";
- unsigned char code = 0;
- code |= (src.at<_Tp>(i-1,j-1) >= center) << 7;
- code |= (src.at<_Tp>(i-1,j ) >= center) << 6;
- code |= (src.at<_Tp>(i-1,j+1) >= center) << 5;
- code |= (src.at<_Tp>(i ,j+1) >= center) << 4;
- code |= (src.at<_Tp>(i+1,j+1) >= center) << 3;
- code |= (src.at<_Tp>(i+1,j ) >= center) << 2;
- code |= (src.at<_Tp>(i+1,j-1) >= center) << 1;
- code |= (src.at<_Tp>(i ,j-1) >= center) << 0;
- dst.at<unsigned char>(i-1,j-1) = code;
- //cout<<(int)code<<" ";
- //cout<<(int)code<<endl;
- }
- }
- }
- //基于旧版本的opencv的LBP算法opencv1.0
- void LBP (IplImage *src,IplImage *dst)
- {
- int tmp[8]={0};
- CvScalar s;
- IplImage * temp = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U,1);
- uchar *data=(uchar*)src->imageData;
- int step=src->widthStep;
- cout<<"step"<<step<<endl;
- for (int i=1;i<src->height-1;i++)
- for(int j=1;j<src->width-1;j++)
- {
- int sum=0;
- if(data[(i-1)*step+j-1]>data[i*step+j])
- tmp[0]=1;
- else
- tmp[0]=0;
- if(data[i*step+(j-1)]>data[i*step+j])
- tmp[1]=1;
- else
- tmp[1]=0;
- if(data[(i+1)*step+(j-1)]>data[i*step+j])
- tmp[2]=1;
- else
- tmp[2]=0;
- if (data[(i+1)*step+j]>data[i*step+j])
- tmp[3]=1;
- else
- tmp[3]=0;
- if (data[(i+1)*step+(j+1)]>data[i*step+j])
- tmp[4]=1;
- else
- tmp[4]=0;
- if(data[i*step+(j+1)]>data[i*step+j])
- tmp[5]=1;
- else
- tmp[5]=0;
- if(data[(i-1)*step+(j+1)]>data[i*step+j])
- tmp[6]=1;
- else
- tmp[6]=0;
- if(data[(i-1)*step+j]>data[i*step+j])
- tmp[7]=1;
- else
- tmp[7]=0;
- //计算LBP编码
- s.val[0]=(tmp[0]*1+tmp[1]*2+tmp[2]*4+tmp[3]*8+tmp[4]*16+tmp[5]*32+tmp[6]*64+tmp[7]*128);
- cvSet2D(dst,i,j,s);写入LBP图像
- }
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- //IplImage* face = cvLoadImage("D://input//yalefaces//01//s1.bmp",CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
- IplImage* face = cvLoadImage("D://input//lena.jpg",CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
- //IplImage* lbp_face = cvCreateImage(cvGetSize(face), IPL_DEPTH_8U,1);
- IplImage* Gray_face = cvCreateImage( cvSize( face->width,face->height ), face->depth, 1);//先分配图像空间
- cvCvtColor(face, Gray_face ,CV_BGR2GRAY);//把载入图像转换为灰度图
- IplImage* lbp_face = cvCreateImage(cvGetSize(Gray_face), IPL_DEPTH_8U,1);//先分配图像空间
- cvNamedWindow("Gray Image",1);
- cvShowImage("Gray Image",Gray_face);
- //Mat face2 = imread("D://input//buti.jpg",CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
- Mat face2 = imread("D://input//yalefaces//01//s1.bmp",CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR);
- //Mat Gray_face2 = Mat::zeros(face2.size(),IPL_DEPTH_8U,1);
- //cvCvtColor(face2,Gray_face2,CV_BGR2RAY);
- Mat lbp_face2 = Mat::zeros(face2.size(),face2.type()) ;
- //Mat::copyTo(lbp_face,face);
- //显示原始的输入图像
- cvNamedWindow("Src Image",CV_WINDOW_AUTOSIZE);
- cvShowImage("Src Image",face);
- //imshow("Src Image",face);
- //计算输入图像的LBP纹理特征
- LBP(Gray_face,lbp_face);
- //olbp_<uchar>((Mat)face,(Mat)lbp_face);//有问题的调用
- olbp_<uchar>(face2,lbp_face2);
- //显示第一幅图像的LBP纹理特征图
- cvNamedWindow("LBP Image",CV_WINDOW_AUTOSIZE);
- cvShowImage("LBP Image",lbp_face);
- //显示第二幅图 的LBP纹理特征图-一张yaleface人脸库中的人脸LBP特征图
- namedWindow("LBP Image2",1);
- imshow("LBP Image2",lbp_face2);
- waitKey();
- //cvReleaseImage(&face);
- cvDestroyWindow("Src Image");
- return 0;
- }
三.示例结果,LBP纹理特征
原始图像lena.jpg
变换成灰度图后:
提取图片的LBP特征:
提取人脸图像的LBP特征;
四.注意事项
1 两个函数都只能对灰度图像就行处理,所以,在使用这两个函数时,必须先把原始图像转换成灰度图像方可
2 关于早期只显示图像1/3或者1/4区域的LBP纹理特征问题的解决方法:
这个是因为你的输入图像不是灰度图的缘故,需要把彩色图,多通道的图像转换成单通道的图像,再作为参数传入函数,才能得到完整图像的LBP纹理特征。
3 载入灰度图像的方法:
把函数cvLoadImage()函数的第二个参数,还有imread() 的第二个参数设置成:CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR
- 就可以了。哦也,这个可是我在网上找了好久才解决的,分享一下,欢迎大家多多指点。
五.遇到的问题
- <span style="font-size:18px;">就是使用Mat结构存储彩色图像,多通道图像后,怎么把它转换成单通道的灰度图。</span>
- <span style="font-size:18px;">我网上找了好多资料,没发现有相关可以参考的函数可以直接调用。有一个提到可以使用IplImage和Mat相互转化。我想用这种方法,就是先转换再调用cvCvtColor() 进行灰度图的转化。但没有试过,不知道可不可以。</span>
- <span style="background-color: rgb(255, 255, 255); "><span style="font-size:18px;">如果有同学知道怎么弄,欢迎告诉我一下,不胜感激</span><span style="font-size: 32px;"><strong>。</strong></span></span>
六.参考
opencv2.4.4中的facerec文档
http://blog.youkuaiyun.com/guoming0000/article/details/8022197
等等。