前言
LBP这篇博客发表了有一年多的时间了,当时是为了研究生毕业论文实验而写的,后来稍微总结了一下写了这篇博客,一年多时间里,大家提了一些宝贵的修改意见,这两天将代码重构了一下,结构更加简洁清晰,速度也有所提高,非常感谢网友@LiuXueFeiNan 提出的宝贵意见,也希望大家能够提出更多更好的建议。
目录
#LBP原理
LBP的原理比较简单,网上有很多很不错的文章,这里给出几篇我认为不错的文章
目标检测的图像特征提取之(二)LBP特征
LBP local binary patterns 人脸特征提取方法
如果大家想深入了解LBP,可以读一读论文
[2002 PAMI] Multiresolution gray-scale and rotation
[2006 PAMI] Face Description with Local Binary Patterns Application to Face Recognition
LBP特征向量提取的步骤
(1)计算图像中每个像素点的LBP模式(等价模式,或者旋转不变+等价模式)。
(2)然后计算每个cell的LBP特征值直方图,然后对该直方图进行归一化处理(每个cell中,对于每个bin,h[i]/=sum,sum就是一副图像中所有等价类的个数)。
(3)最后将得到的每个cell的统计直方图进行连接成为一个特征向量,也就是整幅图的LBP纹理特征向量;
然后便可利用SVM或者其他机器学习算法进行分类识别了。
LBP的实现
LBP.h
//
// LBP.h (2.0)
// 2015-6-30,by QQ
//
// Please contact me if you find any bugs, or have any suggestions.
// Contact:
// Telephone:17761745857
// Email:654393155@qq.com
// Blog: http://blog.csdn.net/qianqing13579
//
// updated 2016-12-12 01:12:55 by QQ, LBP 1.1,GetMinBinary()函数修改为查找表,提高了计算速度
// updated 2016-12-13 14:41:58 by QQ, LBP 2.0,先计算整幅图像的LBP特征图,然后计算每个cell的LBP直方图
#ifndef LBP_H
#define LBP_H
#include “opencv2/opencv.hpp”
#include<vector>
using namespace std;
using namespace cv;
class LBP
{
public:
// 计算基本的256维LBP特征向量
void ComputeLBPFeatureVector_256(const Mat &srcImage, Size cellSize,Mat &featureVector);
void ComputeLBPImage_256(const Mat &srcImage, Mat &LBPImage);// 计算256维LBP特征图
// 计算灰度不变+等价模式LBP特征向量(58种模式)
void ComputeLBPFeatureVector_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector);
void ComputeLBPImage_Uniform(const Mat &srcImage, Mat &LBPImage);// 计算等价模式LBP特征图
// 计算灰度不变+旋转不变+等价模式LBP特征向量(9种模式)
void ComputeLBPFeatureVector_Rotation_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector);
void ComputeLBPImage_Rotation_Uniform(const Mat &srcImage, Mat &LBPImage); // 计算灰度不变+旋转不变+等价模式LBP特征图,使用查找表
// Test
void Test();// 测试灰度不变+旋转不变+等价模式LBP
void TestGetMinBinaryLUT();
private:
void BuildUniformPatternTable(int *table); // 计算等价模式查找表
int GetHopCount(int i);// 获取i中0,1的跳变次数
void ComputeLBPImage_Rotation_Uniform_2(const Mat &srcImage, Mat &LBPImage);// 计算灰度不变+旋转不变+等价模式LBP特征图,不使用查找表
int ComputeValue9(int value58);// 计算9种等价模式
int GetMinBinary(int binary);// 通过LUT计算最小二进制
uchar GetMinBinary(uchar *binary); // 计算得到最小二进制
};
#endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
LBP.cpp
#include"LBP.h"
//获取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)
{
// 注意,是循环二进制,所以需要判断是否为8
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)
{
memset(table, 0, 256sizeof(int));
uchar temp = 1;
for (int i = 0; i<256; ++i)
{
if (GetHopCount(i) <= 2)
{
table[i] = temp;
temp++;
}
}
}
void LBP::ComputeLBPFeatureVector_256(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
Mat LBPImage;
ComputeLBPImage_256(srcImage,LBPImage);
// 计算cell个数
int widthOfCell = cellSize.width;
int heightOfCell = cellSize.height;
int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的个数
int numberOfCell_Y = srcImage.rows / heightOfCell;
// 特征向量的个数
int numberOfDimension = 256 * numberOfCell_X*numberOfCell_Y;
featureVector.create(1, numberOfDimension, CV_32FC1);
featureVector.setTo(Scalar(0));
// 计算LBP特征向量
int stepOfCell=srcImage.cols;
int pixelCount = cellSize.width*cellSize.height;
float *dataOfFeatureVector=(float *)featureVector.data;
// cell的特征向量在最终特征向量中的起始位置
int index = -256;
for (int y = 0; y <= numberOfCell_Y - 1; ++y)
{
for (int x = 0; x <= numberOfCell_X - 1; ++x)
{
index+=256;
// 计算每个cell的LBP直方图
Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
uchar *rowOfCell=cell.data;
for(int y_Cell=0;y_Cell<=cell.rows-1;++y_Cell,rowOfCell+=stepOfCell)
{
uchar *colOfCell=rowOfCell;
for(int x_Cell=0;x_Cell<=cell.cols-1;++x_Cell,++colOfCell)
{
++dataOfFeatureVector[index + colOfCell[0]];
}
}
// 一定要归一化!否则分类器计算误差很大
for (int i = 0; i <= 255; ++i)
dataOfFeatureVector[index + i] /= pixelCount;
}
}
}
//srcImage:灰度图
//LBPImage:LBP图
void LBP::ComputeLBPImage_256(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
LBPImage.create(srcImage.size(), srcImage.type());
// 扩充原图像边界,便于边界处理
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 计算LBP特征图
int heightOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
int widthOfLBP=LBPImage.cols;
uchar *rowOfExtendedImage= extendedImage.data+widthOfExtendedImage+1;
uchar *rowOfLBPImage = LBPImage.data;
for (int y = 1; y <= heightOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBP)
{
// 列
uchar *colOfExtendedImage = rowOfExtendedImage;
uchar *colOfLBPImage = rowOfLBPImage;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x,++colOfExtendedImage,++colOfLBPImage)
{
// 计算LBP值
int LBPValue = 0;
if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
LBPValue += 128;
if (colOfExtendedImage[0 - widthOfExtendedImage ] >= colOfExtendedImage[0])
LBPValue += 64;
if (colOfExtendedImage[0 - widthOfExtendedImage +1] >= colOfExtendedImage[0])
LBPValue += 32;
if (colOfExtendedImage[0 +1] >= colOfExtendedImage[0])
LBPValue += 16;
if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
LBPValue += 8;
if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
LBPValue += 4;
if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
LBPValue += 2;
if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
LBPValue += 1;
colOfLBPImage[0] = LBPValue;
} // x
}// y
}
// cellSize:每个cell的大小,如16*16
void LBP::ComputeLBPFeatureVector_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
Mat LBPImage;
ComputeLBPImage_Uniform(srcImage,LBPImage);
// 计算cell个数
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.create(1, numberOfDimension, CV_32FC1);
featureVector.setTo(Scalar(0));
// 计算LBP特征向量
int stepOfCell=srcImage.cols;
int index = -58;// cell的特征向量在最终特征向量中的起始位置
float *dataOfFeatureVector=(float *)featureVector.data;
for (int y = 0; y <= numberOfCell_Y - 1; ++y)
{
for (int x = 0; x <= numberOfCell_X - 1; ++x)
{
index+=58;
// 计算每个cell的LBP直方图
Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
uchar *rowOfCell=cell.data;
int sum = 0; // 每个cell的等价模式总数
for(int y_Cell=0;y_Cell<=cell.rows-1;++y_Cell,rowOfCell+=stepOfCell)
{
uchar *colOfCell=rowOfCell;
for(int x_Cell=0;x_Cell<=cell.cols-1;++x_Cell,++colOfCell)
{
if(colOfCell[0]!=0)
{
// 在直方图中转化为0~57,所以是colOfCell[0] - 1
++dataOfFeatureVector[index + colOfCell[0]-1];
++sum;
}
}
}
// 一定要归一化!否则分类器计算误差很大
for (int i = 0; i <= 57; ++i)
dataOfFeatureVector[index + i] /= sum;
}
}
}
// 计算等价模式LBP特征图,为了方便表示特征图,58种等价模式表示为1~58,第59种混合模式表示为0
// 注:你可以将第59类混合模式映射为任意数值,因为要突出等价模式特征,所以非等价模式设置为0比较好
void LBP::ComputeLBPImage_Uniform(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
LBPImage.create(srcImage.size(), srcImage.type());
// 计算LBP图
// 扩充原图像边界,便于边界处理
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 构建LBP 等价模式查找表
//int table[256];
//BuildUniformPatternTable(table);
// LUT(256种每一种模式对应的等价模式)
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;
int widthOfLBP=LBPImage.cols;
uchar *rowOfExtendedImage = extendedImage.data+widthOfExtendedImage+1;
uchar *rowOfLBPImage = LBPImage.data;
for (int y = 1; y <= heightOfExtendedImage - 2; ++y,rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBP)
{
// 列
uchar *colOfExtendedImage = rowOfExtendedImage;
uchar *colOfLBPImage = rowOfLBPImage;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
{
// 计算LBP值
int LBPValue = 0;
if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
LBPValue += 128;
if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0])
LBPValue += 64;
if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0])
LBPValue += 32;
if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0])
LBPValue += 16;
if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
LBPValue += 8;
if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
LBPValue += 4;
if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
LBPValue += 2;
if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
LBPValue += 1;
colOfLBPImage[0] = table[LBPValue];
} // x
}// y
}
// 计算9种等价模式,等价模式编号也是从1开始:1~9
int LBP::ComputeValue9(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;
}
int LBP::GetMinBinary(int binary)
{
static const int miniBinaryLUT[256] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15, 1, 17, 9, 19, 5,
21, 11, 23, 3, 25, 13, 27, 7, 29, 15, 31, 1, 9, 17, 25, 9, 37, 19, 39, 5, 37, 21, 43, 11, 45,
23, 47, 3, 19, 25, 51, 13, 53, 27, 55, 7, 39, 29, 59, 15, 61, 31, 63, 1, 5, 9, 13, 17, 21, 25,
29, 9, 37, 37, 45, 19, 53, 39, 61, 5, 21, 37, 53, 21, 85, 43, 87, 11, 43, 45, 91, 23, 87, 47, 95,
3, 11, 19, 27, 25, 43, 51, 59, 13, 45, 53, 91, 27, 91, 55, 111, 7, 23, 39, 55, 29, 87, 59, 119, 15,
47, 61, 111, 31, 95, 63, 127, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 9, 25, 37,
39, 37, 43, 45, 47, 19, 51, 53, 55, 39, 59, 61, 63, 5, 13, 21, 29, 37, 45, 53, 61, 21, 53, 85,
87, 43, 91, 87, 95, 11, 27, 43, 59, 45, 91, 91, 111, 23, 55, 87, 119, 47, 111, 95, 127, 3,
7, 11, 15, 19, 23, 27, 31, 25, 39, 43, 47, 51, 55, 59, 63, 13, 29, 45, 61, 53, 87, 91, 95, 27, 59,
91, 111, 55, 119, 111, 127, 7, 15, 23, 31, 39, 47, 55, 63, 29, 61, 87, 95, 59, 111, 119, 127, 15, 31, 47, 63,
61, 95, 111, 127, 31, 63, 95, 127, 63, 127, 127, 255};
return miniBinaryLUT[binary];
}
// 获取循环二进制的最小二进制模式
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;
}
// cellSize:每个cell的大小,如16*16
void LBP::ComputeLBPFeatureVector_Rotation_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
Mat LBPImage;
ComputeLBPImage_Rotation_Uniform(srcImage,LBPImage);
// 计算cell个数
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.create(1, numberOfDimension, CV_32FC1);
featureVector.setTo(Scalar(0));
// 计算LBP特征向量
int stepOfCell=srcImage.cols;
int index = -9;// cell的特征向量在最终特征向量中的起始位置
float *dataOfFeatureVector=(float *)featureVector.data;
for (int y = 0; y <= numberOfCell_Y - 1; ++y)
{
for (int x = 0; x <= numberOfCell_X - 1; ++x)
{
index+=9;
// 计算每个cell的LBP直方图
Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
uchar *rowOfCell=cell.data;
int sum = 0; // 每个cell的等价模式总数
for(int y_Cell=0;y_Cell<=cell.rows-1;++y_Cell,rowOfCell+=stepOfCell)
{
uchar *colOfCell=rowOfCell;
for(int x_Cell=0;x_Cell<=cell.cols-1;++x_Cell,++colOfCell)
{
if(colOfCell[0]!=0)
{
// 在直方图中转化为0~8,所以是colOfCell[0] - 1
++dataOfFeatureVector[index + colOfCell[0]-1];
++sum;
}
}
}
// 直方图归一化
for (int i = 0; i <= 8; ++i)
dataOfFeatureVector[index + i] /= sum;
}
}
}
void LBP::ComputeLBPImage_Rotation_Uniform(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
LBPImage.create(srcImage.size(), srcImage.type());
// 扩充图像,处理边界情况
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 构建LBP 等价模式查找表
//int table[256];
//BuildUniformPatternTable(table);
// 查找表
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 };
int heigthOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
int widthOfLBPImage = LBPImage.cols;
uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
uchar *rowOfLBPImage = LBPImage.data;
for (int y = 1; y <= heigthOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBPImage)
{
// 列
uchar *colOfExtendedImage = rowOfExtendedImage;
uchar *colOfLBPImage = rowOfLBPImage;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
{
// 计算LBP值
int LBPValue = 0;
if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
LBPValue += 128;
if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0])
LBPValue += 64;
if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0])
LBPValue += 32;
if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0])
LBPValue += 16;
if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
LBPValue += 8;
if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
LBPValue += 4;
if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
LBPValue += 2;
if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
LBPValue += 1;
int minValue = GetMinBinary(LBPValue);
// 计算58种等价模式LBP
int value58 = table[minValue];
// 计算9种等价模式
colOfLBPImage[0] = ComputeValue9(value58);
}
}
}
void LBP::ComputeLBPImage_Rotation_Uniform_2(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
LBPImage.create(srcImage.size(), srcImage.type());
// 扩充图像,处理边界情况
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// 构建LBP 等价模式查找表
//int table[256];
//BuildUniformPatternTable(table);
// 通过查找表
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 };
uchar binary[8] = { 0 };// 记录每个像素的LBP值
int heigthOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
int widthOfLBPImage = LBPImage.cols;
uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
uchar *rowOfLBPImage = LBPImage.data;
for (int y = 1; y <= heigthOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBPImage)
{
// 列
uchar *colOfExtendedImage = rowOfExtendedImage;
uchar *colOfLBPImage = rowOfLBPImage;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
{
// 计算旋转不变LBP(最小的二进制模式)
binary[0] = colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0] ? 1 : 0;
binary[1] = colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0] ? 1 : 0;
binary[2] = colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0] ? 1 : 0;
binary[3] = colOfExtendedImage[0 + 1] >= colOfExtendedImage[0] ? 1 : 0;
binary[4] = colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0] ? 1 : 0;
binary[5] = colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0] ? 1 : 0;
binary[6] = colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0] ? 1 : 0;
binary[7] = colOfExtendedImage[0 - 1] >= colOfExtendedImage[0] ? 1 : 0;
int minValue = GetMinBinary(binary);
// 计算58种等价模式LBP
int value58=table[minValue];
// 计算9种等价模式
colOfLBPImage[0] = ComputeValue9(value58);
}
}
}
// 验证灰度不变+旋转不变+等价模式种类
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 };
for (int i = 0; i <= numberOfMinBinary - 1; ++i)
{
cout << "旋转不变的LBP:"<<number[i] << " "<<"对应的等价模式:" << table[number[i]] << endl;
}
}
void LBP::TestGetMinBinaryLUT()
{
for (int i = 0; i <= 255; ++i)
{
uchar a[8] = { 0 };
int k = 7;
int j = i;
while (j)
{
// 除2取余
a[k] = j % 2;
j /= 2;
–k;
}
uchar minBinary=GetMinBinary(a);
printf("%d,",minBinary);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
测试程序
SVMTest.h
//
// SVMTest.h
// 2016-12-12,by QQ
//
// Please contact me if you find any bugs, or have any suggestions.
// Contact:
// Telephone:15366105857
// Email:654393155@qq.com
// Blog: http://blog.csdn.net/qianqing13579
//
#ifndef SVMTEST
#define SVMTEST
#include “opencv2/ml.hpp”
//#include"…/Utility/CommonUtility.h"
//#include"…/Utility/LogInterface.h"
#include<fstream>
#include"LBP.h"
using namespace cv::ml;
// if you do not need log,comment it,just like :#define LOG_WARN_SVM_TEST(…) //LOG4CPLUS_MACRO_FMT_BODY (“SVMTest”, WARN_LOG_LEVEL, VA_ARGS)
#define LOG_DEBUG_SVM_TEST(…) //LOG4CPLUS_MACRO_FMT_BODY (“SVMTest”, DEBUG_LOG_LEVEL, VA_ARGS)
#define LOG_ERROR_SVM_TEST(…) //LOG4CPLUS_MACRO_FMT_BODY (“SVMTest”, ERROR_LOG_LEVEL, VA_ARGS)
#define LOG_INFO_SVM_TEST(…) //LOG4CPLUS_MACRO_FMT_BODY (“SVMTest”, INFO_LOG_LEVEL, VA_ARGS)
#define LOG_WARN_SVM_TEST(…) //LOG4CPLUS_MACRO_FMT_BODY (“SVMTest”, WARN_LOG_LEVEL, VA_ARGS)
//#define CONFIG_FILE “./Resource/Configuration.xml”
#define CELL_SIZE 16
class SVMTest
{
public:
SVMTest(const string &_trainDataFileList,
const string &_testDataFileList,
const string &_svmModelFilePath,
const string &_predictResultFilePath,
SVM::Types svmType, // See SVM::Types. Default value is SVM::C_SVC.
SVM::KernelTypes kernel,
double c, // For SVM::C_SVC, SVM::EPS_SVR or SVM::NU_SVR. Default value is 0.
double coef, // For SVM::POLY or SVM::SIGMOID. Default value is 0.
double degree, // For SVM::POLY. Default value is 0.
double gamma, // For SVM::POLY, SVM::RBF, SVM::SIGMOID or SVM::CHI2. Default value is 1.
double nu, // For SVM::NU_SVC, SVM::ONE_CLASS or SVM::NU_SVR. Default value is 0.
double p // For SVM::EPS_SVR. Default value is 0.
);
bool Initialize();
virtual ~SVMTest();
void Train();
void Predict();
private:
string trainDataFileList;
string testDataFileList;
string svmModelFilePath;
string predictResultFilePath;
// SVM
Ptr<SVM> svm;
// feature extracting(HOG,LBP,Haar,etc)
LBP lbp;
};
#endif // SVMTEST
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
SVMTest.cpp
#include"SVMTest.h"
SVMTest::SVMTest(const string &_trainDataFileList,
const string &_testDataFileList,
const string &_svmModelFilePath,
const string &_predictResultFilePath,
SVM::Types svmType, // See SVM::Types. Default value is SVM::C_SVC.
SVM::KernelTypes kernel,
double c, // For SVM::C_SVC, SVM::EPS_SVR or SVM::NU_SVR. Default value is 0.
double coef, // For SVM::POLY or SVM::SIGMOID. Default value is 0.
double degree, // For SVM::POLY. Default value is 0.
double gamma, // For SVM::POLY, SVM::RBF, SVM::SIGMOID or SVM::CHI2. Default value is 1.
double nu, // For SVM::NU_SVC, SVM::ONE_CLASS or SVM::NU_SVR. Default value is 0.
double p // For SVM::EPS_SVR. Default value is 0.
):
trainDataFileList(_trainDataFileList),
testDataFileList(_testDataFileList),
svmModelFilePath(_svmModelFilePath),
predictResultFilePath(_predictResultFilePath)
{
// set svm param
svm = SVM::create();
svm->setC©;
svm->setCoef0(coef);
svm->setDegree(degree);
svm->setGamma(gamma);
svm->setKernel(kernel);
svm->setNu(nu);
svm->setP§;
svm->setType(svmType);
//svm->setTermCriteria(TermCriteria(TermCriteria::EPS, 1000, FLT_EPSILON)); // based on accuracy
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6)); // based on the maximum number of iterations
}
bool SVMTest::Initialize()
{
// initialize log
//InitializeLog(“SVMTest”);
return true;
}
SVMTest::~SVMTest()
{
}
void SVMTest::Train()
{
// 读入训练样本图片路径和类别
std::vector<string> imagePaths;
std::vector<int> imageClasses;
string line;
std::ifstream trainingData(trainDataFileList,ios::out);
while (getline(trainingData, line))
{
if(line.empty())
continue;
stringstream stream(line);
string imagePath,imageClass;
stream>>imagePath;
stream>>imageClass;
imagePaths.push_back(imagePath);
imageClasses.push_back(atoi(imageClass.c_str()));
}
trainingData.close();
printf("%d\n",imagePaths.size());
// extract feature
Mat featureVectorsOfSample;
Mat classOfSample;
printf(“get feature…\n”);
LOG_INFO_SVM_TEST(“get feature…”);
for(int i=0;i<=imagePaths.size()-1;++i)
{
Mat srcImage=imread(imagePaths[i],-1);
if(srcImage.empty()||srcImage.depth()!=CV_8U)
{
printf("%s srcImage.empty()||srcImage.depth()!=CV_8U!\n",imagePaths[i].c_str());
LOG_ERROR_SVM_TEST("%s srcImage.empty()||srcImage.depth()!=CV_8U!",imagePaths[i].c_str());
continue;
}
// extract feature
Mat featureVector;
lbp.ComputeLBPFeatureVector_Rotation_Uniform(srcImage,Size(CELL_SIZE,CELL_SIZE),featureVector);
if(featureVector.empty())
continue;
featureVectorsOfSample.push_back(featureVector);
classOfSample.push_back( imageClasses[i]);
printf("get feature... %f% \n",(i+1)*100.0/imagePaths.size());
LOG_INFO_SVM_TEST("get feature... %f",(i+1)*100.0/imagePaths.size());
}
printf(“get feature done!\n”);
LOG_INFO_SVM_TEST(“get feature done!”);
// train
printf("training...\n");
LOG_INFO_SVM_TEST("training...");
double time1, time2;
time1 = getTickCount();
svm->train(featureVectorsOfSample, ROW_SAMPLE, classOfSample);
time2 = getTickCount();
printf("训练时间:%f\n", (time2 - time1)*1000. / getTickFrequency());
LOG_INFO_SVM_TEST("训练时间:%f", (time2 - time1)*1000. / getTickFrequency());
printf("training done!\n");
LOG_INFO_SVM_TEST("training done!");
// save model
svm->save(svmModelFilePath);
}
void SVMTest::Predict()
{
// predict
std::vector<string> testImagePaths;
std::vector<int> testImageClasses;
string line;
std::ifstream testData(testDataFileList,ios::out);
while (getline(testData, line))
{
if(line.empty())
continue;
stringstream stream(line);
string imagePath,imageClass;
stream>>imagePath;
stream>>imageClass;
testImagePaths.push_back(imagePath);
testImageClasses.push_back(atoi(imageClass.c_str()));
}
testData.close();
printf("predicting...\n");
LOG_INFO_SVM_TEST("predicting...");
int numberOfRight_0=0;
int numberOfError_0=0;
int numberOfRight_1=0;
int numberOfError_1=0;
std::ofstream fileOfPredictResult(predictResultFilePath,ios::out); //最后识别的结果
double sum_Predict=0,sum_ExtractFeature=0;
char line2[256]={0};
for (int i = 0; i < testImagePaths.size() ; ++i)
{
Mat srcImage = imread(testImagePaths[i], -1);
if(srcImage.empty()||srcImage.depth()!=CV_8U)
{
printf("%s srcImage.empty()||srcImage.depth()!=CV_8U!\n",testImagePaths[i].c_str());
LOG_ERROR_SVM_TEST("%s srcImage.empty()||srcImage.depth()!=CV_8U!",testImagePaths[i].c_str());
continue;
}
// extract feature
double time1_ExtractFeature = getTickCount();
Mat featureVectorOfTestImage;
lbp.ComputeLBPFeatureVector_Rotation_Uniform(srcImage,Size(CELL_SIZE,CELL_SIZE),featureVectorOfTestImage);
if(featureVectorOfTestImage.empty())
continue;
double time2_ExtractFeature = getTickCount();
sum_ExtractFeature+=(time2_ExtractFeature - time1_ExtractFeature) * 1000 / getTickFrequency();
//对测试图片进行分类并写入文件
double time1_Predict = getTickCount();
int predictResult = svm->predict(featureVectorOfTestImage);
double time2_Predict = getTickCount();
sum_Predict += (time2_Predict - time1_Predict) * 1000 / getTickFrequency();
sprintf(line2, "%s %d\n", testImagePaths[i].c_str(), predictResult);
fileOfPredictResult << line2;
LOG_INFO_SVM_TEST("%s %d", testImagePaths[i].c_str(), predictResult);
// 0
if((testImageClasses[i]==0)&&(predictResult==0))
{
++numberOfRight_0;
}
if((testImageClasses[i]==0)&&(predictResult!=0))
{
++numberOfError_0;
}
// 1
if((testImageClasses[i]==1)&&(predictResult==1))
{
++numberOfRight_1;
}
if((testImageClasses[i]==1)&&(predictResult!=1))
{
++numberOfError_1;
}
printf("predicting...%f%\n", 100.0*(i+1)/testImagePaths.size());
}
printf("predicting done!\n");
LOG_INFO_SVM_TEST("predicting done!");
printf("extract feature time:%f\n", sum_ExtractFeature / testImagePaths.size());
LOG_INFO_SVM_TEST("extract feature time:%f", sum_ExtractFeature / testImagePaths.size());
sprintf(line2, "extract feature time:%f\n", sum_ExtractFeature / testImagePaths.size());
fileOfPredictResult << line2;
printf("predict time:%f\n", sum_Predict / testImagePaths.size());
LOG_INFO_SVM_TEST("predict time:%f", sum_Predict / testImagePaths.size());
sprintf(line2, "predict time:%f\n", sum_Predict / testImagePaths.size());
fileOfPredictResult << line2;
// 0
double accuracy_0=(100.0*(numberOfRight_0)) / (numberOfError_0+numberOfRight_0);
printf("0:%f\n",accuracy_0);
LOG_INFO_SVM_TEST("0:%f",accuracy_0);
sprintf(line2, "0:%f\n", accuracy_0);
fileOfPredictResult << line2;
// 1
double accuracy_1=(100.0*numberOfRight_1) /(numberOfError_1+numberOfRight_1);
printf("1:%f\n",accuracy_1);
LOG_INFO_SVM_TEST("1:%f", accuracy_1);
sprintf(line2, "1:%f\n",accuracy_1);
fileOfPredictResult << line2;
// accuracy
double accuracy_All=(100.0*(numberOfRight_1+numberOfRight_0)) /(numberOfError_0+numberOfRight_0+numberOfError_1+numberOfRight_1);
printf("accuracy:%f\n", accuracy_All);
LOG_INFO_SVM_TEST("accuracy:%f", accuracy_All);
sprintf(line2, "accuracy:%f\n", accuracy_All);
fileOfPredictResult << line2;
fileOfPredictResult.close();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
程序中加入了日志,我已经在程序中注释掉了,这部分大家可以忽略。
main.cpp
#include"SVMTest.h"
int main(int argc, char *argv[])
{
string root="/home/qq/Image2/Texture/SVMTest/";
SVMTest svmTest(root+"Train.txt", // train filelist
root+"Test.txt", // test filelist
root+"Classifier.xml", // classifier
root+"PredictResult.txt", //predict result
SVM::C_SVC, // svmType
SVM::LINEAR, // kernel
1, // c
0, // coef
0, // degree
1, // gamma
0, // nu
0); // p
if(!svmTest.Initialize())
{
printf("initialize failed!\n");
//LOG_ERROR_SVM_TEST("initialize failed!");
return ;
}
svmTest.Train();
svmTest.Predict();
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
#实验结果与分析
实验环境:CPU: i7-4790K,内存:16G,OS:Ubuntu 14.04,QT 5.5,OpenCV 3.1
注:很多网友说程序中的SVM报错,因为3.1中的SVM用法和2.4用法不同,所以如果你用的是2.4版本,需要更新到3.1的才可以使用.
实验中采用了2282个32X64的正样本,2278个32X64的负样本,540个测试样本
其中,正样本从下图中截取而来
负样本从下图中截取而来
正样本是草丛,负样本是路面,是我中午吃完饭散步的时候,随手拍的(-_-)
实验数据可以在这里下载
下面是我的实验结果
窗口大小 | 原始256维 | 等价模式 | 旋转不变等价模式 |
---|---|---|---|
8 x 8 | 91.67% | 93.89% | 94.26% |
16 x 16 | 95.74% | 95.74% | 98.52% |
32 x 32 | 98.51% | 98.52% | 98.70% |
可以看出,本实验中窗口大小为32x32的旋转不变的等价模式识别率最高。
注意:
程序中使用的Train.txt和Test.txt每一行的格式为:
图像的绝对路径 类别
如:
/home/qq/Image2/Texture/0/Grass_0010.jpg 0
速度测试:
重构后的LBP,GetMinBinary()函数采用了查找表,实验中选取窗口大小16x16,提取一幅32X64图像的旋转不变等价模式特征为0.08ms,而没用查找表的时间为0.31ms,速度的提高还是很大的。
Last Updated: 2016-12-13 19:31:07
非常感谢您的阅读,如果您觉得这篇文章对您有帮助,欢迎扫码进行赞赏。
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-258a4616f7.css" rel="stylesheet">
</div>