LBP的统一模式和非统一模式: (降维度)

统一模式:
中心点的一周,两个相邻像素点间求导为1,即由0到1跳变,或者由1向0跳变。
则跳变的次数为U的值,U等于0或者2时即为统一模式,否则为非统一模式。
统一模式共有58种:


非统一模式:

灰度不变性的圆形LBP算法:
从上面的计算我们可以看出,基本的LBP算法的计算仅包含其相邻的八个像素点,半径小,覆盖范围很小,这种方式的表达能力相对较小.
所以,Ojala等人提出一种改进的方法,将原来的33的方形九个像素点的计算模式,改成了任一点即半径的圆形模式,这样表达方式就不受限制了,
而且原始的正方形领域的像素点空间被圆形领域所代替。这种圆形领域的算法的半径R可以任意的改变,且半径R的像素点个数也不固定。*
改进后的LBP算法通常用符号LBP(p,b)表达,R为圆形半径,P代表在该圆形范畴内的P个不同像素点。
下图为****几种常见的不同半径和不同像素点的LBP算法的示意图:

为了对LBP算子进行表示,用函数E表示为中心像素和相邻像素点的联合分布函数:

其中,gr表示图像中某个中心像素点的灰度值,g0~gk-1表示与中心像素点相邻的周围的 像素点,其中K=0,1,2~k-1。
每个E函数其中心像素点和相邻像素点所围成的圆的半径可以不同,其相邻像素点的数量也可以不同。****一个中心像素点周围的其它像素点gk的坐标可以表示成如式1所示:

其中主要注意的是,gr的坐标计算出来之后,可能不是整数,此时一般的解决方法是采用双线性内插值求得该像素点的灰度gr。
假设周围每一点和中心点的差值与中心点是相互独立的,如下式所示:

假设中心像素点的独立性可能有时会造成信息的丢失,但是,其对后续的结果影响很小。而且,在影响结果很小的前提下,以丢失少量信息的代价得到对局部纹理描述的平移不变性,
结果时可以接受的、而且这种相对差值表示的纹理特征与表示亮度的中心像素点关系不是很大,所以可以忽略,如式3所示,联合分布表达式为:

由于最终想要得到的是其二值特征,即知道差值的结果符号即可以,这样结果就不受到到光照的影响,则可以表示成公式4,

通过计算得到的二值,需要给每个S赋一个权值,再对其进行求和,即可得到此中心像素点的局部纹理特征值,即LBPk,r

通过上述的计算过程可知,圆形LBP算法与基本LBP算法基本原理相同,但是它的表达方式更灵活,有很好的鲁棒性,表示范围与表达能力更强。
1 #include <opencv2/opencv.hpp>
2 #include <iostream>
3 #include "math.h"
4
5 using namespace cv;
6 using namespace std;
7
8 Mat src, gray_src;
9 int current_radius = 3; //奇数,中心像素的坐标
10 int max_count = 20;
11 void ELBP_Demo(int, void*);
12 int main(int argc, char** argv) {
13 src = imread("L:/opencv_picture/13.jpg");
14 if (src.empty()) {
15 printf("could not load image...\n");
16 return -1;
17 }
18 const char* output_tt = "LBP Result";
19 namedWindow("input image", CV_WINDOW_AUTOSIZE);
20 namedWindow(output_tt, CV_WINDOW_AUTOSIZE);
21 imshow("input image", src);
22
23 // convert to gray 灰度图像
24 cvtColor(src, gray_src, COLOR_BGR2GRAY);
25 int width = gray_src.cols;
26 int height = gray_src.rows;
27
28 // 基本LBP演示
29 //大框架里采用3×3的滑框,所以上下左右都要减一行出来
30 Mat lbpImage = Mat::zeros(gray_src.rows - 2, gray_src.cols - 2, CV_8UC1);
31 for (int row = 1; row < height - 1; row++) {
32 for (int col = 1; col < width - 1; col++) {
33 uchar c = gray_src.at<uchar>(row, col);
34 uchar code = 0;
35 code |= (gray_src.at<uchar>(row - 1, col - 1) > c) << 7;
36 // gray_src.at() 的像素值与阈值c比较,大于为1,小于等于为0
37 // a |= b 按位取或以后把值赋给a
38 // a |= b << 7 将b向左位移7位以后,将a与b按位取或以后把值赋给a
39 code |= (gray_src.at<uchar>(row - 1, col) > c) << 6;
40 code |= (gray_src.at<uchar>(row - 1, col + 1) > c) << 5;
41 code |= (gray_src.at<uchar>(row, col + 1) > c) << 4;
42 code |= (gray_src.at<uchar>(row + 1, col + 1) > c) << 3;
43 code |= (gray_src.at<uchar>(row + 1, col) > c) << 2;
44 code |= (gray_src.at<uchar>(row + 1, col - 1) > c) << 1;
45 code |= (gray_src.at<uchar>(row, col - 1) > c) << 0;
46 lbpImage.at<uchar>(row - 1, col - 1) = code;
47 //code为8位的二进制数,相当于LBP图像周边像素加权后的像素值
48 }
49 }
50 imshow(output_tt, lbpImage);
51
52 // ELBP 演示
53 namedWindow("ELBP Result", CV_WINDOW_AUTOSIZE);
54 createTrackbar("ELBP Radius:", "ELBP Result", ¤t_radius, max_count, ELBP_Demo);
55 ELBP_Demo(0, 0);
56
57
58 waitKey(0);
59 return 0;
60 }
61
62
63 void ELBP_Demo(int, void*) {
64 int offset = current_radius * 2;
65 Mat elbpImage = Mat::zeros(gray_src.rows - offset, gray_src.cols - offset, CV_8UC1);
66 int width = gray_src.cols;
67 int height = gray_src.rows;
68
69 int numNeighbors = 8;
70 for (int n = 0; n < numNeighbors; n++) { //x、y的精确值
71 float x = static_cast<float>(current_radius) * cos(2.0 * CV_PI*n / static_cast<float>(numNeighbors));
72 float y = static_cast<float>(current_radius) * -sin(2.0 * CV_PI*n / static_cast<float>(numNeighbors));
73
74 //x、y向上向下的整数值
75 int fx = static_cast<int>(floor(x));
76 int fy = static_cast<int>(floor(y));
77 int cx = static_cast<int>(ceil(x));
78 int cy = static_cast<int>(ceil(y));
79 // x、y的小数部分值
80 float ty = y - fy;
81 float tx = x - fx;
82 //一个周围的点在一个像素的四个角点上的权重(x、y小数部分的乘积)
83 float w1 = (1 - tx)*(1 - ty);
84 float w2 = tx*(1 - ty);
85 float w3 = (1 - tx)* ty;
86 float w4 = tx*ty;
87
88 for (int row = current_radius; row < (height - current_radius); row++) {
89 for (int col = current_radius; col < (width - current_radius); col++) {
90 float t = w1* gray_src.at<uchar>(row + fy, col + fx) + w2* gray_src.at<uchar>(row + fy, col + cx) +
91 w3* gray_src.at<uchar>(row + cy, col + fx) + w4* gray_src.at<uchar>(row + cy, col + cx);
92 elbpImage.at<uchar>(row - current_radius, col - current_radius) +=
93 ((t > gray_src.at<uchar>(row, col)) && (abs(t - gray_src.at<uchar>(row, col)) > std::numeric_limits<float>::epsilon())) << n;
94 }
95 }
96 }
97 imshow("ELBP Result", elbpImage);
98 return;
99 }
