参考博客:LBP特征的实现及LBP+SVM分类
运行环境:ubuntu18.04+opencv3.2.0 识别速度3.87ms/张
LBP特征提取网上很多,包括其他Haar特征什么的,SVM级联分类器网上也有介绍训练和使用的博客,这里用的是简单的二分类,即是或不是。
直接复制粘贴代码吧,别的也没有用,原理我也不懂,我只是完成对摄像头误拍的非人图像进行去除,背景大概是这样的。
智能摄像头对场景进行智能人脸检测,但是会有概率误检,比如错误把某动物或者某植物检测为人脸,然后拍照并显示到大屏幕上,这样效果很不好。哪怕没拍到人脸,是个后脑勺或者半个身子,但是只要主体是人还能圆过去,弄个动物或者植物这种风马牛不相及的照片上屏根本无力解释,只能凸出算法效果不好,但是提升人脸检测算法难度较大,就想到在AI检测后做一个SVM分类器,判定拍摄图片是否是人或不是人,把不是人的去掉,是人的才显示到屏幕上。
基本流程:挑选正负样本图片集 >> 读取正负样本并贴标签 >>提取LBP特征 >> 训练xml模型 >> 加载模型识别图片
整体实现的基本流程就是这样很简单,但这里需要特别提醒,就是最开始的挑选正负样本图片集这一步才是最关键的,其他的LBP特征提取理不理解原理都不影响从网上直接贴源码调用,中间的函数调用也是常用的,也就是说如果你的代码编译或者运行不正确,会报错,如果代码没报错,能够正常运行,基本上就是对的,最终识别效果不好9.9成跟数据集有关系。
我解释一下原因,上面说过背景是把人和非人的图片分开,对AI摄像头误检照片进行二次筛选,那么我训练的xml分类器要分类的所有照片,其实本质上都是我的AI经过层层计算最终的评定为“是人”的结果,也就是我们看到的“非人”和“人”在AI那都是符合“人”的特征的,不然摄像头本身就不会拍摄;比如我用svm级联分类器提取haar特征去检测误检的“盆栽”,显示结果就是把叶子部分识别为人脸,甚至你对检测出来的叶子的区域做haar特征的人眼检测,依然能够检测到符合人眼的特征,所以结论就是,所有AI摄像头输出的图片,对AI来说,都是符合人的特征的。
这里讲既然都符合人的特征,我怎么判定区分是人或不是人?
首先是AI摄像头本身的误检率就只有10%不到,如果误检率太高,应该先提升算法精确度,也就是AI自身能够识别出绝大多数“非人”的情况,只有在某些特定条件下才会被误导拍摄,这些误检的图片都应该包含这些特殊信息,否则AI不会只拍摄这些图片,毕竟更多的时间场景是不变的,要真认为盆栽是人,会一直盯着盆栽一直拍,所以真相就是当光线变化或者有风等等特殊条件的存在时,误导了AI,那么误导AI的10%的图像都必然有同一特点,把这些特点提取出来,用xml分类器t贴上"非人"标签过滤出去就好。
上面说了一大堆,总结就是训练的正负样本集一定要从AI检测后拍摄的图像中来,私自添加其他数据集的,会导致xml二分类识别准确率下降,原因就是其他数据集的特征是你人眼看的,不是AI自己检测过滤的,私自添加的特征不符合我的AI对人的检测标准。比如我们通过人眼识别的"非人"照片里存在AI检测为人的“特征”,分类器把这个特征贴上“非人”的标签,在实际分类的时候,会将原来AI通过该特征检测为人的图片判定为“非人”,降低准确率。
至于用Haar还是LBP特征提取,或者其他什么的,都随便,因为二分类物体判定是或不是,本身就是特征差异特别大的才会这么简单暴力区分,要时刻谨记,检测的数据来源是AI,我们只处理AI误检的,影响AI检测的特殊条件特征都会集中在AI的误检照片里,不需要考虑更换场景后xml分类器是否试用,换个场景AI依然能够有90%以上的正确识别率,如果现在的xml效果差了,就逐步添加AI不同场景下的误检样本就可以。
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); // 计算得到最小二进制 }; #endifLBP.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, 256 * sizeof(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); } }main.c
#include <stdio.h> #include <time.h> #include <opencv2/opencv.hpp> #include <opencv/cv.h> #include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/ml/ml.hpp> #include <sys/io.h> #include <dirent.h> #include <sys/stat.h> #include <sys/time.h> #include <iostream> #include <fstream> #include <unistd.h> #include"LBP.h" #define CELLSIZE_LBP 16 // LBP的窗口大小,4,8,16,32 #define TRUE_SWATCH_PATH "/home/qushy/share/OpenCV/SVM/1data/data/test/true" //训练正样本路径 #define FALSE_SWATCH_PATH "/home/qushy/share/OpenCV/SVM/1data/data/test/false" //训练负样本路径 #define TEST_SWATCH_PATH "/home/qushy/share/OpenCV/SVM/1data/data/test/src" //识别测试样本 #define SAVE_TRUE_BUBBLE "/home/qushy/share/OpenCV/SVM/1data/data/test/bubble" //识别正样本存储路径 #define SAVE_FALSE_BUBBLE "/home/qushy/share/OpenCV/SVM/1data/data/test/nobubble"//识别负样本存储路径 using namespace std; using namespace cv; using namespace cv::ml; long what_time_is_it_now(void) { struct timeval t; gettimeofday(&t, NULL); return t.tv_sec * 1000 + t.tv_usec / 1000; } void cp_result_pic(const char* src, const char* dst) { char cmd[256] = { 0 }; sprintf(cmd, "cp %s %s", src, dst); system(cmd); } void getFiles(string path, vector<string>& files) { DIR* dir = NULL; struct dirent* pDir = NULL; string p; dir = opendir(path.c_str()); if (dir != NULL) { cout << path.c_str() << endl; while (1) { pDir = readdir(dir); if (pDir == NULL) break; if (pDir->d_type == DT_REG) { files.push_back(p.assign(path).append("/").append(pDir->d_name)); } } } closedir(dir); } //获取测试样本 void getTestBubble(vector<string>& imagePaths) { string filePath = TEST_SWATCH_PATH; vector<string> files; getFiles(filePath, files); int number = files.size(); for (int i = 0; i < number; i++) { imagePaths.push_back(files[i].c_str()); } cout << "getTestBubble " << number << endl; } //获取正样本 //并贴标签为1 void getBubble(vector<string>& imagePaths, vector<int>& imageClass) { string filePath = TRUE_SWATCH_PATH; vector<string> files; getFiles(filePath, files); int number = files.size(); for (int i = 0; i < number; i++) { imagePaths.push_back(files[i].c_str()); imageClass.push_back(1); //该样本为数字1 } cout << "getBubble " << number << endl; } //获取负样本 //并贴标签为0 void getNoBubble(vector<string>& imagePaths, vector<int>& imageClass) { string filePath = FALSE_SWATCH_PATH; vector<string> files; getFiles(filePath, files); int number = files.size(); for (int i = 0; i < number; i++) { imagePaths.push_back(files[i].c_str()); imageClass.push_back(0); //该样本为数字0 } cout << "getNoBubble " << number << endl; } void LBP_SVM_Rotation() { // 读入训练样本路径和类别 vector<string> imagePaths; vector<int> imageClass; getBubble(imagePaths, imageClass); getNoBubble(imagePaths, imageClass); // 计算样本LBP特征向量矩阵和类别矩阵 int lengthOfFeatureVector = (32 / CELLSIZE_LBP) * (64 / CELLSIZE_LBP) * 9; // 特征向量的维数 Mat featureVectorOfSample; Mat classOfSample; vector<string>::size_type numberOfSample = imagePaths.size(); Mat srcImage; LBP lbp; Mat featureVector; cout << "computer lbp feature vector rotation" <<endl; for (vector<string>::size_type i = 0; i <= numberOfSample - 1; ++i) { //cout << imagePaths[i].c_str() << endl; // 读入图片 srcImage = imread(imagePaths[i].c_str(), 0); resize(srcImage, srcImage, Size(256, 256), 0, 0, INTER_LINEAR); // 计算样本LBP特征向量 lbp.ComputeLBPFeatureVector_Rotation_Uniform(srcImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector); if (featureVector.empty()) printf("ComputeLBPFeatureVector_Rotation_Uniform faild\n"); featureVectorOfSample.push_back(featureVector); classOfSample.push_back(imageClass[i]); } // 使用SVM分类器训练 // 参数设置 Ptr<SVM> svm = SVM::create(); svm->setType(SVM::C_SVC); svm->setKernel(SVM::RBF);//核函数 svm->setDegree(0); svm->setGamma(1); svm->setCoef0(0); svm->setC(1); svm->setNu(0); svm->setP(0); svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 100, 1e-6)); cout << "training svm ..." << endl; svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample); svm->save("Classifier.xml"); cout << "save svm Classifier.xml" << endl; } void LBP_SVM_Rotation_Test() { Ptr<ml::SVM>svm = ml::SVM::load("Classifier.xml"); // 使用训练好的分类器进行识别 vector<string> testImagePath; getTestBubble(testImagePath); // 识别 LBP lbp; int lengthOfFeatureVector = (32 / CELLSIZE_LBP) * (64 / CELLSIZE_LBP) * 9; vector<string>::size_type numberOfTestImage = testImagePath.size(); Mat featureVectorOfTestImage; int result_true = 0; int result_false = 0; cout << "computer predict result:" << endl; long t_start = what_time_is_it_now(); // 注意将循环体内的耗时变量和操作提取到循环体内 for (vector<string>::size_type i = 0; i <= numberOfTestImage - 1; ++i) { Mat testImage = imread(testImagePath[i].c_str(), 0); resize(testImage, testImage, Size(256, 256), 0, 0, INTER_LINEAR); //cout << testImagePath[i].c_str() << endl; // 计算LBP特征向量 lbp.ComputeLBPFeatureVector_Rotation_Uniform(testImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVectorOfTestImage); int predict = (int)svm->predict(featureVectorOfTestImage); if (predict) { result_true++; //cp_result_pic(testImagePath[i].c_str(), SAVE_TRUE_BUBBLE); } else { result_false++; //cp_result_pic(testImagePath[i].c_str(), SAVE_FALSE_BUBBLE); } } long t_end = what_time_is_it_now(); cout << "result true " << result_true << endl; cout << "result false " << result_false << endl; cout << "total " << t_end - t_start << " ms" << " average " << (t_end - t_start) * 1.0 / numberOfTestImage << " ms" << endl; } int main() { LBP_SVM_Rotation(); LBP_SVM_Rotation_Test(); return 0; }