LBP(local binary pattern)是一种用来描述图像局部纹理特征的算子。原始的LBP于1994年提出,它反映内容是每个像素与周围像素的关系。后被不断的改进和优化,分别提出了LBP旋转不变模式、LBP均匀模式等。
一:原始的LBP
给出一个简单的案例计算LBP:如下图,周围8个像素点比中间点像素值大(或者相等)的记为1,小的记为0,这样就得到二值图,然后按顺时针方向得到二进制串10001111
这样中间点的像素值就用241来代替。注意这里的计算LBP的顺序并没有硬性要求,只是一个量化公式,在同一处理中保持相同的顺序即可。
下面给出LBP的计算公式:
二、旋转不变的LBP模式<LBPROT>
原始的LBP不具有旋转不变性,这样我们就提出了旋转不变的LBP模式。旋转不变的LBP计算公式如下:
这样计算出来的LBP共有36种,分布情况如下面两幅图:
其中白点代表1,黑点代表0。
三:均匀LBP模式
旋转LBP模式同样存在缺陷,大量的实验证明LBP模式的36种情况在一幅图像中分布出现的频率差异较大,得到的效果并不是很好。因此人们提出了均匀LBP模式即uniform LBP。
均匀模式就是一个二进制序列从0到1或是从1到0的变过不超过2次(这个二进制序列首尾相连)。比如:10100000的变化次数为3次所以不是一个uniform pattern。所有的8位二进制数中共有58(变化次数为0的有2种,变化次数为1的有0种,变化次数为2的有56种)个uniform pattern.为什么要提出这么个uniform LBP呢,因为研究者发现他们计算出来的大部分值都在这58种之中,可达到90%以上,所以他们把值分为59类,58个uniform pattern为一类,其它的所有值为第59类。59=(2+0+56)+1,这样直方图从原来的256维变成59维。起到了降维的作用。
计算公式:
应用:
在模式识别如判断两幅图片的相似性时,我们可以统计这两幅图像的LBP特征的直方图。这样,在原始的LBP模式中,统计的直方图可以当做一个256维的向量。直接这样判断,很多时候会因为“位置没有对准”而产生很大的误差。大量研究表明,此时我们可以将一幅图片划分为若干的子区域,对每个子区域内的每个像素点都提取LBP特征,然后,在每个子区域内建立LBP特征的统计直方图。如此一来,每个子区域,就可以用一个统计直方图来进行描述;整个图片就由若干个统计直方图组成。比如将一幅图像划分为10*10个区域,这样得到向量的维数就是256*10*10。而针对uniform LBP,此时向量的维数就是59*10*10, 从而达到降维的目的。
目前,LBP局部纹理提取算子,已经成功应用在指纹识别、字符识别、人脸识别、车牌识别等领域。
Code:
原始的LBP:
1 void LBP(Mat &image, Mat &result) 2 { 3 for(int y = 1; y < image.rows-1; y ++) 4 { 5 for(int x = 1; x < image.cols-1; x++) 6 { 7 uchar neighbor[8] = {0}; 8 neighbor[0] = image.at<uchar>(y-1, x-1); 9 neighbor[1] = image.at<uchar>(y-1, x); 10 neighbor[2] = image.at<uchar>(y-1, x+1); 11 neighbor[3] = image.at<uchar>(y, x+1); 12 neighbor[4] = image.at<uchar>(y+1, x+1); 13 neighbor[5] = image.at<uchar>(y+1, x); 14 neighbor[6] = image.at<uchar>(y+1, x-1); 15 neighbor[7] = image.at<uchar>(y, x-1); 16 uchar center = image.at<uchar>(y, x); 17 uchar temp = 0; 18 for(int k = 0; k < 8; k++) 19 { 20 temp += (neighbor[k] >= center)* (1<<k); // 计算LBP的值 21 } 22 result.at<uchar>(y,x) = temp; 23 } 24 } 25 } 26 27 int main() 28 { 29 Mat image = imread("F:\\lena.png", 0); 30 if(!image.data) 31 { 32 cout << "Fail to load image" << endl; 33 return 0; 34 } 35 Mat result; 36 result.create(Size(image.cols, image.rows), image.type()); 37 LBP(image, result); 38 namedWindow("result", 0); 39 imshow("result", result); 40 waitKey(0); 41 return 0; 42 }
uniform LBP:
1 #include "Stafx.h" 2 using namespace cv; 3 using namespace std; 4 5 // 计算跳变次数 6 int getHopCount(int i) 7 { 8 int a[8] = {0}; 9 int cnt = 0; 10 int k = 7; 11 /************************************************************************/ 12 /* while循环详解: 13 例如i的二进制为000000000000000000000000abcdefgh,经过i&1后a[7]=h;经过i>>1后右移一位 14 变成000000000000000000000000abcdefg,再经过i&1后a[6]=g;经过i>>1后右移一位 15 0000000000000000000000000abcdef,依次类推,则数组a为a[0]=a,a[1]=b,a[2]=c,a[3]=d,a[4]=e, 16 a[5]=f,a[6]=g,a[7]=h 17 */ 18 /************************************************************************/ 19 while(i) 20 { 21 a[k] = i&1; //取i的二进制的最低位 22 i = i >> 1; //右移 23 --k; 24 } 25 //a[0]~a[7]组成环形,只要相邻两个不同就加1 26 for(k = 0; k < 7; k++) 27 { 28 if(a[k] != a[k+1]) 29 { 30 ++cnt; 31 } 32 } 33 if(a[0] != a[7]) 34 { 35 ++cnt; 36 } 37 return cnt; 38 } 39 40 // 降维数组 由256->59 41 void lbp59table(uchar *table) 42 { 43 memset(table, 0, 256); 44 uchar temp = 1; 45 for(int i = 0; i < 256; i++) 46 { 47 if(getHopCount(i) <= 2) // 跳变次数<=2 的为非0值 48 { 49 table[i] = temp; //划分为多少类,类标签从小到大排列,好比一个映射表,搜索索引值 50 temp ++; 51 } 52 } 53 } 54 55 void uniformLBP(Mat &image, Mat &result, uchar *table) 56 { 57 for(int y = 1; y < image.rows-1; y ++) 58 { 59 for(int x = 1; x < image.cols-1; x++) 60 { 61 uchar neighbor[8] = {0}; 62 neighbor[0] = image.at<uchar>(y-1, x-1); 63 neighbor[1] = image.at<uchar>(y-1, x); 64 neighbor[2] = image.at<uchar>(y-1, x+1); 65 neighbor[3] = image.at<uchar>(y, x+1); 66 neighbor[4] = image.at<uchar>(y+1, x+1); 67 neighbor[5] = image.at<uchar>(y+1, x); 68 neighbor[6] = image.at<uchar>(y+1, x-1); 69 neighbor[7] = image.at<uchar>(y, x-1); 70 uchar center = image.at<uchar>(y, x); 71 uchar temp = 0; 72 for(int k = 0; k < 8; k++) 73 { 74 //1<<k表示2^k次幂 75 temp += (neighbor[k] >= center)* (1<<k); // 计算LBP的值 76 } 77 result.at<uchar>(y,x) = table[temp]; // 降为59维空间 78 } 79 } 80 } 81 82 int main() 83 { 84 uchar table[256]; 85 lbp59table(table); 86 Mat image = imread("lena.png", 0); 87 if(!image.data) 88 { 89 cout << "Fail to load image" << endl; 90 return 0; 91 } 92 Mat result; 93 result.create(Size(image.cols, image.rows), image.type()); 94 uniformLBP(image, result, table); 95 namedWindow("uniformResult", 0); 96 imshow("uniformResult", result); 97 waitKey(0); 98 return 0; 99 }
unform lBP
3 #include "Stafx.h" 4 5 //提取局部纹理特征均匀LBP算子 6 7 int gethopcount(int i) //判断环形二进制字符串两位的变化次数 8 { 9 int a[8]={0}; 10 int temp=0; //变换次数 11 int k=7; 12 while (i) 13 { 14 a[k]=i&1; //取二进制最低位 15 i=i>>1; //右移 16 k--; 17 } 18 for (int j=0;j<7;j++) //判断下标0~7 19 { 20 if (a[j]!=a[j+1]) temp++; 21 } 22 //判断下标0和7 23 if (a[0]!=a[7]) temp++; 24 return temp; 25 } 26 //实现降维操作256->59 27 void lbp59table(uchar* table) 28 { 29 memset(table,0,256); 30 uchar temp=1; 31 for (int i=0;i<256;i++) 32 { 33 if (gethopcount(i)<=2) 34 { 35 table[i]=temp; 36 temp++; 37 } 38 } 39 } 40 //均匀LBP算法 41 void uniformLBP(IplImage* src,IplImage* result, uchar* table) 42 { 43 cvCopy(src,result); //注意到cvCopy与cvCloneImage的区别 44 /************************************************************************/ 45 /* cvCopy必须先要创建内存空间,才能使用 46 而cvCloneImage会自动开辟内存空间,并返回指向该内存空间的指针 47 如果在这里使用result=cvCloneImage(src);则main函数中显示的result是无图像 48 说明这两个地址所指向的空间内存不一样。 49 */ 50 /************************************************************************/ 51 for (int i=1;i<src->width-1;i++) 52 for (int j=1;j<src->height-1;j++) 53 { 54 uchar neighbor[8]={0}; 55 neighbor[0]=CV_IMAGE_ELEM(src,uchar,j-1,i-1); 56 neighbor[1]=CV_IMAGE_ELEM(src,uchar,j-1,i); 57 neighbor[2]=CV_IMAGE_ELEM(src,uchar,j-1,i+1); 58 neighbor[3]=CV_IMAGE_ELEM(src,uchar,j,i+1); 59 neighbor[4]=CV_IMAGE_ELEM(src,uchar,j+1,i+1); 60 neighbor[5]=CV_IMAGE_ELEM(src,uchar,j+1,i); 61 neighbor[6]=CV_IMAGE_ELEM(src,uchar,j+1,i-1); 62 neighbor[7]=CV_IMAGE_ELEM(src,uchar,j,i-1); 63 uchar central=CV_IMAGE_ELEM(src,uchar,j,i); 64 uchar temp=0; 65 for (int k=0;k<8;k++) 66 { 67 temp+=(neighbor[k]>=central)*(1<<k); //1<<k表示2^k次幂 68 } 69 uchar*ptr=(uchar*) (result->imageData+j*result->widthStep+i); 70 *ptr=table[temp]; 71 }; 72 } 73 void main() 74 { 75 uchar table[256]; 76 lbp59table(table); //由256维降成59维 77 IplImage* src=cvLoadImage("lena.png",CV_LOAD_IMAGE_GRAYSCALE); 78 IplImage* result=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1); 79 uniformLBP(src,result,table); 80 cvShowImage("1",result); 81 cvWaitKey(); 82 cvReleaseImage(&src); 83 cvReleaseImage(&result); 84 }