很久没有写博客了,最近实现了一下LBP特征,写一篇相关的博客,代码写的比较挫,组织比较混乱(-_-),就将就看看吧,发现有什么错误的地方,欢迎大家批评指正。
注:本文使用VS2013+OpenCV3.0
LBP的原理比较简单,网上有很多很不错的文章,这里给出几篇我认为不错的文章
目标检测的图像特征提取之(二)LBP特征
LBP local binary patterns 人脸特征提取方法
LBP特征学习及实现
如果大家想深入了解LBP,可以读一读论文
[2002 PAMI] Multiresolution gray-scale and rotation
[2006 PAMI] Face Description with Local Binary Patterns Application to Face Recognition
(1)首先将检测窗口划分为16×16的小区域(cell);
(2)计算每个像素点的旋转不变等价模式的LBP特征值,得到对应的十进制数,也就是模式。
(3)然后计算每个cell的LBP特征值直方图,然后对该直方图进行归一化处理(每个模块中,对于每个bin,h[i]/=sum,sum就是一副图像中所有等价类的个数)。
(4)最后将得到的每个cell的统计直方图进行连接成为一个特征向量,也就是整幅图的LBP纹理特征向量;
然后便可利用SVM或者其他机器学习算法进行识别了。
下面就是最近实现的LBP
LBP.h
#ifndef __LBP_H__
#define __LBP_H__
#include "opencv2/opencv.hpp"
#include<vector>
using namespace std;
using namespace cv;
// LBP的简单实现
class LBP
{
public:
// 灰度不变LBP(256种模式)
void LBP_256(const Mat &srcImage,Mat &LBPImage);// 计算256维LBP特征图
// 灰度不变+等价模式LBP(58种模式),没有考虑旋转不变
void ComputerLBPFeatureVector(const Mat &srcImage, Size cellSize, vector<float> &featureVector);// 计算LBP特征
void UniformLBP(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell); // 计算每个cell的特征图和特征向量
void BuildUniformPatternTable(int *table);
int GetHopCount(int i);// 获取i中0,1的跳变次数
void UniformLBP_LUT(const Mat &srcImage, Mat &LBPImage);// 使用查找表,获取LBP特征图
// 灰度不变+旋转不变+等价模式LBP(9种模式)
void ComputerLBPFeatureVector_Rotation(const Mat &srcImage, Size cellSize, vector<float> &featureVector);// 计算LBP特征
void RotationUniformLBP(const Mat &srcImage, Mat &LBPImage,vector<float> &LBPFeatureVector, int indexOfCell);// 计算特征图
int ComputerValue9(int value58);// 计算9种等价模式
uchar GetMinBinary(uchar *binary);
void Test();// 测试灰度不变+旋转不变+等价模式LBP
};
#endif
LBP.cpp
#include"LBP.h"
//srcImage:灰度图
//LBPImage:LBP图
void LBP::LBP_256(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
int heightOfLBP = srcImage.rows;
int widthOfLBP = srcImage.cols;
int depth = srcImage.depth();
int channels = srcImage.channels();
CV_Assert(depth == CV_8U && channels==1);
LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);
// 计算LBP特征图
// 扩充原图像边界,便于边界处理
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 计算LBP
int heightOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
uchar *rowPointer_ToExtended= extendedImage.data+widthOfExtendedImage+1;
uchar *rowPointer_ToLBP = LBPImage.data;
int LBPValue=0;// 每个像素的LBP值
for (int y = 1; y <= heightOfExtendedImage - 2; ++y)
{
// 列
uchar *colPointer_ToExtended = rowPointer_ToExtended;
uchar *colPointer_ToLBP = rowPointer_ToLBP;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
{
LBPValue = 0;
if (colPointer_ToExtended[0 - widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
LBPValue += 128;
if (colPointer_ToExtended[0 - widthOfExtendedImage ] >= colPointer_ToExtended[0])
LBPValue += 64;
if (colPointer_ToExtended[0 - widthOfExtendedImage +1] >= colPointer_ToExtended[0])
LBPValue += 32;
if (colPointer_ToExtended[0 +1] >= colPointer_ToExtended[0])
LBPValue += 16;
if (colPointer_ToExtended[0 + widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
LBPValue += 8;
if (colPointer_ToExtended[0 + widthOfExtendedImage] >= colPointer_ToExtended[0])
LBPValue += 4;
if (colPointer_ToExtended[0 + widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
LBPValue += 2;
if (colPointer_ToExtended[0 - 1] >= colPointer_ToExtended[0])
LBPValue += 1;
// LBP图
colPointer_ToLBP[0] = LBPValue;
// 下一个像素
colPointer_ToExtended++;
colPointer_ToLBP++;
}
// 下一行
rowPointer_ToExtended += widthOfExtendedImage;
rowPointer_ToLBP += widthOfLBP;
}// end of y
}
// 使用查找表,获取LBP特征图,注意,为了方便表示特征图,58种等价模式表示为1~58,第59种混合模式表示为0
void LBP::UniformLBP_LUT(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
int heightOfLBP = srcImage.rows;
int widthOfLBP = srcImage.cols;
int depth = srcImage.depth();
int channels = srcImage.channels();
CV_Assert(depth == CV_8U && channels == 1);
LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);
// 计算LBP图
// 扩充原图像边界,便于边界处理
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// LUT
static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58};
// 计算LBP
int heightOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
uchar *rowPointer_ToExtended = extendedImage.data+widthOfExtendedImage+1;
uchar *rowPointer_ToLBP = LBPImage.data;
int LBPValue = 0;// 每个像素的LBP值
for (int y = 1; y <= heightOfExtendedImage - 2; ++y)
{
// 列
uchar *colPointer_ToExtended = rowPointer_ToExtended;
uchar *colPointer_ToLBP = rowPointer_ToLBP;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
{
// 计算LBP
LBPValue = 0;
if (colPointer_ToExtended[0 - widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
LBPValue += 128;
if (colPointer_ToExtended[0 - widthOfExtendedImage] >= colPointer_ToExtended[0])
LBPValue += 64;
if (colPointer_ToExtended[0 - widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
LBPValue += 32;
if (colPointer_ToExtended[0 + 1] >= colPointer_ToExtended[0])
LBPValue += 16;
if (colPointer_ToExtended[0 + widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
LBPValue += 8;
if (colPointer_ToExtended[0 + widthOfExtendedImage] >= colPointer_ToExtended[0])
LBPValue += 4;
if (colPointer_ToExtended[0 + widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
LBPValue += 2;
if (colPointer_ToExtended[0 - 1] >= colPointer_ToExtended[0])
LBPValue += 1;
colPointer_ToLBP[0] = table[LBPValue];
// 下一个像素
colPointer_ToExtended++;
colPointer_ToLBP++;
}
// 下一行
rowPointer_ToExtended += widthOfExtendedImage;
rowPointer_ToLBP += widthOfLBP;
}// end of y
}
//获取i中0,1的跳变次数
int LBP::GetHopCount(int i)
{
// 转换为二进制
int a[8] = { 0 };
int k = 7;
while (i)
{
// 除2取余
a[k] = i % 2;
i/=2;
--k;
}
// 计算跳变次数
int count = 0;
for (int k = 0; k<8; ++k)
{
// 注意,是循环二进制
if (a[k] != a[k + 1 == 8 ? 0 : k + 1])
{
++count;
}
}
return count;
}
// 建立等价模式表
// 这里为了便于建立LBP特征图,58种等价模式序号从1开始:1~58,第59类混合模式映射为0
void LBP::BuildUniformPatternTable(int *table)
{
if (table == NULL)
{
return;
}
memset(table, 0, 256*sizeof(int));
uchar temp = 1;
for (int i = 0; i<256; ++i)
{
if (GetHopCount(i) <= 2)
{
table[i] = temp;
temp++;
}
}
// 输出表格
//for (int i = 0; i < 256;++i)
//printf("%d,",table[i]);
}
// 2015-9-25 20:31:18,by QQ
// 获取一幅图像LBP特征
// cellSize:每个cell的大小,如16*16
void LBP::ComputerLBPFeatureVector(const Mat &srcImage, Size cellSize, vector<float> &featureVector)
{
// 设置每个窗口大小
int widthOfCell = cellSize.width;
int heightOfCell = cellSize.height;
int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的个数
int numberOfCell_Y = srcImage.rows / heightOfCell;
// 特征向量的个数
int numberOfDimension = 58 * numberOfCell_X*numberOfCell_Y;
featureVector.resize(numberOfDimension,0);
//每个cell
Mat ROI,cell,LBPImage;
IplImage iplImage;
// 计算LBP特征向量
for (int y = 0; y <= numberOfCell_Y - 1; ++y)
{
for (int x = 0; x <= numberOfCell_X - 1; ++x)
{
// 每个cell
ROI = srcImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
// 拷贝每个cell
iplImage = ROI;
cell = cvarrToMat(&iplImage, true);
// 计算
UniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
}
}
}
// Updated:2015-9-25 20:02:18,by QQ
// 计算每个cell的特征图和特征向量
// LBPImage:LBP特征图(这里为了便于建立LBP特征图,58种等价模式序号从1开始:1~58,第59类混合模式映射为0)
// LBPFeature:每幅图的LBP特征
// indexOfCell:cell索引
// 注:你可以将第59类混合模式映射为任意数值,但是设置为0能够更好突出特征,因为要突出等价模式特征,所以非等价模式设置为0比较好
void LBP::UniformLBP(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell)
{
// 参数检查,内存分配
int heightOfLBP = srcImage.rows;
int widthOfLBP = srcImage.cols;
int depth = srcImage.depth();
int channels = srcImage.channels();
CV_Assert(depth == CV_8U && channels == 1);
LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);
// 扩充原图像边界,便于边界处理
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 构建LBP 等价模式查找表
int table[256];
BuildUniformPatternTable(table);
// 计算LBP特征图
int heightOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
uchar *rowPointer_ToExtended = extendedImage.data + widthOfExtendedImage + 1;
uchar *rowPointer_ToLBP = LBPImage.data;
int LBPValue = 0;// 每个像素的LBP值
for (int y = 1; y <= heightOfExtendedImage - 2; ++y)
{
// 列
uchar *colPointer_ToExtended = rowPointer_ToExtended;
uchar *colPointer_ToLBP = rowPointer_ToLBP;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
{
// 计算一般的256维LBP值
LBPValue = 0;
if (colPointer_ToExtended[0 - widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
LBPValue += 128;
if (colPointer_ToExtended[0 - widthOfExtendedImage] >=colPointer_ToExtended[0])
LBPValue += 64;
if (colPointer_ToExtended[0 - widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
LBPValue += 32;
if (colPointer_ToExtended[0 + 1] >= colPointer_ToExtended[0])
LBPValue += 16;
if (colPointer_ToExtended[0 + widthOfExtendedImage + 1] >= colPointer_ToExtended[0])
LBPValue += 8;
if (colPointer_ToExtended[0 + widthOfExtendedImage] >= colPointer_ToExtended[0])
LBPValue += 4;
if (colPointer_ToExtended[0 + widthOfExtendedImage - 1] >= colPointer_ToExtended[0])
LBPValue += 2;
if (colPointer_ToExtended[0 - 1] >= colPointer_ToExtended[0])
LBPValue += 1;
// 计算Uniform模式(等价模式)LBP值
colPointer_ToLBP[0] = table[LBPValue];
// 下一个像素
++colPointer_ToExtended;
++colPointer_ToLBP;
}// end of x
// 下一行
rowPointer_ToExtended += widthOfExtendedImage;
rowPointer_ToLBP += widthOfLBP;
}// end of y
// 计算cell的LBP特征
uchar *imageDataOfLBP = LBPImage.data;
int numberOfPixel = LBPImage.rows*LBPImage.cols;
int index = 58 * indexOfCell;// 该cell的特征向量起始位置
int sum = 0;
for (int i = 0; i <= numberOfPixel - 1; ++i)
{
// 只统计等价模式
if (imageDataOfLBP[i] != 0)
{
// 等价模式转化为0~57,所以是imageDataOfLBP[i] - 1
++LBPFeatureVector[index + imageDataOfLBP[i] - 1];
++sum;
}
}
// 直方图归一化
for (int i = 0; i <= 57; ++i)
{
LBPFeatureVector[index + i] /= sum;
}
}
// 2015-10-25 18:06:30,by QQ
// 计算9种等价模式
int LBP::ComputerValue9(int value58)
{
int value9 = 0;
switch (value58)
{
case 1:
value9 = 1;
break;
case 2:
value9 = 2;
break;
case 4:
value9 = 3;
break;
case 7:
value9 = 4;
break;
case 11:
value9 = 5;
break;
case 16:
value9 = 6;
break;
case 22:
value9 = 7;
break;
case 29:
value9 = 8;
break;
case 58:
value9 = 9;
break;
}
return value9;
}
// 2015-9-24,by QQ
// 获取循环二进制的最小二进制模式
uchar LBP::GetMinBinary(uchar *binary)
{
// 计算8个二进制
uchar LBPValue[8] = { 0 };
for (int i = 0; i <= 7; ++i)
{
LBPValue[0] += binary[i] <<(7-i);
LBPValue[1] += binary[(i+7)%8] << (7 - i);
LBPValue[2] += binary[(i + 6) % 8] << (7 - i);
LBPValue[3] += binary[(i + 5) % 8] << (7 - i);
LBPValue[4] += binary[(i + 4) % 8] << (7 - i);
LBPValue[5] += binary[(i + 3) % 8] << (7 - i);
LBPValue[6] += binary[(i + 2) % 8] << (7 - i);
LBPValue[7] += binary[(i + 1) % 8] << (7 - i);
}
// 选择最小的
uchar minValue = LBPValue[0];
for (int i = 1; i <= 7; ++i)
{
if (LBPValue[i] < minValue)
{
minValue = LBPValue[i];
}
}
return minValue;
}
// 2015-10-21 12:03:19,by QQ
// cellSize:每个cell的大小,如16*16
void LBP::ComputerLBPFeatureVector_Rotation(const Mat &srcImage, Size cellSize, vector<float> &featureVector)
{
// 设置每个窗口大小
int widthOfCell = cellSize.width;
int heightOfCell = cellSize.height;
int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的个数
int numberOfCell_Y = srcImage.rows / heightOfCell;
// 特征向量的个数
int numberOfDimension = 9 * numberOfCell_X*numberOfCell_Y;
featureVector.resize(numberOfDimension, 0);
//每个cell
Mat ROI, cell, LBPImage;
IplImage iplImage;
// 计算LBP特征向量
for (int y = 0; y <= numberOfCell_Y - 1; ++y)
{
for (int x = 0; x <= numberOfCell_X - 1; ++x)
{
// 每个cell
ROI = srcImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
// 拷贝每个cell
iplImage = ROI;
cell = cvarrToMat(&iplImage, true);
// 计算
RotationUniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
}
}
}
// 2015-9-24,by QQ
// 计算旋转不变等价模式的LBP值
void LBP::RotationUniformLBP(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
LBPImage.create(srcImage.size(), srcImage.depth());
// 扩充图像,处理边界情况
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 构建LBP 等价模式查找表
int table[256];
BuildUniformPatternTable(table);
uchar binary[8] = { 0 };// 记录每个像素的LBP值
int heigthOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
int widthOfLBPImage = LBPImage.cols;
// 行
uchar *rowPointer_ToExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
uchar *rowPointer_ToLBPImage = LBPImage.data;
for (int y = 1; y <= heigthOfExtendedImage - 2; ++y)
{
// 列
uchar *colPointer_ToExtendedImage = rowPointer_ToExtendedImage;
uchar *colPointer_ToLBPImage = rowPointer_ToLBPImage;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x)
{
// 计算旋转不变LBP(最小的二进制模式)
binary[0] = colPointer_ToExtendedImage[0 - widthOfExtendedImage - 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[1] = colPointer_ToExtendedImage[0 - widthOfExtendedImage] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[2] = colPointer_ToExtendedImage[0 - widthOfExtendedImage + 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[3] = colPointer_ToExtendedImage[0 + 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[4] = colPointer_ToExtendedImage[0 + widthOfExtendedImage + 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[5] = colPointer_ToExtendedImage[0 + widthOfExtendedImage] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[6] = colPointer_ToExtendedImage[0 + widthOfExtendedImage - 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
binary[7] = colPointer_ToExtendedImage[0 - 1] >= colPointer_ToExtendedImage[0] ? 1 : 0;
int minValue = GetMinBinary(binary);
// 计算58种等价模式LBP
int value58=table[minValue];
// 计算9种等价模式
colPointer_ToLBPImage[0] = ComputerValue9(value58);
// 下一个像素
++colPointer_ToExtendedImage;
++colPointer_ToLBPImage;
}
// 下一行
rowPointer_ToExtendedImage += widthOfExtendedImage;
rowPointer_ToLBPImage += widthOfLBPImage;
}
// 计算cell的LBP特征
uchar *imageDataOfLBP = LBPImage.data;
int numberOfPixel = LBPImage.rows*LBPImage.cols;
int index = 9 * indexOfCell;// 该cell的特征向量起始位置
int sum = 0;
for (int i = 0; i <= numberOfPixel - 1; ++i)
{
// 只统计等价模式
if (imageDataOfLBP[i] != 0)
{
// 等价模式转化为0~8,所以是imageDataOfLBP[i] - 1
++LBPFeatureVector[index + imageDataOfLBP[i] - 1];
++sum;
}
}
// 直方图归一化
for (int i = 0; i <= 8; ++i)
{
LBPFeatureVector[index + i] /= sum;
}
}
// 2015-10-21 11:58:43,by QQ
// 验证灰度不变+旋转不变+等价模式种类
void LBP::Test()
{
uchar LBPValue[8] = { 0 };
int k = 7, j;
int temp;
LBP lbp;
int number[256] = { 0 };
int numberOfMinBinary = 0;
// 旋转不变
for (int i = 0; i < 256; ++i)
{
k = 7;
temp = i;
while (k >= 0)
{
LBPValue[k] = temp & 1;
temp = temp >> 1;
--k;
}
int minBinary = lbp.GetMinBinary(LBPValue);
// 查找有无重复的
for (j = 0; j <= numberOfMinBinary - 1; ++j)
{
if (number[j] == minBinary)
break;
}
if (j == numberOfMinBinary)
{
number[numberOfMinBinary++] = minBinary;
}
}
cout << "旋转不变一共有:"<<numberOfMinBinary <<"种"<< endl;
// LUT
static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };
cout << "旋转不变+等价模式:" << endl;
for (int i = 0; i <= numberOfMinBinary - 1; ++i)
{
cout << number[i] << " " << table[number[i]] << endl;
}
}
LBP等价模式+SVM
#define CELLSIZE_LBP 16 // LBP的窗口大小,4,8,16
#define PATH "D:/Image/Texture/Example_01/"
void LBP_SVM()
{
// 读入训练样本路径和类别
vector<string> imagePaths;
vector<int> imageClass;
string buffer;
int numberOfLine=0;
ifstream file(string(PATH) + "TrainData.txt", ios::in);
while (!file.eof())
{
if (getline(file, buffer))
{
++numberOfLine;
if (numberOfLine % 2 == 0)//读到样本类别
{
imageClass.push_back(atoi(buffer.c_str()));
}
else
{
imagePaths.push_back(buffer);
}
}
}
file.close();
// 计算样本LBP特征向量矩阵和类别矩阵
int lengthOfFeatureVector = (32 / CELLSIZE_LBP)*(64 / CELLSIZE_LBP) * 58; // 特征向量的维数
Mat featureVectorOfSample(imagePaths.size(), lengthOfFeatureVector, CV_32FC1); // 样本的特征向量矩阵
Mat classOfSample(imagePaths.size(), 1, CV_32SC1);
vector<string>::size_type numberOfSample = imagePaths.size();
Mat srcImage;
LBP lbp;
vector<float> featureVector;
for (vector<string>::size_type i = 0; i <= numberOfSample - 1; ++i)
{
// 读入图片
srcImage = imread(imagePaths[i], -1);
// 计算样本LBP特征向量
lbp.ComputerLBPFeatureVector(srcImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);
for (vector<float>::size_type j = 0; j <= lengthOfFeatureVector - 1; ++j)
{
featureVectorOfSample.at<float>(i, j) = featureVector[j];
}
classOfSample.at<int>(i, 0) = imageClass[i];
}
// 使用SVM分类器训练
// 参数设置
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);
svm->save(string(PATH)+"Classifier.xml");
// 使用训练好的分类器进行识别
vector<string> testImagePath;
ifstream testFile(string(PATH)+"TestData.txt", ios::in);// 注意要去掉最后一行的换行,否则最后一幅图片读出来就是空的
while (!testFile.eof())
{
getline(testFile, buffer);
testImagePath.push_back(buffer);
}
// 识别
Mat testImage;
vector<string>::size_type numberOfTestImage = testImagePath.size();
vector<float> featureVectorOfTestImage;
Mat _featureVectorOfTestImage(1, lengthOfFeatureVector, CV_32FC1);
char line[512];
ofstream resultOfPrediction(string(PATH)+"PredictResult.txt", ios::out);
// 注意将循环体内的耗时变量和操作提取到循环体内
for (vector<string>::size_type i = 0; i <= numberOfTestImage-1; ++i)
{
testImage = imread(testImagePath[i], -1);
// 计算LBP特征向量
lbp.ComputerLBPFeatureVector(testImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVectorOfTestImage);
for (vector<float>::size_type j = 0; j <= featureVectorOfTestImage.size() - 1; ++j)
{
_featureVectorOfTestImage.at<float>(0, j) = featureVectorOfTestImage[j];
}
int predict = svm->predict(_featureVectorOfTestImage);
sprintf(line, "%s %d\n", testImagePath[i].c_str(), predict);
resultOfPrediction << line;
}
resultOfPrediction.close();
}
LBP旋转不变,等价模式+SVM
void LBP_SVM_Rotation()
{
// 读入训练样本路径和类别
vector<string> imagePaths;
vector<int> imageClass;
string buffer;
int numberOfLine = 0;
ifstream file(string(PATH) + "TrainData.txt", ios::in);
while (!file.eof())
{
if (getline(file, buffer))
{
++numberOfLine;
if (numberOfLine % 2 == 0)//读到样本类别
{
imageClass.push_back(atoi(buffer.c_str()));
}
else
{
imagePaths.push_back(buffer);
}
}
}
file.close();
// 计算样本LBP特征向量矩阵和类别矩阵
int lengthOfFeatureVector = (32 / CELLSIZE_LBP)*(64 / CELLSIZE_LBP) * 9; // 特征向量的维数
Mat featureVectorOfSample(imagePaths.size(), lengthOfFeatureVector, CV_32FC1); // 样本的特征向量矩阵
Mat classOfSample(imagePaths.size(), 1, CV_32SC1);
vector<string>::size_type numberOfSample = imagePaths.size();
Mat srcImage;
LBP lbp;
vector<float> featureVector;
for (vector<string>::size_type i = 0; i <= numberOfSample - 1; ++i)
{
// 读入图片
srcImage = imread(imagePaths[i], -1);
// 计算样本LBP特征向量
lbp.ComputerLBPFeatureVector_Rotation(srcImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);
for (vector<float>::size_type j = 0; j <= lengthOfFeatureVector - 1; ++j)
{
featureVectorOfSample.at<float>(i, j) = featureVector[j];
}
classOfSample.at<int>(i, 0) = imageClass[i];
}
// 使用SVM分类器训练
// 参数设置
Ptr<SVM> svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::LINEAR);
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);
svm->save(string(PATH) + "Classifier.xml");
// 使用训练好的分类器进行识别
vector<string> testImagePath;
ifstream testFile(string(PATH) + "TestData.txt", ios::in);// 注意要去掉最后一行的换行,否则最后一幅图片读出来就是空的
while (!testFile.eof())
{
getline(testFile, buffer);
testImagePath.push_back(buffer);
}
// 识别
Mat testImage;
vector<string>::size_type numberOfTestImage = testImagePath.size();
vector<float> featureVectorOfTestImage;
Mat _featureVectorOfTestImage(1, lengthOfFeatureVector, CV_32FC1);
char line[512];
ofstream resultOfPrediction(string(PATH) + "PredictResult.txt", ios::out);
// 注意将循环体内的耗时变量和操作提取到循环体内
for (vector<string>::size_type i = 0; i <= numberOfTestImage - 1; ++i)
{
testImage = imread(testImagePath[i], -1);
// 计算LBP特征向量
lbp.ComputerLBPFeatureVector_Rotation(testImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVectorOfTestImage);
for (vector<float>::size_type j = 0; j <= featureVectorOfTestImage.size() - 1; ++j)
{
_featureVectorOfTestImage.at<float>(0, j) = featureVectorOfTestImage[j];
}
int predict = svm->predict(_featureVectorOfTestImage);
sprintf(line, "%s %d\n", testImagePath[i].c_str(), predict);
resultOfPrediction << line;
}
resultOfPrediction.close();
}
实验中采用了2282个32X64的正样本,2278个32X64的负样本,540个测试样本
其中,正样本从下图中截取而来
正样本是草丛,负样本是路面,分别是我中午吃完饭散步的时候,随手拍的(-_-)
实验数据可以在这里下载
下面是我实验的正确率
cell窗口大小 | 4 X 4 | 8 X 8 | 16 X 16 |
---|---|---|---|
等价模式 | 79.6% | 92.4% | 96.7% |
旋转不变,等价模式 | 88.3% | 95.9% | 97.2% |
可以看出,旋转不变+等价模式效果更好,而且16X16的效果更好,32X32的没有测试,有兴趣的读者可以试一下,看看效果怎么样。
好了,简单的LBP实现就是这样,有什么问题的,可以留言一起讨论。
非常感谢您的阅读,如果您觉得这篇文章对您有帮助,请您支付宝扫码支持作者,多谢啦 :-)