OpenCV-图像特征提取算法3_LBP
一,简介
LBP(Local Binary Pattern,局部二值模式)是一种用来描述图像局部纹理特征的算子;它具有旋转不变性和灰度不变性等显著的优点。它是首先由T. Ojala, M.Pietikäinen, 和 D. Harwood 在1994年提出,用于纹理特征提取。
二、原始LBP特征描述及计算方法
2.1,算法描述
1)对图像中的所有点,以该点为中心,取3x3的邻域窗口;
2)将8-邻域像素值与中心点像素值进行比较,大于或等于中心像素标记为1,否则标记为0;
3)将周围0-1序列,以一定的顺序排列,成一个8位的无符号的二进制数,转化成整数;
4)这个整数就是表征这个窗口的LBP值。
LBP值共有2的8次方种可能,因此LBP值有256种。中心像素的LBP值反映了该像素周围区域的纹理信息。 以上,便是最基本的LBP算子。由于直接利用的灰度比较,所以其具有灰度不变性;但是,有两个很明显的缺点:
1)产生的二进制模式多;
2)不具有旋转不变性。
2.2,实现代码
实现代码部分整理了好久。
//原始LBP特征计算
Mat LBP(Mat img)
{
Mat result;
result.create(img.rows - 2, img.cols - 2, img.type());
result.setTo(0);
for (int i = 1; i<img.rows - 1; i++)
{
for (int j = 1; j<img.cols - 1; j++)
{
uchar center = img.at<uchar>(i, j);
uchar code = 0;
code |= (img.at<uchar>(i - 1, j - 1) >= center) << 7;
code |= (img.at<uchar>(i - 1, j) >= center) << 6;
code |= (img.at<uchar>(i - 1, j + 1) >= center) << 5;
code |= (img.at<uchar>(i, j + 1) >= center) << 4;
code |= (img.at<uchar>(i + 1, j + 1) >= center) << 3;
code |= (img.at<uchar>(i + 1, j) >= center) << 2;
code |= (img.at<uchar>(i + 1, j - 1) >= center) << 1;
code |= (img.at<uchar>(i, j - 1) >= center) << 0;
result.at<uchar>(i - 1, j - 1) = code;
}
}
return result;
}
//测试
void CCutImageVS2013Dlg::OnBnClickedTestButton1()
{
Mat image = imread("D:\\test\\hog.png", 0);
Size dsize = Size(image.cols * 0.5, image.rows * 0.5);
resize(image, image, dsize);
Mat dst = LBP(image);
imshow("原图", image);
imshow("LBP", dst);
waitKey(0);
}
2.3,处理效果
三、圆形LBP特征(Circular LBP or Extended LBP)
3.1,算法描述
不难注意到原始的LBP算子仅仅只是覆盖了很小的一个3×3领域的范围,这显然不能满足提取不同尺寸纹理特征的需求。为了适应不同尺度的纹理特征,并达到灰度和旋转不变性的要求,Ojala等对LBP 算子进行了改进,将 3×3邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域。改进后的LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点。
假设半径为R的圆形区域内含有P个采样点的LBP算子(1,2指的是半径,8,16指的是采样点数):
比如下图定义了一个5X5的领域:
上图中的8个黑色的采样点,R是采样半径,p是第p个采样点,P是采样数目。对于给定中心点(,),其邻域像素位置为(,),p∈P,其采样点(,)用如下公式计算:
通过上式可以计算任意个采样点的坐标,但是计算得到的坐标未必完全是整数,所以可以通过双线性插值来得到该采样点的像素值:
3.2,实现代码
//圆形LBP特征计算,这种方法适于理解,但在效率上存在问题,声明时默认neighbors=8
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++)
{
//获得中心像素点的灰度值
int center = src.at<uchar>(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<uchar>(x1, y1) * w1 + src.at<uchar>(x1, y2) *w2 \
+ src.at<uchar>(x2, y1) * w3 + src.at<uchar>(x2, y2) *w4;
//通过比较获得LBP值,并按顺序排列起来
lbpCode |= (neighbor>center) << (neighbors - k - 1);
}
dst.at<uchar>(i - radius, j - radius) = lbpCode;
}
}
}
//圆形LBP特征计算,效率优化版本,声明时默认neighbors=8
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++)
{
//获得中心像素点的灰度值
int center = src.at<uchar>(i, j);
//根据双线性插值公式计算第k个采样点的灰度值
float neighbor = src.at<uchar>(i + x1, j + y1) * w1 + src.at<uchar>(i + x1, j + y2) *w2 \
+ src.at<uchar>(i + x2, j + y1) * w3 + src.at<uchar>(i + x2, j + y2) *w4;
//LBP特征图像的每个邻居的LBP值累加,累加通过与操作完成,对应的LBP值通过移位取得
dst.at<uchar>(i - radius, j - radius) |= (neighbor>center) << (neighbors - k - 1);
}
}
}
}
//一个窗口显示多幅图像
//const std::string& MultiShow_WinName //窗口名称
//const vector<Mat>& SrcImg_V //图片数组
//CvSize SubPlot //显示图像排列方式,比如cvSize(4, 1)表示1行4列
//CvSize ImgMax_Size //图像尺寸
//bool bgray = true //true表示显示灰度图,false表示显示彩色图,默认为灰度图
void MultiImage_OneWin(const std::string& MultiShow_WinName, const vector<Mat>& SrcImg_V, CvSize SubPlot, CvSize ImgMax_Size,bool bgray=true)
{
//************* Usage *************//
//vector<Mat> imgs(4);
//imgs[0] = imread("F:\\SA2014.jpg");
//imgs[1] = imread("F:\\SA2014.jpg");
//imgs[2] = imread("F:\\SA2014.jpg");
//imgs[3] = imread("F:\\SA2014.jpg");
//MultiImage_OneWin("T", imgs, cvSize(2, 2), cvSize(400, 280));
//Window's image
Mat Disp_Img;
//Width of source image
CvSize Img_OrigSize = cvSize(SrcImg_V[0].cols, SrcImg_V[0].rows);
//******************** Set the width for displayed image ********************//
//Width vs height ratio of source image
float WH_Ratio_Orig = Img_OrigSize.width / (float)Img_OrigSize.height;
CvSize ImgDisp_Size = cvSize(100, 100);
if (Img_OrigSize.width > ImgMax_Size.width)
ImgDisp_Size = cvSize(ImgMax_Size.width, (int)ImgMax_Size.width / WH_Ratio_Orig);
else if (Img_OrigSize.height > ImgMax_Size.height)
ImgDisp_Size = cvSize((int)ImgMax_Size.height*WH_Ratio_Orig, ImgMax_Size.height);
else
ImgDisp_Size = cvSize(Img_OrigSize.width, Img_OrigSize.height);
//******************** Check Image numbers with Subplot layout ********************//
int Img_Num = (int)SrcImg_V.size();
if (Img_Num > SubPlot.width * SubPlot.height)
{
cout << "Your SubPlot Setting is too small !" << endl;
exit(0);
}
//******************** Blank setting ********************//
CvSize DispBlank_Edge = cvSize(80, 60);
CvSize DispBlank_Gap = cvSize(15, 15);
//******************** Size for Window ********************//
if (bgray)//灰度图
{
Disp_Img.create(Size(ImgDisp_Size.width*SubPlot.width + DispBlank_Edge.width + (SubPlot.width - 1)*DispBlank_Gap.width,
ImgDisp_Size.height*SubPlot.height + DispBlank_Edge.height + (SubPlot.height - 1)*DispBlank_Gap.height), CV_8UC1);//此处修改三通道或者单通道图像
}
else //彩色图
{
Disp_Img.create(Size(ImgDisp_Size.width*SubPlot.width + DispBlank_Edge.width + (SubPlot.width - 1)*DispBlank_Gap.width,
ImgDisp_Size.height*SubPlot.height + DispBlank_Edge.height + (SubPlot.height - 1)*DispBlank_Gap.height), CV_8UC3);//此处修改三通道或者单通道图像
}
Disp_Img.setTo(0);//Background
//Left top position for each image
int EdgeBlank_X = (Disp_Img.cols - (ImgDisp_Size.width*SubPlot.width + (SubPlot.width - 1)*DispBlank_Gap.width)) / 2;
int EdgeBlank_Y = (Disp_Img.rows - (ImgDisp_Size.height*SubPlot.height + (SubPlot.height - 1)*DispBlank_Gap.height)) / 2;
CvPoint LT_BasePos = cvPoint(EdgeBlank_X, EdgeBlank_Y);
CvPoint LT_Pos = LT_BasePos;
//Display all images
for (int i = 0; i < Img_Num; i++)
{
//Obtain the left top position
if ((i%SubPlot.width == 0) && (LT_Pos.x != LT_BasePos.x))
{
LT_Pos.x = LT_BasePos.x;
LT_Pos.y += (DispBlank_Gap.height + ImgDisp_Size.height);
}
//Writting each to Window's Image
Mat imgROI = Disp_Img(Rect(LT_Pos.x, LT_Pos.y, ImgDisp_Size.width, ImgDisp_Size.height));
resize(SrcImg_V[i], imgROI, Size(ImgDisp_Size.width, ImgDisp_Size.height));
LT_Pos.x += (DispBlank_Gap.width + ImgDisp_Size.width);
}
//Get the screen size of computer
int Scree_W = GetSystemMetrics(SM_CXSCREEN);
int Scree_H = GetSystemMetrics(SM_CYSCREEN);
cvNamedWindow(MultiShow_WinName.c_str(), CV_WINDOW_AUTOSIZE);
cvMoveWindow(MultiShow_WinName.c_str(), (Scree_W - Disp_Img.cols) / 2, (Scree_H - Disp_Img.rows) / 2);//Centralize the window
cvShowImage(MultiShow_WinName.c_str(), &(IplImage(Disp_Img)));
cvWaitKey(0);
cvDestroyWindow(MultiShow_WinName.c_str());
}
//测试
void CCutImageVS2013Dlg::OnBnClickedTestButton1()
{
Mat image = imread("D:\\test\\hog.png", 0);
Size dsize = Size(image.cols * 0.25, image.rows * 0.25);
resize(image, image, dsize);
//-------------原始LBP---------------
//Mat dst = LBP(image);
//imshow("原图", image);
//imshow("LBP", dst);
//waitKey(0);
if (image.empty()){
cout << "图像加载失败" << endl;
return;
}
vector<Mat> imageConvert(4); //改变亮度后的图
vector<Mat> dst(4); //LBP处理后的图
image.convertTo(imageConvert[0], image.type(), 1, -30); //调节图像亮度
image.convertTo(imageConvert[1], image.type(), 1, -10); //调节图像亮度
image.convertTo(imageConvert[2], image.type(), 1, 10); //调节图像亮度
image.convertTo(imageConvert[3], image.type(), 1, 30); //调节图像亮度
//getCircularLBPFeature(imageConvert[0], dst[0], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[1], dst[1], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[2], dst[2], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[3], dst[3], 3, 8); //LBP处理
getCircularLBPFeatureOptimization(imageConvert[0], dst[0], 3, 8); //LBP处理
getCircularLBPFeatureOptimization(imageConvert[1], dst[1], 3, 8); //LBP处理
getCircularLBPFeatureOptimization(imageConvert[2], dst[2], 3, 8); //LBP处理
getCircularLBPFeatureOptimization(imageConvert[3], dst[3], 3, 8); //LBP处理
MultiImage_OneWin("原图", imageConvert, cvSize(4, 1), cvSize(image.cols, image.rows), true);
MultiImage_OneWin("圆形LBP处理", dst, cvSize(4, 1), cvSize(image.cols, image.rows), true);
return;
}
3.3,处理效果
通过LBP特征的定义可以看出,LBP特征对光照变化是鲁棒的,其效果如下图所示:
四、旋转不变LBP特征
4.1,算法描述
由于LBP的二进制模式是以一定的方向、顺序进行编码的,所以当图像发生旋转时,按这种编码的话,LBP值会发生改变,因此是不具有旋转不变性的。Maenpaa等人提出了具有旋转不变性的LBP算子。
解决办法是:不断旋转邻域得到一系列的LBP值,取其中最小值作为该邻域的LBP值。旋转过程实质上就是对二进制模式进行循环移位的过程。
一共36个旋转不变的LBP编码模式,如下图所示:
通过引入旋转不变的定义,使LBP算子更具鲁棒性。但这也是LBP算子丢失了方向信息。在很多场合,方向信息非常重要;然而,在纹理图像分析中,LBP依然被证明是有效的。
4.2,实现代码
//旋转不变圆形LBP特征计算,声明时默认neighbors=8
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++)
{
//获得中心像素点的灰度值
int center = src.at<uchar>(i, j);
//根据双线性插值公式计算第k个采样点的灰度值
float neighbor = src.at<uchar>(i + x1, j + y1) * w1 + src.at<uchar>(i + x1, j + y2) *w2 \
+ src.at<uchar>(i + x2, j + y1) * w3 + src.at<uchar>(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
void TestCircularLBP()
{
Mat image = imread("D:\\test\\hog.png", 0);
Size dsize = Size(image.cols * 0.25, image.rows * 0.25);
resize(image, image, dsize);
//-------------1原始LBP---------------
//Mat dst = LBP(image);
//imshow("原图", image);
//imshow("LBP", dst);
//waitKey(0);
if (image.empty()){
cout << "图像加载失败" << endl;
return;
}
vector<Mat> imageConvert(4); //改变亮度后的图
vector<Mat> dst(4); //LBP处理后的图
image.convertTo(imageConvert[0], image.type(), 1, -30); //调节图像亮度
image.convertTo(imageConvert[1], image.type(), 1, -10); //调节图像亮度
image.convertTo(imageConvert[2], image.type(), 1, 10); //调节图像亮度
image.convertTo(imageConvert[3], image.type(), 1, 30); //调节图像亮度
//-------------2圆形LBP---------------
//getCircularLBPFeature(imageConvert[0], dst[0], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[1], dst[1], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[2], dst[2], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[3], dst[3], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[0], dst[0], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[1], dst[1], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[2], dst[2], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[3], dst[3], 3, 8); //LBP处理
//-------------3旋转LBP---------------
getRotationInvariantLBPFeature(imageConvert[0], dst[0], 3, 8); //LBP处理
getRotationInvariantLBPFeature(imageConvert[1], dst[1], 3, 8); //LBP处理
getRotationInvariantLBPFeature(imageConvert[2], dst[2], 3, 8); //LBP处理
getRotationInvariantLBPFeature(imageConvert[3], dst[3], 3, 8); //LBP处理
MultiImage_OneWin("原图", imageConvert, cvSize(4, 1), cvSize(image.cols, image.rows), true);
MultiImage_OneWin("旋转不变性LBP处理", dst, cvSize(4, 1), cvSize(image.cols, image.rows), true);
return;
}
//测试
void CCutImageVS2013Dlg::OnBnClickedTestButton1()
{
TestCircularLBP();
}
4.3,处理效果
五、LBP等价模式
5.1,算法描述
也称为Uniform Pattern LBP特征。原始的LBP算子,随着邻域内采样点数的增加,二进制模式的种类是急剧增加的。
对于半径为R的圆形区域内含有P个采样点的LBP算子将会产P^2中模式,如5X5领域内20个采样点,有2^20=104857种二进制模式。过多的二值模式对于特征的提取以及信息的存取都是不利的。例如,将LBP算子用于人脸识别时,常采用的LBP模式的统计直方图来表达人脸信息,而较多的模式种类将使得数据量过大,且直方图过于稀疏。因此,需要对原始LBP模式进行降维,使得数据量减少的情况下能最好的代表图像的信息。
旋转LBP模式同样存在缺陷,大量的实验证明LBP模式的36种情况在一幅图像中分布出现的频率差异较大,得到的效果不是很好。因此人们提出了uniform LBP。
“等价模式”被定义为:当某个LBP所对应的循环二进制数从0到1或者从1到0最多有两次跳变时,该LBP所对应的二进制就称为一个等价模式。在实际图像中,计算出来的大部分值都在等价模式之中,可达百分之90%以上。uniformLBP模式的个数为P(P-1)+2,P为领域像素点个数。对于8个采样点,uniform形式有58种输出, 其他的所有值为第59类,这样直方图从原来的256维降到了59维,并且可以减少高频噪声带来的影响。
uniform形式的58种LBP模式如下图所示:
5.2,实现代码
//计算跳变次数
#include<bitset>
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;
}
//4等价模式LBP特征计算
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++)
{
//获得中心像素点的灰度值
int center = src.at<uchar>(i, j);
//根据双线性插值公式计算第k个采样点的灰度值
float neighbor = src.at<uchar>(i + x1, j + y1) * w1 + src.at<uchar>(i + x1, j + y2) *w2 \
+ src.at<uchar>(i + x2, j + y1) * w3 + src.at<uchar>(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)];
}
}
}
}
}
//测试各种LBP
void TestCircularLBP()
{
Mat image = imread("D:\\test\\hog.png", 0);
Size dsize = Size(image.cols * 0.25, image.rows * 0.25);
resize(image, image, dsize);
//-------------1原始LBP---------------
//Mat dst = LBP(image);
//imshow("原图", image);
//imshow("LBP", dst);
//waitKey(0);
if (image.empty()){
cout << "图像加载失败" << endl;
return;
}
vector<Mat> imageConvert(4); //改变亮度后的图
vector<Mat> dst(4); //LBP处理后的图
image.convertTo(imageConvert[0], image.type(), 1, -30); //调节图像亮度
image.convertTo(imageConvert[1], image.type(), 1, -10); //调节图像亮度
image.convertTo(imageConvert[2], image.type(), 1, 10); //调节图像亮度
image.convertTo(imageConvert[3], image.type(), 1, 30); //调节图像亮度
//-------------2圆形LBP---------------
//getCircularLBPFeature(imageConvert[0], dst[0], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[1], dst[1], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[2], dst[2], 3, 8); //LBP处理
//getCircularLBPFeature(imageConvert[3], dst[3], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[0], dst[0], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[1], dst[1], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[2], dst[2], 3, 8); //LBP处理
//getCircularLBPFeatureOptimization(imageConvert[3], dst[3], 3, 8); //LBP处理
//-------------3旋转LBP---------------
//getRotationInvariantLBPFeature(imageConvert[0], dst[0], 3, 8); //LBP处理
//getRotationInvariantLBPFeature(imageConvert[1], dst[1], 3, 8); //LBP处理
//getRotationInvariantLBPFeature(imageConvert[2], dst[2], 3, 8); //LBP处理
//getRotationInvariantLBPFeature(imageConvert[3], dst[3], 3, 8); //LBP处理
//-------------4等价LBP---------------
getUniformPatternLBPFeature(imageConvert[0], dst[0], 3, 8); //LBP处理
getUniformPatternLBPFeature(imageConvert[1], dst[1], 3, 8); //LBP处理
getUniformPatternLBPFeature(imageConvert[2], dst[2], 3, 8); //LBP处理
getUniformPatternLBPFeature(imageConvert[3], dst[3], 3, 8); //LBP处理
MultiImage_OneWin("原图", imageConvert, cvSize(4, 1), cvSize(image.cols, image.rows), true);
MultiImage_OneWin("旋转不变性LBP处理", dst, cvSize(4, 1), cvSize(image.cols, image.rows), true);
return;
}
//测试
void CCutImageVS2013Dlg::OnBnClickedTestButton1()
{
TestCircularLBP();
}
5.3,处理效果
欢迎扫码关注我的微信公众号
更多推荐
所有评论(0)