【C/C++】基于opencv的分类器:SVM、BP、KNN及Bayes分类器

        学习机器学习课程,你肯定会接触到一些传统的分类器,如:SVM、BP、KNN及Bayes分类器。这些分类器在opencv中都能找到相应函数。读研时,用过这四个分类器作过对比试验。工作后,也尝试过用他们来识别电池的铜铝极耳。但是,识别效果不是很好,原因是存在极耳反光现象,造成铜铝极耳高度相似。还有,那就是其识别效果与手工设计的特征向量密切相关,很难设计合适的特征。再有,极耳在ROI区域的位置有差异时,其识别效果也会有很大影响。所以,在这方面的应用上,其稳定性不会很好。后来,在物体分类上,我们基本都是采用卷积神经网来做。

BP神经网络

待写

//BP神经网络,训练部分
void MyBPtrain(Mat& samples, Mat& labels)
{
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	label.convertTo(label, CV_32FC1);
	int samrow = sample.rows;
	int samcol = sample.cols;
	Mat labelsMat(samrow, 1, CV_32FC1);
	for (int i = 0; i< samrow; i++)
	{
		if (label.at<float>(i, 0) == 1.0)
		{
			labelsMat.at<float>(i, 0) = 1;

		}
		else if (label.at<float>(i, 0) == -1.0)
		{
			labelsMat.at<float>(i, 0) = 0;
		}
	}

	// BP 模型创建和参数设置
	Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::create();

	Mat layers_size = (Mat_<int>(1, 3) << samcol, 6, 1); // samcol维点,1维输出
	bp->setLayerSizes(layers_size);

	bp->setTrainMethod(ml::ANN_MLP::BACKPROP, 0.1, 0.1);
	bp->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM);
	bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, /*FLT_EPSILON*/1e-6));
	bp->train(sample, ml::ROW_SAMPLE, labelsMat);
	std::cout << "训练完成" << std::endl;
	bp->save("BP.xml");
	std::cout << "保存完毕" << std::endl;
}
//BP神经网络,测试部分
void MyBPtest(Mat& samples, Mat&Demonstration, Mat& labels)
{

	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	label.convertTo(label, CV_32FC1);
	Mat responseMat;
	Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::load("BP.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}

		bp->predict(e, responseMat);
		float* p = responseMat.ptr<float>(0);
		if (p[0] >0.5&& label.at<float>(i, 0) == 1)
		{
			True0++;
		}
		else if (p[0]<0.5 && label.at<float>(i, 0) == -1)
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;
	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		bp->predict(sampleMat, responseMat);
		float* p = responseMat.ptr<float>(0);
		if (p[0] < 0.5&&label0.at<float>(i, 0) == -1.0)
		{
			True1++;
		}
		if (p[0] > 0.5&&label0.at<float>(i, 0) == 1.0)
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;

}

SVM分类器

待写

//线性核svm,训练部分
void MySvmtrain(Mat& samples, Mat& labels)
{
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	//创建SVM分类器
	Ptr<cv::ml::SVM> svm = cv::ml::SVM::create();
	svm->setType(cv::ml::SVM::C_SVC);
	svm->setKernel(cv::ml::SVM::LINEAR);
	//svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 10000, 0.0001));
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 10000, 0.001));
	//模型训练
	svm->train(sample, cv::ml::ROW_SAMPLE, label);
	std::cout << "训练完成" << std::endl;
	//模型保存
	svm->save("SVM.xml");
	std::cout << "保存完毕" << std::endl;
}
//线性核svm,测试部分
void MySvmtest(Mat& samples, Mat&Demonstration, Mat& labels)
{
	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	//label.convertTo(label, CV_32FC1);
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	SVM_params = cv::ml::SVM::load("SVM.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		response = SVM_params->predict(e);
		if (response == label.at<float>(i, 0))
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;

	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);
	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float  response = SVM_params->predict(sampleMat);

		if (response == label0.at<float>(i, 0))
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}

KNN分类器

待写

//KNN模型,训练部分
void MyKNNtrain(int k, Mat& samples, Mat& labels)
{
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	int K = k;
	Ptr<cv::ml::KNearest> knn = cv::ml::KNearest::create();
	knn->setDefaultK(K);
	knn->setIsClassifier(true);

	// 设置训练数据
	Ptr<cv::ml::TrainData> tData = cv::ml::TrainData::create(sample, cv::ml::ROW_SAMPLE, label);
	knn->train(tData);
	std::cout << "训练完毕" << std::endl;
	knn->save("KNN.txt");
	std::cout << "保存完毕" << std::endl;
}
//KNN模型,测试部分
void MyKNNtest(Mat& samples, Mat&Demonstration, Mat& labels)
{

	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	Ptr<cv::ml::KNearest> knn = cv::Algorithm::load<cv::ml::KNearest>("KNN.txt");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		response = knn->predict(e);
		if (response == label.at<float>(i, 0))
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;
	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float  response = knn->predict(sampleMat);

		if (response == label0.at<float>(i, 0))
		{
			True1++;
		}
		
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}

Bayes分类器

待写

//正态贝叶斯分类器,训练部分
void MyBayestrain(Mat& samples, Mat& labels)
{

	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k <cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	Ptr<cv::ml::NormalBayesClassifier> model = cv::ml::NormalBayesClassifier::create();
	model->train(sample, cv::ml::ROW_SAMPLE, label);
	std::cout << "训练完成" << std::endl;
	model->save("Bayes.xml");
	std::cout << "保存完毕" << std::endl;
}
//正态贝叶斯分类器,测试部分
void MyBayestest(Mat& samples, Mat&Demonstration, Mat& labels)
{

	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	label.convertTo(label, CV_32FC1);
	Mat responseMat;
	Ptr<ml::NormalBayesClassifier> model = ml::NormalBayesClassifier::load("Bayes.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		float r = model->predict(e);
		if (r == label.at<float>(i, 0))
		{
			True0++;
		}


	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;

	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float r = model->predict(sampleMat);

		if (r == label0.at<float>(i, 0))
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}

测试实验

待写

附加代码

训练部分代码(exe)

头文件

LBP.h 

#pragma once
#include "stdafx.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 ComputerLBPFeatureVector_Rotation58(const Mat &srcImage, Size cellSize, vector<float> &featureVector);
	void RotationUniformLBP58(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell);
	void Test();// 测试灰度不变+旋转不变+等价模式LBP
};

#endif

 modelTraining.h

#pragma once
#include"trainFeatureExtraction.h"
#include "stdafx.h"
#include <string>   
#include <iostream>
#include <stdlib.h>
#include<vector>
#include<stdio.h>
#include<math.h>

#define c1  2     //加速度因子
#define c2  2     //加速度因子
#define maxgen 10  // 迭代次数
#define sizepop 40 // 种群规模
#define popmax 10// 个体最大取值
#define popmin 0.0001 // 个体最小取值
#define Vmax 0.5 // 速度最大值
#define Vmin -0.5 //速度最小值
#define dim 2 // 粒子的维数
#define w_start 0.9
#define w_end 0.4
#define PI 3.1415926 //圆周率
//宏定义 
//#define PosSamNO 20   //训练集正样本个数   
//#define NegSamNO 20   //训练集负样本个数
//#define testPos 20    //测试集以及验证集正样本个数   
//#define testNeg 20    //测试集以及验证集负样本个数
//#define samplesize 3  //样本列数(所提取特征的数量)
int K = 3;//k折交叉验证
float pop[sizepop][dim]; // 定义种群数组
float pop1[sizepop][dim]; // 定义种群数组
float V[sizepop][dim]; // 定义种群速度数组
float fitness[sizepop]; // 定义种群的适应度数组
float result[maxgen];  //定义存放每次迭代种群最优值的数组
float pbest[sizepop][dim];  // 个体极值的位置
float gbest[dim]; //群体极值的位置
float fitnesspbest[sizepop]; //个体极值适应度的值
float fitnessgbest; // 群体极值适应度值
float genbest[maxgen][dim]; //每一代最优值取值粒子
int var = 0;//变异选取
float fitness1;
float fitness2;
float fitness3[sizepop];
float Gamma = 0.00001, Nu = 0.00001;
using namespace std;
using namespace cv;

//特征降维
void PCAAlgorithm(const Mat&normSamples, Mat&normSamplesPCA, int K);


//利用pso_svm()为非线性核svm寻找适宜参数
void pso_svm(Mat &samples, Mat &labels);
float kFoldCrossValidation(float gamma, float c, Mat samples_, Mat label, int K);
vector<vector<int>*>  *randomIndex(int Fold, Mat label);
void MySVM(vector<float> *Accuracy, float gamma, float c, float*testlabel, float*testdata, int testrows, int testclos, float*trainlabel, float*traindata, int trainrows, int trainclos);
float * groupsExtremum(float * fit, int size);
void Mytrain(float gamma, float c, Mat& samples, Mat& labels);
void Mytest(Mat& samples, Mat&Demonstration, Mat& labels);

//线性核svm
void MySvmtrain(Mat& samples, Mat& labels);
void MySvmtest(Mat& samples, Mat&Demonstration, Mat& labels);
//KNN模型
void MyKNNtrain(int k, Mat& samples, Mat& labels);
void MyKNNtest(Mat& samples, Mat&Demonstration, Mat& labels);
//BP神经网络
void MyBPtrain(Mat& samples, Mat& labels);
void MyBPtest(Mat& samples, Mat&Demonstration, Mat& labels);
//正态贝叶斯分类器
void MyBayestrain(Mat& samples, Mat& labels);
void MyBayestest(Mat& samples, Mat&Demonstration, Mat& labels);



 trainFeatureExtraction.h

#pragma once
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <time.h>
#include"LBP.h"
using namespace std;
using namespace cv;
//宏定义 
#define PosSamNO 700   //训练集正样本个数   
#define NegSamNO 700   //训练集负样本个数
#define testPos 114   //测试集以及验证集正样本个数   
#define testNeg 114  //测试集以及验证集负样本个数
#define CELLSIZE_LBP 16 //LBP的窗口大小,4,8,16
#define samplesize 657//467   //样本列数,即是降维前的特征数
#define cutSamplesize 328   //降维后样本列数,即是实际训练与测试的特征数
#define isPoleEar false//是否保存判断极耳存在的相关参数


class ExtractionMethode
{
public:
	ExtractionMethode();
	~ExtractionMethode();
	//获取训练与测试数据集的特征信息
	void setTrainData(vector<Mat>&data);
	//提取数据集的特征信息
	void extractFeature(const Mat&src, vector<float>&featureVec);
	//单个样本的特征信息归一化
	void NormFeatureInform(const vector<float>&featureVec, Mat&src);
	//训练与测试数据集的特征信息归一化
	void allNormFeatureInform(Mat&samples,Mat&normSamples);
	
private:
	string imReadPath1 = "";
	string imReadPath2 = "";
	string imReadPath3 = "";
	string imReadPath4 = "";
	LBP lbp;
private:
	//计算均值
	void conutSamplesMean(Mat& samples, vector<float>&samplesMean);
	//计算方差
	void conutSamplesVariance(Mat& samples, vector<float>&samplesMean, vector<float>&samplesVariance);
	//均值方差归一化
	void samplesNormalization(Mat& samples, Mat& normSamples, vector<float>&samplesMean, vector<float>&samplesVariance);
	//提取颜色特征信息
	void colourFeature(const Mat&src, vector<float> &LAB);
	double calc3orderMom(Mat &channel);
	void colorMom(const Mat &img, vector<float> &LAB);
	//提取局部二值模式特征(LBP)
	void extractLBPFeature(const Mat &img, vector<float> &LBP);

};
源文件

LBP.cpp 

#pragma once
#include "stdafx.h"
#include"LBP.h"

//srcImage:灰度图
//LBPImage:LBP图

void LBP::LBP_256(const Mat &srcImage, Mat &LBPImage)
{
	// 参数检查,内存分配
	//图片行数,即高度
	int heightOfLBP = srcImage.rows;
	//图片列数,即宽度
	int widthOfLBP = srcImage.cols;
	//图片深度,例如8U、32F
	int depth = srcImage.depth();
	//图片通道数
	int channels = srcImage.channels();
	//CV_Assert()作用:CV_Assert()若括号中的表达式值为false,则返回一个错误信息。此处用于参数检查。
	CV_Assert(depth == CV_8U && channels == 1);
	//创建一个Mat的实例
	LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);
	// 计算LBP特征图
	// 扩充原图像边界,便于边界处理
	Mat extendedImage;
	//边界扩展copyMakeBorder
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
	// 计算LBP
	//理解突破点(Mat的data指针):原理:图像矩阵是一个二维数组,不论是灰度图像还是彩色图像,在计算机内存中都是以一维数组的形式存储的。
	//用Mat存储一幅图像时,若图像在内存中是连续存储的(Mat对象的isContinuous == true),则可以将图像的数据看成是一个一维数组,
	//而其data(uchar*)成员就是指向图像数据的第一个字节的,因此可以用data指针访问图像的数据。不要忘了进行强制类型转换。
	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
按照定义,考虑LBP特征序列跳变次数小于2的可能数目,分情况讨论:
(1)0次跳变,只有两种,即00...0或者111...1
(2)   1次跳变,两种可能,0— > 1或者1— > 0,比如00001111...111,或者111....10000
每一种情况都有p - 1种,比如如果是1开头,那么必须要在后面的p - 1个位置中的某一个位置完成一次跳变
两种情况下共2(p - 1)种
(3) 两次跳变
0— > 1— > 0: 第一次跳变分别再位置2,位置3,位置4..., 位置p - 1时,第二次跳变分别有p - 2, p - 3, ...1种可能的跳变位置
所以共有p - 2 + p - 3 + .... + 1 = (p - 1)(p - 2) / 2种模式可能
1— > 0— > 1:同理有(p - 1)(p - 2) / 2种模式可能
所以两次跳变可能有(p - 1)(p - 2)种模式
综上,对于Uniform LBP共有2 + 2(p - 1) + (p - 1)(p - 2) = p(p - 1) + 2种模式可能。
对于3×3邻域内8个采样点来说,二进制模式由原始的256种减少为58种,
即:它把值分为59类,58个uniform pattern为一类,其它的所有值为第59类。
这样直方图从原来的256维变成59维。这使得特征向量的维数更少,并且可以减少高频噪声带来的影响。
(循环二进制)
*/

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()作用:CV_Assert()若括号中的表达式值为false,则返回一个错误信息。此处用于参数检查。
	CV_Assert(depth == CV_8U && channels == 1);
	//创建一个Mat的实例
	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]);
}

// 获取一幅图像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;

	Mat 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);//浅拷贝
			cell = iplImage;
			// 计算
			UniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
		}
	}
}

// 计算每个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;
			
		}

		// 下一行
		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;
	}
}

// 计算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;

}

// 获取循环二进制的最小二进制模式
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::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;
	Mat 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);
			cell = iplImage;
			// 计算
			RotationUniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
		}
	}
}

// 计算旋转不变等价模式的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特征向量LBPFeatureVector
	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;
	}
}


// 验证灰度不变+旋转不变+等价模式种类
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;
	}

}

// cellSize:每个cell的大小,如16*16
void LBP::ComputerLBPFeatureVector_Rotation58(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;
	Mat 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);
			cell = iplImage;
			// 计算
			RotationUniformLBP58(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
		}
	}
}

void LBP::RotationUniformLBP58(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);
			cout << minValue << "  ";
			// 计算58种等价模式LBP
			colPointer_ToLBPImage[0] = table[minValue];

			// 下一个像素
			++colPointer_ToExtendedImage;
			++colPointer_ToLBPImage;
		}

		// 下一行
		rowPointer_ToExtendedImage += widthOfExtendedImage;
		rowPointer_ToLBPImage += widthOfLBPImage;
	}
	//cout << binary[0] << binary[1] << binary[2] << binary[3] << binary[4] << binary[5] << binary[6] << binary[7] << "  ";
	// 计算cell的LBP特征向量LBPFeatureVector
	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;
	}
}

 modelTraining.cpp

#pragma once
#include"modelTraining.h"


int _tmain(int argc, _TCHAR* argv[])
{
	/step1:获取训练与测试数据集的特征信息/
	int flag =1;//选择训练模型
	ExtractionMethode ExtractionMethode;
	vector<Mat>data;
	ExtractionMethode.setTrainData(data);
	/step2:训练与测试数据集的特征信息归一化//
	Mat samples, normSamples, Demonstration, labelsF, labelsS;
	samples = data[0];
	labelsF = data[1];
	ExtractionMethode.allNormFeatureInform(samples, normSamples);
	/step3:特征降维
	//PCA降维
	
	Mat normSamplesPCA;
	PCAAlgorithm(normSamples, normSamplesPCA, cutSamplesize);
	normSamples = normSamplesPCA.clone();

	

	/step4:模型的训练与测试
	labelsF.convertTo(labelsS, CV_32SC1);//类型CV_32FC1转换CV_32SC1
	switch (flag)
	{
		std::cout << "开始测试......" << endl;
		clock_t start, finish; //程序开始和结束时间
		double duration;
	case 1:
		//线性核svm
		start = clock(); //开始计时
		srand((unsigned)time(NULL)); // 初始化随机数种子
		MySvmtrain(normSamples, labelsS);
		finish = clock(); //结束时间
		duration = (double)(finish - start) / CLOCKS_PER_SEC; // 程序运行时间
		printf("线性核svm耗时:%lf\n", duration);
		MySvmtest(normSamples, Demonstration, labelsS);
		break;
	case 2:
		//KNN模型
		start = clock(); //开始计时
		srand((unsigned)time(NULL)); // 初始化随机数种子
		MyKNNtrain(1, normSamples, labelsS);
		finish = clock(); //结束时间
		duration = (double)(finish - start) / CLOCKS_PER_SEC; // 程序运行时间
		printf("KNN耗时:%lf\n", duration);
		MyKNNtest(normSamples, Demonstration, labelsS);
		break;
	case 3:
		//BP神经网络
		start = clock(); //开始计时
		srand((unsigned)time(NULL)); // 初始化随机数种子
		MyBPtrain(normSamples, labelsS);
		finish = clock(); //结束时间
		duration = (double)(finish - start) / CLOCKS_PER_SEC; // 程序运行时间
		printf("BP神经网络耗时:%lf\n", duration);
		MyBPtest(normSamples, Demonstration, labelsS);
		break;
	case 4:
		//正态贝叶斯分类器
		start = clock(); //开始计时
		srand((unsigned)time(NULL)); // 初始化随机数种子
		MyBayestrain(normSamples, labelsS);
		finish = clock(); //结束时间
		duration = (double)(finish - start) / CLOCKS_PER_SEC; // 程序运行时间
		printf("正态贝叶斯分类器耗时:%lf\n", duration);
		MyBayestest(normSamples, Demonstration, labelsS);
		break;
	default:
		//利用pso_svm()寻找适宜参数
		pso_svm(normSamples, labelsF);
		float gamma = Gamma;//SVM核参数
		float c = Nu;//惩罚系数
		std::cout << "测试设置参数Gamma: " << Gamma << "  Nu: " << Nu << std::endl;
		start = clock(); //开始计时
		srand((unsigned)time(NULL)); // 初始化随机数种子
		//训练SVM模型
		Mytrain(gamma, c, normSamples, labelsS);
		duration = (double)(finish - start) / CLOCKS_PER_SEC; // 程序运行时间
		printf("非线性核svm耗时:%lf\n", duration);
		//SVM模型进行预测
		Mytest(normSamples, Demonstration, labelsS);
		break;
	}
	std::cout << "结束测试......" << endl;
	//cv::waitKey(0);
	getchar();
	return 0;
	
}
//粒子群优化SVM参数
void pso_svm(Mat &samples, Mat &labels)
{
	Mat samples_, labels_;
	samples_.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	labels_.create(PosSamNO + NegSamNO , 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			samples_.at<float>(num, k) = samples.at<float>(num, k);
		}
		labels_.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	std::cout << "开始测试......" << std::endl;
	clock_t start, finish; //程序开始和结束时间
	start = clock(); //开始计时
	srand((unsigned)time(NULL)); // 初始化随机数种子
	int num = 0;
	std::cout << "次数: " << "####################  " << num << std::endl;
	for (int i = 0; i < sizepop; i++)
	{
		for (int j = 0; j < dim; j++)
		{
			V[i][j] = ((float)std::rand()) / RAND_MAX - 0.5; //0到1之间
			pop[i][j] = (((float)std::rand()) / RAND_MAX) * 10; //0到1之间的随机数,Gamma Nu 
		}
		// tell the trainer the parameters we want to use
		std::cout << "gamma: " << pop[i][0] << "    nu: " << pop[i][1];
		fitness[i] = kFoldCrossValidation(pop[i][0], pop[i][1], samples_, labels_, K);
		std::cout << "  交叉训练测试结果: " << fitness[i] << endl;
	}

	float * best_fit_index; // 用于存放群体极值和其位置(序号)
	best_fit_index = groupsExtremum(fitness, sizepop); //求群体极值
	int index = (int)(*best_fit_index);
	// 群体极值位置
	for (int i = 0; i < dim; i++)
	{
		gbest[i] = pop[index][i];
	}

	// 个体极值位置
	for (int i = 0; i < sizepop; i++)
	{
		for (int j = 0; j < dim; j++)
		{
			pbest[i][j] = pop[i][j];
		}
	}

	// 个体极值适应度值
	for (int i = 0; i < sizepop; i++)
	{
		fitnesspbest[i] = fitness[i];
	}

	//群体极值适应度值
	double bestfitness = *(best_fit_index + 1);
	fitnessgbest = bestfitness;

	//迭代寻优
	for (int i = 0; i < maxgen; i++)
	{
		num++;
		std::cout << "次数: " << "####################  " << num << endl;
		for (int j = 0; j < sizepop; j++)
		{
			//速度更新及粒子更新
			for (int k = 0; k < dim; k++)
			{
				// 速度更新
				double rand1 = (double)std::rand() / RAND_MAX; //0到1之间的随机数
				double rand2 = (double)std::rand() / RAND_MAX;
				double w;
				double Tmax = (double)maxgen;
				w = w_start - (w_start - w_end)*((i + 1) / maxgen);

				V[j][k] = w * V[j][k] + c1 * rand1*(pbest[j][k] - pop[j][k]) + c2 * rand2*(gbest[k] - pop[j][k]);
				if (V[j][k] > Vmax)
					V[j][k] = Vmax;
				if (V[j][k] < Vmin)
					V[j][k] = Vmin;
				// 粒子更新
				pop[j][k] = pop[j][k] + V[j][k];
				if (pop[j][k] > popmax)
					pop[j][k] = popmax;
				if (pop[j][k] < popmin)
					pop[j][k] = popmin;
			}
			// tell the trainer the parameters we want to use
			std::cout << "gamma: " << pop[j][0] << "    nu: " << pop[j][1];
			fitness1 = fitness[j];
			fitness[j] = kFoldCrossValidation(pop[j][0], pop[j][1], samples_, labels_, K);
			std::cout << "  交叉训练测试结果: " << fitness[j] << endl;
		}

		for (int j = 0; j < sizepop; j++)
		{
			// 个体极值更新
			if (fitness[j] > fitnesspbest[j])
			{
				for (int k = 0; k < dim; k++)
				{
					pbest[j][k] = pop[j][k];
				}
				fitnesspbest[j] = fitness[j];
			}
			// 群体极值更新
			if (fitness[j] > fitnessgbest)
			{
				for (int k = 0; k < dim; k++)
					gbest[k] = pop[j][k];
				fitnessgbest = fitness[j];
			}
		}
		for (int k = 0; k < dim; k++)
		{
			genbest[i][k] = gbest[k]; // 每一代最优值取值粒子位置记录
		}
		result[i] = fitnessgbest; // 每代的最优值记录到数组
	}

	int best_result_genbest;
	best_fit_index = groupsExtremum(result, maxgen);
	float best_result = *(best_fit_index + 1); //最优解
	best_result_genbest = *(best_fit_index); //最优解
	Gamma = genbest[best_result_genbest][0];
	Nu = genbest[best_result_genbest][1];

	printf("最优值粒子位置为:%lf, %lf,实验结果:%lf\n", Gamma, Nu, best_result);
	finish = clock(); //结束时间
	float duration = (float)(finish - start) / CLOCKS_PER_SEC; // 程序运行时间
	printf("程序运行耗时:%lf\n", duration);
	std::cout << "设置参数Gamma: " << Gamma << "  Nu: " << Nu << std::endl;
	std::cout << "结束测试......" << std::endl;
}
//K折交叉验证
float kFoldCrossValidation(float gamma, float c, Mat samples_, Mat label, int K)
{
	将样本随机分成n份 存在vector中为n分的索引
	vector<vector<int>*>  *indexRandov = randomIndex(K, label);
	vector<vector<float>*> *samples = new vector<vector<float>*>();
	vector<float>*Mylabel = new vector<float>();

	//Mat  samples_, label 转化为向量Mylabel samples
	for (int i = 0; i < samples_.rows; i++)
	{
		vector<float>*subsamples = new vector<float>();
		Mylabel->push_back(label.at<float>(i, 0));
		for (int j = 0; j < samples_.cols; j++)
		{
			subsamples->push_back(samples_.at<float>(i, j));
		}
		samples->push_back(subsamples);
	}

	//进行svm整合 交叉验证
	//存储每次的正确率
	vector<float> *Accuracy = new vector<float>();
	for (int i = 0; i < K; i++)
	{   //每次获取其正确率 存放在vector<vector<int>*> 中
		//根据indexRandov整合数据
		//测试标签
		float *testlable = new float[(indexRandov->at(i))->size()];
		float *testlablestart = testlable;

		//设置测试集
		vector<int>* testindex = indexRandov->at(i);
		float *testdata = new float[(indexRandov->at(i))->size() * (samples->at(0))->size()];
		float *testdatastart = testdata;

		for (int j = 0; j < testindex->size(); j++, testlable++)
		{   //根据索引找出对应的值
			//根据引索找出,对应的标签
			int indexvalue = testindex->at(j);
			*testlable = Mylabel->at(indexvalue);
			//cout<<"ikjkfhgihg"<< Mylabel->at(j) << endl;

			//根据索引值找出对应的值
			vector<float> * testlist = samples->at(indexvalue);
			for (int k = 0; k < testlist->size(); k++, testdata++)
			{
				*testdata = testlist->at(k);
			}
		}

		//设置训练集
		float *traindata = new float[(samples->size() - (indexRandov->at(i))->size()) * (samples->at(0))->size()];
		float *traindatastart = traindata;
		float *trainlable = new float[(samples->size() - (indexRandov->at(i))->size())];
		float *trainlablestart = trainlable;
		for (int kk = 0; kk < K; kk++)
		{
			if (kk != i)
			{
				vector<int>* trainindex = indexRandov->at(kk);
				for (int j = 0; j < trainindex->size(); j++, trainlable++)
				{   //根据索引找出对应的值
					//根据引索找出,对应的标签
					int indexvalue = trainindex->at(j);
					*trainlable = Mylabel->at(indexvalue);
					//根据索引值找出对应的值
					vector<float> * trainlist = samples->at(trainindex->at(j));
					for (int k = 0; k < trainlist->size(); k++, traindata++)
					{
						*traindata = trainlist->at(k);
					}
				}
			}
		}

		MySVM(Accuracy, gamma, c, testlablestart, testdatastart, (indexRandov->at(i))->size(),
			(samples->at(0))->size(), trainlablestart, traindatastart,
			(samples->size() - (indexRandov->at(i))->size()), (samples->at(0))->size());
	}

	float sum = 0.0, averageAccuracy = 0.0;
	for (int t = 0; t < K; t++)
	{
		sum = sum + Accuracy->at(t);
	}
	float CrossValidation;
	averageAccuracy = sum / K;
	CrossValidation = averageAccuracy;
	//释放内存
	vector<vector<int>*>().swap(*indexRandov);
	vector<vector<float>*>().swap(*samples); 
	vector<float>().swap(*Mylabel); 
	vector<float>().swap(*Accuracy);
	return CrossValidation;
}
//序号打乱,返回随机索引
vector<vector<int>*>  *randomIndex(int Fold, Mat label)
{
	//初始化
	int size = label.rows;
	vector<vector<int>*> * indexrandom = new vector<vector<int>*>();
	vector<int> random;
	for (int i = 0; i < size; i++)
	{
		random.push_back(i);
	}

	//将数组打乱顺序
	//srand函数是随机数发生器的初始化函数。原型:void srand(unsigned seed);
	//用法:它初始化随机种子,会提供一个种子,这个种子会对应一个随机数,
	//如果使用相同的种子后面的rand()函数会出现一样的随机数
	srand(time(NULL));
	int index, temp;
	for (int i = 0; i < size; i++)
	{
		index = rand() % (size - i) + i;
		//index != i时,对应值位置交换
		if (index != i)
		{
			temp = random.at(i);
			random.at(i) = random.at(index);
			random.at(index) = temp;
		}
	}

	//划分n等分
	int mathevermath = size / Fold;//每份几个
	for (int i = 0; i < Fold; i++)
	{
		int end = mathevermath * (i + 1);
		//考虑余数
		if ((size - end) < mathevermath)
		{
			end = size;
		}

		vector<int>* index = new vector<int>();
		for (int j = mathevermath * i; j < end; j++)
		{

			index->push_back(random.at(j));
		}
		indexrandom->push_back(index);
	}
	//释放内存
	
	vector<int>().swap(random);
	return indexrandom;
}
void MySVM(vector<float> *Accuracy, float gamma, float c, float*testlabel, float*testdata, int testrows, int testclos, float*trainlabel, float*traindata, int trainrows, int trainclos)
{
	// step 1:  
	//训练样本vector转Mat
	Mat traininglabelsMat;
	traininglabelsMat.create(trainrows, 1, CV_32SC1);
	Mat trainingDataMat;
	trainingDataMat.create(trainrows, trainclos, CV_32FC1);
	for (int i = 0; i < trainrows; i++)
	{
		traininglabelsMat.at<signed int>(i, 0) = trainlabel[i];
		for (int j = 0; j < trainclos; j++)
		{
			trainingDataMat.at<float>(i, j) = traindata[i*trainclos + j];
		}
	}

	//测试样本vector转Mat
	Mat testlabelsMat;
	testlabelsMat.create(testrows, 1, CV_32SC1);
	Mat testDataMat;
	testDataMat.create(testrows, testclos, CV_32FC1);
	for (int i = 0; i < testrows; i++)
	{
		testlabelsMat.at<signed int>(i, 0) = testlabel[i];
		for (int j = 0; j < testclos; j++)
		{
			testDataMat.at<float>(i, j) = testdata[i*testclos + j];
		}
	}

	// step 2:训练参数设定
	// 创建分类器并设置参数
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	SVM_params->setType(cv::ml::SVM::C_SVC);     //C_SVC用于分类,C_SVR用于回归
	SVM_params->setKernel(cv::ml::SVM::/*SIGMOIDPOLY*/RBF);
	// 注释掉部分对本项目不影响,影响因子只有两个
	//SVM_params->setDegree(3);            //核函数中的参数degree,针对多项式核函数;
	SVM_params->setGamma(gamma);       //核函数中的参数gamma,针对多项式/RBF/SIGMOID核函数; 
	//SVM_params->setCoef0(1);             //核函数中的参数,针对多项式/SIGMOID核函数;
	SVM_params->setC(c);                //SVM最优问题参数,设置C-SVC,EPS_SVR和NU_SVR的参数;
	//SVM_params->setNu(0.00001);                //SVM最优问题参数,设置NU_SVC, ONE_CLASS 和NU_SVR的参数; 
	//SVM_params->setP(0);                 //SVM最优问题参数,设置EPS_SVR 中损失函数p的值. 
	//结束条件,即训练1000次或者误差小于0.01结束
	SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 10000, 0.001));
	//SVM_params->setTermCriteria(TermCriteria(TermCriteria::EPS, 10000, 0.00001));

	// step 3:  
	//启动训练过程
	//Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, traininglabelsMat);
	//SVM_params->train(tData);//训练
	SVM_params->train(trainingDataMat, ml::ROW_SAMPLE, traininglabelsMat);
	
	//计算每次训练得到的模型的预测正确率
	double foldAccuracy, NegTrue = 0.0, PosTrue = 0.0, Pos = 0.0, Neg = 0.0;
	for (int i = 0; i < testDataMat.rows; ++i)
	{
		Mat e(1, testDataMat.cols, CV_32FC1);
		for (int j = 0; j < testDataMat.cols; ++j)
		{
			e.at<float>(0, j) = testDataMat.at<float>(i, j);
		}
		float  response = SVM_params->predict(e);
		if (1 == testlabelsMat.at<signed int>(i, 0))
		{
			Pos++;
		}
		else
		{
			Neg++;
		}
		if (response == testlabelsMat.at<signed int>(i, 0) && response == 1)
		{
			PosTrue++;
		}
		else if (response == testlabelsMat.at<signed int>(i, 0) && response == -1)
		{
			NegTrue++;
		}
	}
	foldAccuracy = PosTrue / Pos + NegTrue / Neg;
	Accuracy->push_back(foldAccuracy);
}
//求群体极值
float * groupsExtremum(float * fit, int size)
{
	int index = 0; // 初始化序号
	float max = *fit; // 初始化最大值为数组第一个元素
	static float best_fit_index[2];
	for (int i = 1; i < size; i++)
	{
		if (*(fit + i) > max)
		{
			max = *(fit + i);
			index = i;
		}
	}
	best_fit_index[0] = index;
	best_fit_index[1] = max;
	return best_fit_index;
}
void Mytrain(float gamma, float c, Mat& samples, Mat& labels)
{

	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	SVM_params->setType(cv::ml::SVM::C_SVC);     //C_SVC用于分类,C_SVR用于回归
	SVM_params->setKernel(cv::ml::SVM::POLY/*SIGMOIDPOLYRBF*/);
	// 注释掉部分对本项目不影响,影响因子只有两个
	SVM_params->setDegree(1);            //核函数中的参数degree,针对多项式核函数;
	//SVM_params->setGamma(0.08192);       //核函数中的参数gamma,针对多项式/RBF/SIGMOID核函数; 
	SVM_params->setGamma(gamma);
	//SVM_params->setCoef0(1);             //核函数中的参数,针对多项式/SIGMOID核函数;
	//SVM_params->setC(0.15625);            //SVM最优问题参数,设置C-SVC,EPS_SVR和NU_SVR的参数;
	SVM_params->setC(c);
	SVM_params->setNu(0.5);                //SVM最优问题参数,设置NU_SVC, ONE_CLASS 和NU_SVR的参数; 
	SVM_params->setP(0);                 //SVM最优问题参数,设置EPS_SVR 中损失函数p的值. 
	//结束条件,即训练1000次或者误差小于0.01结束

	//SVM_params->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 10000, 0.001));

	// step 3:  
	//启动训练过程
	Ptr<cv::ml::TrainData> tData = cv::ml::TrainData::create(sample, cv::ml::ROW_SAMPLE, label);
	SVM_params->train(tData);//训练
	//SVM_params->train(trainingDataMat, ml::ROW_SAMPLE, traininglabelsMat);
	std::cout << "训练完成" << std::endl;
	SVM_params->save("Psosvm.xml");
	std::cout << "保存完毕" << std::endl;
}
void Mytest(Mat& samples, Mat&Demonstration, Mat& labels)
{
	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	SVM_params = cv::ml::SVM::load("Psosvm.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		response = SVM_params->predict(e);
		if (response == label.at<float>(i, 0))
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;

	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float  response = SVM_params->predict(sampleMat);

		if (response == label0.at<float>(i, 0))
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}

//线性核svm
void MySvmtrain(Mat& samples, Mat& labels)
{
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	//创建SVM分类器
	Ptr<cv::ml::SVM> svm = cv::ml::SVM::create();
	svm->setType(cv::ml::SVM::C_SVC);
	svm->setKernel(cv::ml::SVM::LINEAR);
	//svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 10000, 0.0001));
	svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 10000, 0.001));
	//模型训练
	svm->train(sample, cv::ml::ROW_SAMPLE, label);
	std::cout << "训练完成" << std::endl;
	//模型保存
	svm->save("SVM.xml");
	std::cout << "保存完毕" << std::endl;
}
void MySvmtest(Mat& samples, Mat&Demonstration, Mat& labels)
{
	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	//label.convertTo(label, CV_32FC1);
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	SVM_params = cv::ml::SVM::load("SVM.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		response = SVM_params->predict(e);
		if (response == label.at<float>(i, 0))
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;

	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);
	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float  response = SVM_params->predict(sampleMat);

		if (response == label0.at<float>(i, 0))
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}
//KNN模型
void MyKNNtrain(int k, Mat& samples, Mat& labels)
{
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	int K = k;
	Ptr<cv::ml::KNearest> knn = cv::ml::KNearest::create();
	knn->setDefaultK(K);
	knn->setIsClassifier(true);

	// 设置训练数据
	Ptr<cv::ml::TrainData> tData = cv::ml::TrainData::create(sample, cv::ml::ROW_SAMPLE, label);
	knn->train(tData);
	std::cout << "训练完毕" << std::endl;
	knn->save("KNN.txt");
	std::cout << "保存完毕" << std::endl;
}
void MyKNNtest(Mat& samples, Mat&Demonstration, Mat& labels)
{

	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	Ptr<cv::ml::KNearest> knn = cv::Algorithm::load<cv::ml::KNearest>("KNN.txt");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		response = knn->predict(e);
		if (response == label.at<float>(i, 0))
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;
	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float  response = knn->predict(sampleMat);

		if (response == label0.at<float>(i, 0))
		{
			True1++;
		}
		
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}
//BP神经网络
void MyBPtrain(Mat& samples, Mat& labels)
{
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	label.convertTo(label, CV_32FC1);
	int samrow = sample.rows;
	int samcol = sample.cols;
	Mat labelsMat(samrow, 1, CV_32FC1);
	for (int i = 0; i< samrow; i++)
	{
		if (label.at<float>(i, 0) == 1.0)
		{
			labelsMat.at<float>(i, 0) = 1;

		}
		else if (label.at<float>(i, 0) == -1.0)
		{
			labelsMat.at<float>(i, 0) = 0;
		}
	}

	// BP 模型创建和参数设置
	Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::create();

	Mat layers_size = (Mat_<int>(1, 3) << samcol, 6, 1); // samcol维点,1维输出
	bp->setLayerSizes(layers_size);

	bp->setTrainMethod(ml::ANN_MLP::BACKPROP, 0.1, 0.1);
	bp->setActivationFunction(ml::ANN_MLP::SIGMOID_SYM);
	bp->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 1000, /*FLT_EPSILON*/1e-6));
	bp->train(sample, ml::ROW_SAMPLE, labelsMat);
	std::cout << "训练完成" << std::endl;
	bp->save("BP.xml");
	std::cout << "保存完毕" << std::endl;
}
void MyBPtest(Mat& samples, Mat&Demonstration, Mat& labels)
{

	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	label.convertTo(label, CV_32FC1);
	Mat responseMat;
	Ptr<ml::ANN_MLP> bp = ml::ANN_MLP::load("BP.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}

		bp->predict(e, responseMat);
		float* p = responseMat.ptr<float>(0);
		if (p[0] >0.5&& label.at<float>(i, 0) == 1)
		{
			True0++;
		}
		else if (p[0]<0.5 && label.at<float>(i, 0) == -1)
		{
			True0++;
		}
	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;
	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		bp->predict(sampleMat, responseMat);
		float* p = responseMat.ptr<float>(0);
		if (p[0] < 0.5&&label0.at<float>(i, 0) == -1.0)
		{
			True1++;
		}
		if (p[0] > 0.5&&label0.at<float>(i, 0) == 1.0)
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;

}
//正态贝叶斯分类器
void MyBayestrain(Mat& samples, Mat& labels)
{

	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32SC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k <cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}
	Ptr<cv::ml::NormalBayesClassifier> model = cv::ml::NormalBayesClassifier::create();
	model->train(sample, cv::ml::ROW_SAMPLE, label);
	std::cout << "训练完成" << std::endl;
	model->save("Bayes.xml");
	std::cout << "保存完毕" << std::endl;
}
void MyBayestest(Mat& samples, Mat&Demonstration, Mat& labels)
{

	labels.convertTo(labels, CV_32FC1);
	Mat sample, label;
	sample.create(PosSamNO + NegSamNO, cutSamplesize, CV_32FC1);
	label.create(PosSamNO + NegSamNO, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO + NegSamNO; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample.at<float>(num, k) = samples.at<float>(num, k);
		}
		label.at<float>(num, 0) = labels.at<float>(num, 0);
	}

	label.convertTo(label, CV_32FC1);
	Mat responseMat;
	Ptr<ml::NormalBayesClassifier> model = ml::NormalBayesClassifier::load("Bayes.xml");
	double foldAccuracy0, foldAccuracy1, True0 = 0.0, True1 = 0.0;
	for (int i = 0; i < sample.rows; ++i)
	{
		Mat e(1, sample.cols, CV_32FC1);

		float  response;
		for (int j = 0; j < sample.cols; ++j)
		{
			e.at<float>(0, j) = sample.at<float>(i, j);
		}
		float r = model->predict(e);
		if (r == label.at<float>(i, 0))
		{
			True0++;
		}


	}
	foldAccuracy0 = True0 / sample.rows;
	std::cout << "训练数据正确率: " << foldAccuracy0 << std::endl;

	Mat sample0, label0, sampleMat;
	sampleMat.create(1, cutSamplesize, CV_32FC1);
	sample0.create(testPos + testNeg, cutSamplesize, CV_32FC1);
	label0.create(testPos + testNeg, 1, CV_32FC1);
	//label0.convertTo(label, CV_32FC1);

	for (int num = 0; num < testPos + testNeg; num++)
	{
		for (int k = 0; k < cutSamplesize; k++)
		{
			sample0.at<float>(num, k) = samples.at<float>(PosSamNO + NegSamNO + num, k);
		}
		label0.at<float>(num, 0) = labels.at<float>(PosSamNO + NegSamNO + num, 0);
	}

	int row = sample0.rows, col = sample0.cols;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			//sample_type sample;
			sampleMat.at< float>(0, j) = sample0.at< float>(i, j);
		}
		float r = model->predict(sampleMat);

		if (r == label0.at<float>(i, 0))
		{
			True1++;
		}
	}
	foldAccuracy1 = True1 / row;
	std::cout << "测试数据正确率: " << foldAccuracy1 << std::endl;
}

//3. PCA降维:1. PCA之前先做归一化处理 2.PCA模型可以保存 
void PCAAlgorithm(const Mat&normSamples, Mat&normSamplesPCA, int K)
{
	
	//PCA主成分维数,需要小于样本数,大于样本数时等于样本数
	PCA pca(normSamples, Mat(), PCA::DATA_AS_ROW, K);
	//TODO:把PCA模型保存
	FileStorage fs("PCA.xml", FileStorage::WRITE);
	pca.write(fs);
	fs.release(); 
    normSamplesPCA = pca.project(normSamples);//映射 降维,降维后的矩阵传给SVM训练
    //Mat back = pca.backProject(projectedMat);//从K维矩阵反映射到原来的维数
}


 trainFeatureExtraction.cpp

#pragma once
#include "stdafx.h"
#include"trainFeatureExtraction.h"

ExtractionMethode::ExtractionMethode()
{

}

ExtractionMethode::~ExtractionMethode()
{

}
//获取训练与测试数据集的特征信息
void ExtractionMethode::setTrainData(vector<Mat>&data)
{
	step1:获取训练样本中正样本的特征信息
	vector<float> featureVec;
	//std::ifstream finPos("D:\\data\\isPole\\Pole1\\list.txt");
	std::ifstream finPos("D:\\data\\poseNegPole\\Al1\\list.txt");
	std::string ImgName1;
	Mat samples, labels;
	samples.create(PosSamNO + NegSamNO + testPos + testNeg, samplesize, CV_32FC1);
	labels.create(PosSamNO + NegSamNO + testPos + testNeg, 1, CV_32FC1);
	for (int num = 0; num < PosSamNO && getline(finPos, ImgName1); num++)
	{
		featureVec.clear();
		ImgName1 = imReadPath1 + ImgName1;
		cv::Mat srcImage = cv::imread(ImgName1);

		extractFeature(srcImage, featureVec);

		for (int k = 0; k< samplesize; k++)
		{
			samples.at<float>(num, k) = featureVec[k];
		}
		labels.at<float>(num, 0) = 1.00;
	}

	step2:获取训练样本中负样本的特征信息
	//std::ifstream finNeg("D:\\data\\isPole\\Wu1\\list.txt");
	std::ifstream finNeg("D:\\data\\poseNegPole\\Cu1\\list.txt");
	std::string ImgName2;
	
	for (int num = 0; num < NegSamNO && getline(finNeg, ImgName2); num++)
	{
		featureVec.clear();
		ImgName2 = imReadPath2 + ImgName2;
		cv::Mat srcImage = cv::imread(ImgName2);
		extractFeature(srcImage, featureVec);

		for (int k = 0; k < samplesize; k++)
		{
			samples.at<float>(PosSamNO + num, k) = featureVec[k];
		}
		labels.at<float>(PosSamNO + num, 0) = -1.;
	}

	step3:获取正测试样本的特征信息
	//std::ifstream finPosTest("D:\\data\\isPole\\Pole2\\list.txt");
	std::ifstream finPosTest("D:\\data\\poseNegPole\\Al2\\list.txt");
	std::string ImgName3;
	
	for (int num = 0; num < testPos && getline(finPosTest, ImgName3); num++)
	{
		featureVec.clear();
		ImgName3 = imReadPath3 + ImgName3;
		cv::Mat srcImage = cv::imread(ImgName3);
		extractFeature(srcImage, featureVec);
		for (int k = 0; k < samplesize; k++)
		{
			samples.at<float>(PosSamNO + NegSamNO + num, k) = featureVec[k];
		}
		labels.at<float>(PosSamNO + NegSamNO + num, 0) = 1.0;
	}

	step4:获取负测试样本的特征信息
	//std::ifstream finNegTest("D:\\data\\isPole\\Wu2\\list.txt");
	std::ifstream finNegTest("D:\\data\\poseNegPole\\Cu2\\list.txt");
	std::string ImgName4;
	
	for (int num = 0; num < testNeg && getline(finNegTest, ImgName4); num++)
	{
		featureVec.clear();
		ImgName4 = imReadPath4 + ImgName4;
		cv::Mat srcImage = cv::imread(ImgName4);
		extractFeature(srcImage, featureVec);

		for (int k = 0; k < samplesize; k++)
		{
			samples.at<float>(PosSamNO + NegSamNO + testPos + num, k) = featureVec[k];
		}
		labels.at<float>(PosSamNO + NegSamNO + testPos + num, 0) = -1.0;
	}

	data.push_back(samples);
	data.push_back(labels);
	vector<float>().swap(featureVec);
	return;
}
//训练与测试数据集的特征信息归一化
void ExtractionMethode::allNormFeatureInform(Mat&samples,Mat&normSamples)
{
	vector<float>samplesMean;
	vector<float>samplesVariance;
	normSamples.create(samples.rows, samples.cols, CV_32FC1);
	conutSamplesMean(samples, samplesMean);//计算均值
	conutSamplesVariance(samples, samplesMean, samplesVariance);//计算方差

	//cout << samplesMean[0] << "  " << samplesMean[1] << "  " << samplesMean[2] << std::endl;
	//cout << samplesVariance[0] << "  " << samplesVariance[1] << "  " << samplesVariance[2] << std::endl;
	samplesNormalization(samples,normSamples,samplesMean,samplesVariance);

	 数据写入
	cv::FileStorage fs;
	//fs.open("posNegPoleEar.yaml", cv::FileStorage::WRITE);
	fs.open("posNegPoleEar.yaml", FileStorage::APPEND);
	if (isPoleEar)
	{
		fs << "samplesMeanEx" << samplesMean << "samplesVarianceEx" << samplesVariance;
	}
	else
	{
		fs << "samplesMean" << samplesMean << "samplesVariance" << samplesVariance;
	}	
	fs.release();

	
	数据读取
	//cv::FileStorage fs2("test.yaml", cv::FileStorage::READ);
	//vector<float>samplesMeanR;
	//vector<float>samplesVarianceR;
	//fs2["samplesMean"] >> samplesMeanR;
	//fs2 ["samplesVariance"] >> samplesVarianceR;
	//fs2.release();
	return;
}
//单个样本的特征信息归一化
void ExtractionMethode::NormFeatureInform(const vector<float>&featureVec, Mat&src)
{
	int featureVecSize = featureVec.size();
	src.create(1, featureVec.size(), CV_32FC1);
	int row = src.rows;
	int col = src.cols;
	vector<float> _variance;
	vector<float> _mean;
	for (int i = 0; i < featureVecSize; i++)
	{
		src.at<float>(0, i) = featureVec[i];
		//src.at<float>(0, i) = (featureVec[i] - _mean[i]) / _variance[i];
	}

	vector<float>().swap(_variance);
	vector<float>().swap(_mean);
	return;
}
//计算均值
void ExtractionMethode:: conutSamplesMean(Mat& samples, vector<float>&samplesMean)
{
	
	int row = samples.rows;
	int col = samples.cols;
	for (int i = 0; i < col; i++)
	{
		float sum_mean = 0.0;
		for (int j = 0; j < row; j++)
		{
			sum_mean += samples.at<float>(j, i);
		}
		samplesMean.push_back(sum_mean / row);
	}
}
//计算方差
void ExtractionMethode::conutSamplesVariance(Mat& samples,vector<float>&samplesMean, vector<float>&samplesVariance)
{
	
	int row = samples.rows;
	int col = samples.cols;
	for (int i = 0; i < col; i++)
	{
		float variance_sum = 0;
		for (int j = 0; j < row; j++)
		{
			variance_sum += pow(samples.at<float>(j, i) - samplesMean[i], 2);
		}
		samplesVariance.push_back(sqrt(variance_sum / row));
	}
}
//均值方差归一化
void ExtractionMethode::samplesNormalization(Mat& samples, Mat& normSamples, vector<float>&samplesMean, vector<float>&samplesVariance)
{
	int row = samples.rows;
	int col = samples.cols;
	for (int i = 0; i < row; i++)
	{
		float variance_sum = 0;
		for (int j = 0; j < col; j++)
		{
			normSamples.at<float>(i, j) = (samples.at<float>(i, j) - samplesMean[j]) / samplesVariance[j];
			//cout << normSamples.at<float>(i, j) << "  ";
		}
		//cout << endl;
	}
	
}
//提取数据集的特征信息
void ExtractionMethode::extractFeature(const Mat&src, vector<float>&featureVec)
{ 
	Mat srcCopy =src.clone();
	cv::resize(srcCopy, srcCopy, cv::Size(400, 200));
	//colourFeature(srcCopy, featureVec);
	colorMom(srcCopy, featureVec);
	extractLBPFeature(srcCopy, featureVec);
	
	
	return;

}
//提取颜色特征信息—计算Lab各分量平均值 
void ExtractionMethode::colourFeature(const Mat&src, vector<float> &LAB)
{  
	Mat LabImg;
	int row = src.rows;
	int col = src.cols;
	int srcSize;
	srcSize = row * col;
	cv::cvtColor(src, LabImg, COLOR_BGR2Lab);
	vector<float> LabFeature;
	LabImg.convertTo(LabImg, CV_32FC3);
   float Lsum = 0, asum = 0, bsum = 0;
for (int i = 0; i < row; i++)
{
	for (int j = 0; j < col; j++)
	{
		Lsum += LabImg.at<Vec3f>(i, j)[0];
		asum += LabImg.at<Vec3f>(i, j)[1];
		bsum += LabImg.at<Vec3f>(i, j)[2];
	}
}
float  lavg = 0.0, aavg = 0.0, bavg = 0.0;
lavg = Lsum / srcSize;
aavg = asum / srcSize;
bavg = bsum / srcSize;
LabFeature.push_back(lavg);
LabFeature.push_back(aavg);
LabFeature.push_back(bavg);

for (int s = 0; s < LabFeature.size(); s++)
{
	LAB.push_back(LabFeature[s]);
}
vector<float>().swap(LabFeature);
return;
}
//提取颜色特征信息—计算三阶矩 
double ExtractionMethode::calc3orderMom(Mat &channel)  //计算三阶矩
{
	uchar *p;
	double mom = 0;
	double m = mean(channel)[0];    //计算单通道图像的均值
	int nRows = channel.rows;
	int nCols = channel.cols;
	if (channel.isContinuous())     //连续存储有助于提升图像扫描速度
	{
		nCols *= nRows;
		nRows = 1;
	}
	for (int i = 0; i < nRows; i++) //计算立方和
	{
		p = channel.ptr<uchar>(i);
		for (int j = 0; j < nCols; j++)
			mom += pow((p[j] - m), 3);
	}
	float temp;
	temp = cvCbrt((float)(mom / (nRows*nCols)));    //求均值的立方根
	mom = (double)temp;
	return mom;
}

//提取颜色特征信息—计算9个颜色矩:3个通道的1、2、3阶矩(Lab,RGB)
void ExtractionMethode::colorMom(const Mat &img, vector<float> &LAB)
{
	cv::cvtColor(img, img, COLOR_BGR2Lab);
	double *Mom = new double[9];    //存放9个颜色矩
	Mat hsvimg;
	if (img.channels() != 3)
		cout << "Error,input image must be a color image" << endl;
	Mat r(img.rows, img.cols, CV_8U);
	Mat g(img.rows, img.cols, CV_8U);
	Mat b(img.rows, img.cols, CV_8U);
	Mat channels[] = { b, g, r };
	split(img, channels);
	Mat tmp_m, tmp_sd;
	//计算b通道的颜色矩
	meanStdDev(b, tmp_m, tmp_sd);
	Mom[0] = tmp_m.at<double>(0, 0);
	Mom[3] = tmp_sd.at<double>(0, 0);
	Mom[6] = calc3orderMom(b);
	//  cout << Mom[0] << " " << Mom[1] << " " << Mom[2] << " " << endl;
	//计算g通道的颜色矩
	meanStdDev(g, tmp_m, tmp_sd);
	Mom[1] = tmp_m.at<double>(0, 0);
	Mom[4] = tmp_sd.at<double>(0, 0);
	Mom[7] = calc3orderMom(g);
	//  cout << Mom[3] << " " << Mom[4] << " " << Mom[5] << " " << endl;
	//计算r通道的颜色矩
	meanStdDev(r, tmp_m, tmp_sd);
	Mom[2] = tmp_m.at<double>(0, 0);
	Mom[5] = tmp_sd.at<double>(0, 0);
	Mom[8] = calc3orderMom(r);
	//  cout << Mom[6] << " " << Mom[7] << " " << Mom[8] << " " << endl;
	for (int s = 0; s < 9; s++)
	{
		LAB.push_back(Mom[s]);
	}
	delete[] Mom;
	return;
	//return Mom;//返回颜色矩数组
}

void ExtractionMethode::extractLBPFeature(const Mat &img, vector<float> &LBP)
{

	Mat samples = img.clone();
	cv::resize(samples, samples, cv::Size(192, 96));
	Mat samp;
	cvtColor(samples, samp, COLOR_BGR2GRAY);
	// 计算样本LBP特征向量矩阵和类别矩阵
	//int lengthOfFeatureVector = (64 / CELLSIZE_LBP)*(32 / CELLSIZE_LBP) * 58; // 计算58种等价模式,特征向量的维数
	int lengthOfFeatureVector = (192 / CELLSIZE_LBP)*(96 / CELLSIZE_LBP) * 9; // 计算9种等价模式,特征向量的维数
	vector<float> featureVector;
	// 计算样本LBP特征向量
	//lbp.ComputerLBPFeatureVector(samp, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);计算58种等价模式
	//lbp.ComputerLBPFeatureVector_Rotation58(samp, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);计算58种旋转不变等价模式
	lbp.ComputerLBPFeatureVector_Rotation(samp, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);//计算9种等价模式
	for (int s = 0; s < featureVector.size(); s++)
	{
		LBP.push_back(featureVector[s]);
	}

}

测试部分代码(dll)

头文件

common.h

#pragma once
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <time.h>
#include "featureAnalysisTestBasic.h"

using namespace cv;

struct ResultParamters
{
	Mat src; //原图 未裁切	
	Rect ROIRect;
	int detectResult=0;
};

detectionMethode.h

#pragma once
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include "featureAnalysisTestBasic.h"
#include "common.h"
#include <time.h>
using namespace std;
using namespace cv;

class detection
{
public:
	detection();
	~detection();
	
	void detectionPoleEar(const stInputParameters& IP, stOuputParameters& RD);
	
private:
	double dazhou_th;
	bool isAutoDetection;
	bool isPosNegEearDetect;
	vector<float>samplesMean;
	vector<float>samplesVariance ;
	vector<float>samplesMeanEx;
	vector<float>samplesVarianceEx;
	std::ofstream detectionOut;
	ResultParamters LocalRP;
	stInputParameters LocalIP;
private:
	void getConfigParams();
	void poleEarInspect(const stInputParameters& IP, stOuputParameters& RD);
	bool isExistDatteries(const Mat& src);
	Mat cutDatteriesROI(const Mat& src, Rect & outputRect);
	void byThreshExtractBinaryImg(const Mat& src, Mat& binaryImg);
	void byColorExtractBinaryImg(const Mat& src, Mat& binaryImg);
	void byEdgeExtractBinaryImg(const Mat& src, Mat&srcCopyROI, Rect & outputRect);
	void posNegEearDetect(const Mat&src, const int& flag);
	void drawResult(stOuputParameters& RD);
	void enhanceImg(const Mat&src, Mat&dst);
	void extractBinaryImgLab(const Mat& src, Mat& binaryImg);
	void isPosNegEear(const Mat&src, const int& flag);

};


inline bool comparaPointY(const Point& p1, const Point& p2)
{
	return p1.y < p2.y;
}

featureAnalysisTestBasic.h

#pragma  once 
#include "stdafx.h"
#include <Windows.h>
#include <iostream> 
#include <vector>
#include <string>
//#include <direct.h>
//#include <afxwin.h>
#include <opencv2/opencv.hpp>

using namespace  cv;
using namespace std;

//输入信息结构体
typedef struct stInputParameters
{
	int iMethode;
	int iCorner;
	bool bInitial;
	Point topLeft;
	Point bottomRight;
	cv::Mat Src;
	stInputParameters()
	{
		iMethode = 1;
		iCorner = 1;
		topLeft = Point(100, 100);
		bottomRight = Point(500, 300);
		bInitial = false;
		Src = NULL;
	}

	stInputParameters operator=(const stInputParameters& params)
	{
		iMethode = params.iMethode;
		iCorner = params.iCorner;
		topLeft = params.topLeft;
		bottomRight = params.bottomRight;
		return(*this);
	}

}stInputParameters;


// 输出信息结构体
typedef struct stOuputParameters
{
	int iResult;
	cv::Mat colorMat;  // 返回结果图
	stOuputParameters()
	{
		iResult = 0;
	}
	stOuputParameters operator=(const stOuputParameters& resData)
	{
		this->iResult = resData.iResult;
		if (!resData.colorMat.empty())
			resData.colorMat.copyTo(this->colorMat);
		return (*this);
	}
	void init()
	{
		this->iResult = 0;
		if (!colorMat.empty())
			colorMat.release();
	}

}stOuputParameters;

typedef struct stFileNameParams
{
	string strFileName;
	string strBarcode;

	stFileNameParams()
	{
		strFileName = "";
		strBarcode = "";
	}

	stFileNameParams operator=(const stFileNameParams& name)
	{
		strFileName = name.strFileName;
		strBarcode = name.strBarcode;

		return(*this);
	}

}stFileNameParams;

featureExtraction.h

#pragma once
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include"LBP.h"
#define CELLSIZE_LBP 16 //LBP的窗口大小,4,8,16
//#include "featureAnalysisTestBasic.h"
//#include "common.h"
#include <time.h>
using namespace std;
using namespace cv;

class ExtractionMethode
{
public:
	ExtractionMethode();
	~ExtractionMethode();
	void extractFeature(const Mat&src, vector<float>&featureVec);
	void NormFeatureInform(const vector<float>&featureVec, Mat&src, bool isPoleEar, const vector <float>& samplesMeanR, const vector <float>& samplesVarianceR);
	void PCAAlgorithmTest(const Mat &featureMat, Mat &featureMatPCA);
private:

	LBP lbp;
private:
	void colourFeature(const Mat&src, vector<float> &LAB);
	double calc3orderMom(Mat &channel);
	void colorMom(Mat &img, vector<float> &LAB);
	//提取局部二值模式特征(LBP)
	void extractLBPFeature(const Mat &img, vector<float> &LBP);
	

};

identificationMethod.h

#pragma once
#include <Windows.h>
#include <opencv2/opencv.hpp>
#include <opencv2/ml/ml.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include "featureAnalysisTestBasic.h"
#include "common.h"
#include <time.h>
using namespace std;
using namespace cv;

class Classifiers
{
public:
	Classifiers();
	~Classifiers();
	void SVMClassifier(Mat& samples, int&detectResult,bool isPoleEar=false);
	void KNNClassifier(Mat& samples, int&detectResult, bool isPoleEar = false);
	void BPClassifier(Mat&samples, int&detectResult, bool isPoleEar = false);
	void BayesClassifier(Mat& samples, int&detectResult, bool isPoleEar = false);
	void PsosvmClassifier(Mat& samples, int&detectResult, bool isPoleEar = false);

private:

	
private:
	
	

};

LBP.h

#pragma once
#include "stdafx.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 ComputerLBPFeatureVector_Rotation58(const Mat &srcImage, Size cellSize, vector<float> &featureVector);
	void RotationUniformLBP58(const Mat &srcImage, Mat &LBPImage, vector<float> &LBPFeatureVector, int indexOfCell);
	void Test();// 测试灰度不变+旋转不变+等价模式LBP
};

#endif

poleEarinspect.h

#pragma once
#ifdef POLEEARINSPECT_EXPORTS
#define  POLEEARINSPECT_API __declspec(dllexport)
#else
#define  POLEEARINSPECT_API __declspec(dllimport)
#endif

class POLEEARINSPECT_API CpoleEarinspect
{
public:
	CpoleEarinspect(void);
	// TODO: 在此添加您的方法。
};

extern POLEEARINSPECT_API int npoleEarinspect;

POLEEARINSPECT_API int fnpoleEarinspect(void);

#include <fstream>
#include <opencv2/opencv.hpp>
#include "featureAnalysisTestBasic.h"
#include "detectionMethode.h"

extern "C"
{
	/// 概述: pre角质量判断
	/// 参数说明:
	// 初始化
	POLEEARINSPECT_API bool poleEarDetectionInit(const stInputParameters& inPara);

	/// 返回值:
	POLEEARINSPECT_API int poleEarDetection(stInputParameters& inPara, stOuputParameters& outPara);
};

源文件

detectionMethode.cpp

#pragma once
#include "stdafx.h"
#include"detectionMethode.h"
#include"identificationMethod.h"
#include"featureExtraction.h"

detection::detection()
{

}

detection::~detection()
{
	
	vector<float>().swap(samplesMean);
	vector<float>().swap(samplesVariance);
	vector<float>().swap(samplesMeanEx);
	vector<float>().swap(samplesVarianceEx);
}
static Classifiers ClassifiersOne;
static ExtractionMethode ExtractionMethodeOne;

//极耳检测函数
void detection::detectionPoleEar(const stInputParameters& IP, stOuputParameters& RD)
{
	/step1:初始化日志文件
	char cs[1024];
	SYSTEMTIME st = { 0 };
	GetLocalTime(&st);
	sprintf_s(cs, "D://Save//detectionPoleEar_%d-%d-%d-%d.log", st.wYear, st.wMonth, st.wDay, IP.iCorner);
	if (true)//是否启动日志功能
	{
		detectionOut.open(cs, std::ios::out | std::ios::app);
	}
	
	///step2:从配置文件中获取参数值
	detectionOut << "getConfigParams start...... " << std::endl;
	clock_t step2Start = clock();
	getConfigParams();//从配置文件中获取参数值
	clock_t step2End = clock();
	detectionOut << "getConfigParams end...... " << std::endl << std::endl;
	cout << "getConfigParams Time: " << step2End - step2Start << std::endl;

	/step3:检测与识别电池极耳
	detectionOut << "poleEarInspect start...... " << std::endl;
	clock_t step3Start = clock();
	poleEarInspect(IP, RD);//检测与识别电池铜铝极耳
	clock_t step3End = clock();
	detectionOut << "poleEarInspect end...... " << std::endl << std::endl;
	cout << "poleEarInspect Time: " << step3End - step3Start << std:: endl;
}

//获取配置参数
void detection::getConfigParams()
{
	CvFileStorage* fs = cvOpenFileStorage("D:\\paraPoleEar\\paraConfig.xml", 0, CV_STORAGE_READ);
	//=====================================================================================
	if (fs == NULL)
	{
		//不存在参数配置文件,创建
		fs = cvOpenFileStorage("D:\\paraPoleEar\\paraConfig.xml", 0, CV_STORAGE_WRITE);
		cvWriteReal(fs, "dazhou_th", 0.3);
		cvWriteReal(fs, "isAutoDetection", false);
		cvWriteReal(fs, "isPosNegEearDetect", false);
		cvReleaseFileStorage(&fs);
		fs = cvOpenFileStorage("D:\\paraPoleEar\\paraConfig.xml", 0, CV_STORAGE_READ);
	 }
	isAutoDetection = cvReadRealByName(fs, 0, "isAutoDetection", false);
	isPosNegEearDetect = cvReadRealByName(fs, 0, "isPosNegEearDetect", false);
	dazhou_th = cvReadRealByName(fs, 0, "dazhou_th", 0.5);
	cvReleaseFileStorage(&fs);
	//均值与方差读取
	cv::FileStorage f;
	f.open("posNegPoleEar.yaml", cv::FileStorage::READ);

	if (!f.isOpened() )
	{
		return;
	}
	f["samplesMean"] >> samplesMean;
	f["samplesVariance"] >> samplesVariance;
	f["samplesMeanEx"] >> samplesMeanEx;
	f["samplesVarianceEx"] >> samplesVarianceEx;
	f.release();
}

//极耳识别主要函数,包括判断电芯是否存在、获取电芯极耳区域、
//判断极耳是否存在、识别电芯正负极耳及打印结果信息
void detection::poleEarInspect(const stInputParameters& IP, stOuputParameters& RD)
{
	if (IP.iMethode == 1)
	{  
		this->LocalIP = IP;
		IP.Src.copyTo(LocalRP.src);
		/*static int a = 1;
		stringstream ss;
		ss << a;
		string str = ss.str();
		cv::imwrite("D:\\data\\isPole\\temp\\" + str + ".jpg", LocalRP.src);
		a++;*/
		
	//********************************step1:判断图片中是否存在电芯 ****************************************
	if (!isExistDatteries(LocalRP.src))
	{
		LocalRP.src.copyTo(RD.colorMat);
		putText(RD.colorMat, "No datteries", Point(200, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 3);//++
		RD.iResult = 0;
		return;
	}
	
	//********************************step2:获取电芯极耳区域 ***********************************************
	Mat datteriesROI;
	if (!isAutoDetection)
	{
		//非自动检测极耳区域
		Rect rect = Rect(IP.topLeft, IP.bottomRight);
		rect = rect&Rect(0, 0, LocalRP.src.cols, LocalRP.src.rows);
		LocalRP.ROIRect = rect;
		LocalRP.src(rect).copyTo(datteriesROI);
		resize(datteriesROI, datteriesROI, cv::Size(400, 200));
		
	}
	else
	{
		//自动检测极耳区域
		datteriesROI = cutDatteriesROI(LocalRP.src, LocalRP.ROIRect);
	}
	//********************************step3:判断电芯极耳是否存在 ***********************************************
	
	if (isPosNegEearDetect)
	{
		LocalRP.detectResult = 0;
		isPosNegEear(datteriesROI, 1);
		if (LocalRP.detectResult == -1)
		{
			LocalRP.src.copyTo(RD.colorMat);
			putText(RD.colorMat, "No poleEar", Point(200, 50), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 3);//++
			RD.iResult = 0;
			return;
		}
	}
	//********************************step4:识别电芯正负极耳 ***********************************************
	posNegEearDetect(datteriesROI,1);
	//********************************step5:在结果图中标记相关信息 ***********************************************
	drawResult(RD);

	}
}

//判断电芯是否存在
bool detection::isExistDatteries(const Mat& src)
{
	Mat  binaryImg, dstTempImage;
	 //byThreshExtractBinaryImg(src, dstTempImage);
	byColorExtractBinaryImg(src, dstTempImage);
	vector<vector<Point> > contours;
	vector<cv::Vec4i> hierarchy;
  	findContours(dstTempImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	for (unsigned int i = 0; i < contours.size(); i++)
	{
		Mat tmpImg = Mat::zeros(dstTempImage.size(), CV_8UC1);
		drawContours(tmpImg, contours, i, Scalar(255), CV_FILLED);
		int area = countNonZero(tmpImg);
		if (area >5000)
		{
			return 1;
		}
	}
	return 0;
}

//裁剪电芯极耳区域与返回极耳区域对角坐标
Mat detection::cutDatteriesROI(const Mat& src, Rect & outputRect)
{
	Mat srcGray, srcCopy, binaryImgROI, srcCopyROI;
	srcCopy=src.clone();
	//通过颜色提取极耳区域
	//byColorExtractBinaryImg(srcCopy, binaryImgROI);
	//通过边缘提取极耳区域
	byEdgeExtractBinaryImg(srcCopy, srcCopyROI,outputRect);
	保存分割图片ROI
	/*static int a = 1369;
	stringstream ss;
	ss << a;
	string str = ss.str();
	cv::imwrite("D:\\data\\isPole\\temp\\"+str+".jpg", srcCopyROI);
	a++;*/
	return srcCopyROI;
	
}

//判断极耳是否存在
void detection::isPosNegEear(const Mat&src, const int& flag)
{
	Mat sample, featureMat;
	vector<float> featureVec;
	sample = src.clone();
	//********************************step1:提取ROI特征信息 ************************************************

	ExtractionMethodeOne.extractFeature(sample, featureVec);

	//********************************step2:归一化特征信息 ***********************************************

	ExtractionMethodeOne.NormFeatureInform(featureVec, featureMat, true, samplesMeanEx, samplesVarianceEx);

	//********************************step3:降低特征信息维度 **********************************************
	

	//********************************step4:识别电芯正负极耳 *********************************************

	switch (flag)
	{
	case 1:
		ClassifiersOne.SVMClassifier(featureMat, LocalRP.detectResult,1);
		break;
	case 2:
		ClassifiersOne.KNNClassifier(featureMat, LocalRP.detectResult,1);
		break;
	case 3:
		ClassifiersOne.BPClassifier(featureMat, LocalRP.detectResult,1);
		break;
	case 4:
		ClassifiersOne.BayesClassifier(featureMat, LocalRP.detectResult,1);
		break;
	default:
		ClassifiersOne.PsosvmClassifier(featureMat, LocalRP.detectResult, 1);
		break;
	}
	return;
}

//铜(负)铝(正)极耳识别
void detection::posNegEearDetect(const Mat&src,const int& flag)
{
	Mat sample,  featureMat;
	vector<float> featureVec;
	sample = src.clone();
	//********************************step1:提取ROI特征信息 ************************************************

	ExtractionMethodeOne.extractFeature(sample, featureVec);

	//********************************step2:归一化特征信息 ***********************************************

	ExtractionMethodeOne.NormFeatureInform(featureVec, featureMat,0, samplesMean,samplesVariance);

	//********************************step3:降低特征信息维度 **********************************************
	Mat featureMatPCA;
	ExtractionMethodeOne.PCAAlgorithmTest(featureMat, featureMatPCA);
	featureMat = featureMatPCA.clone();
	//********************************step4:识别电芯正负极耳 *********************************************

	switch (flag)
	{
	case 1:
		ClassifiersOne.SVMClassifier(featureMat, LocalRP.detectResult);
		break;
	case 2:
		ClassifiersOne.KNNClassifier(featureMat, LocalRP.detectResult);
		break;
	case 3:
		ClassifiersOne.BPClassifier(featureMat, LocalRP.detectResult);
		break;
	case 4:
		ClassifiersOne.BayesClassifier(featureMat, LocalRP.detectResult);
		break;
	default:
		ClassifiersOne.PsosvmClassifier(featureMat, LocalRP.detectResult);
		break;
		}
	return ;
}

//打印结果信息
void detection::drawResult(stOuputParameters& RD)
{
	LocalRP.src.copyTo(RD.colorMat);
	if (LocalRP.detectResult== 1)
	{
		RD.iResult = 1;
		rectangle(RD.colorMat, LocalRP.ROIRect, Scalar(0, 0, 255), 2, 8, 0);
		putText(RD.colorMat, "Al", Point(LocalRP.ROIRect.tl().x, LocalRP.ROIRect.tl().y-20), FONT_HERSHEY_COMPLEX, 2, Scalar(0, 255,0), 5);//++
	}
	else
	{
		RD.iResult = -1;
		rectangle(RD.colorMat, LocalRP.ROIRect, Scalar(0, 0, 255), 2, 8, 0);
		putText(RD.colorMat, "Cu", Point(LocalRP.ROIRect.tl().x, LocalRP.ROIRect.tl().y-20), FONT_HERSHEY_COMPLEX, 2, Scalar( 0, 255,0), 5);//++
	}
	return;
}

//通过灰度阈值提取电芯区域
void detection::byThreshExtractBinaryImg(const Mat& src, Mat& binaryImg)
{
	Mat srcCopy = src.clone();
	resize(srcCopy, srcCopy, Size(0, 0), 0.5, 0.5);
	Mat srcCopyROI = Mat::zeros(srcCopy.size(), CV_8UC1);
	cv::cvtColor(srcCopy, srcCopy, CV_BGR2GRAY);
	Rect datteriesRect = Rect(srcCopy.cols*0.2, 0, srcCopy.cols*0.6, srcCopy.rows*0.3);
	datteriesRect = datteriesRect&Rect(0, 0, srcCopy.cols, srcCopy.rows);
	srcCopy(datteriesRect).copyTo(srcCopyROI(datteriesRect));

	cv::Mat dstTempImage1, dstTempImage2, dstTempImage;
	cv::threshold(srcCopyROI, dstTempImage1, 20, 255, cv::THRESH_BINARY);
	cv::threshold(srcCopyROI, dstTempImage2, 80, 255, cv::THRESH_BINARY_INV);
	cv::bitwise_and(dstTempImage1, dstTempImage2, dstTempImage);

	Mat morphEL = getStructuringElement(MORPH_RECT, Size(5, 5));
	morphologyEx(dstTempImage, dstTempImage, MORPH_CLOSE, morphEL);
	binaryImg = dstTempImage.clone();
}

//通过颜色提取电芯区域
void detection::byColorExtractBinaryImg(const Mat& src, Mat& binaryImg)
{
	Mat srcCopy = src.clone();
	cv::cvtColor(srcCopy, srcCopy, COLOR_BGR2HSV);
	Mat srcMask = Mat::zeros(src.size(), CV_8UC1);
	binaryImg = Mat::zeros(src.size(), CV_8UC1);
	for (int i = 0; i < srcCopy.rows; i++) //time is the number of noise you add
	{
		for (int j = 0; j < srcCopy.cols; j++)
		{
			/*int BG = srcCopy.at<Vec3b>(i, j)[0] - srcCopy.at<Vec3b>(i, j)[1];
			int BR = srcCopy.at<Vec3b>(i, j)[0] - srcCopy.at<Vec3b>(i, j)[2];

			int RG = srcCopy.at<Vec3b>(i, j)[2] - srcCopy.at<Vec3b>(i, j)[1];
			int RB = srcCopy.at<Vec3b>(i, j)[2] - srcCopy.at<Vec3b>(i, j)[0];*/
			
			int H = srcCopy.at<Vec3b>(i, j)[0] ;
			int S = srcCopy.at<Vec3b>(i, j)[1] ;
			int V = srcCopy.at<Vec3b>(i, j)[2] ;

			/*if (BG>0 && BR>0 || (RG>100 && RB>100))
			{
				srcMask.at<uchar>(i, j) = 255;
			}*/
			if (H>50 && S>50)
			{
				srcMask.at<uchar>(i, j) = 255;
			}
		}
	}
	Mat morphEL = getStructuringElement(MORPH_RECT, Size(25, 25));
	morphologyEx(srcMask, srcMask, MORPH_OPEN, morphEL);

	vector<vector<Point>> contours;
	findContours(srcMask, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	int AreaTemp = 0;
	int index = 0;
	for (size_t i = 0; i < contours.size(); i++)
	{
		Mat tempImg = Mat::zeros(srcMask.size(), CV_8UC1);
		drawContours(tempImg, contours, i, Scalar(255), CV_FILLED);
		if (countNonZero(tempImg)>AreaTemp)
		{
			AreaTemp = countNonZero(tempImg);
			index = i;
		}
	}
	if (contours.size()>0)
	{
		srcMask.setTo(0);
		drawContours(srcMask, contours, index, Scalar(255), CV_FILLED);
	}
	binaryImg = srcMask;
}

//通过边缘提取电芯区域
void detection::byEdgeExtractBinaryImg(const Mat& src, Mat&srcCopyROI, Rect & outputRect)
{
	Mat srcGray, srcCopy;
	srcCopy = src.clone();
	cv::cvtColor(srcCopy, srcGray, CV_BGR2GRAY);
	Mat kernel = (Mat_<float>(7, 3) << 1, 2, 5,7,5, 2,1,
		1.0,0.0, 0.0, 1.0, 0.0, 0.0,-1.0
		- 1, -2, -5,-7,-5, -2 - 1);
	srcGray.convertTo(srcGray, CV_32FC1, 1.0 / 255);
	pow(srcGray, 2, srcGray);
	threshold(srcGray, srcGray, 0.05, 1, THRESH_TOZERO);

	Mat filterImg, enFilterImg;
	filter2D(srcGray, filterImg, CV_32FC1, kernel);
	GaussianBlur(filterImg, filterImg, Size(), 4,3);
	boxFilter(filterImg, filterImg, CV_32FC1, Size(15, 13));
	Scharr(filterImg, filterImg, CV_32FC1, 0, 1, 5);
	filterImg = max(filterImg, 0);
	enhanceImg(filterImg, enFilterImg);
	
	
	normalize(enFilterImg, enFilterImg, 65535, 0, NORM_MINMAX);
	enFilterImg.convertTo(enFilterImg, CV_8UC1, 255);
	Mat binaryImg;
	threshold(enFilterImg, binaryImg, 1, 255, THRESH_BINARY);

	Rect backgroundRect = Rect(0, 0, binaryImg.cols, 0.2*binaryImg.rows);
	backgroundRect = backgroundRect&Rect(0, 0, binaryImg.cols, binaryImg.rows);
	binaryImg(backgroundRect).setTo(0);

	vector<vector<Point>> contours;
	findContours(binaryImg, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	Mat tempImg = Mat::zeros(binaryImg.size(), CV_8UC1);
	for (size_t i = 0; i < contours.size(); i++)
	{
		Rect outRect = boundingRect(contours[i]);
		if (outRect.width >= 150 && outRect.width <= 100000)
		{
			drawContours(tempImg, contours, i, Scalar(255), CV_FILLED);
		}
	}
	bitwise_and(binaryImg, tempImg, binaryImg);

	Mat morphEL = getStructuringElement(MORPH_RECT, Size(25, 11));
	morphologyEx(binaryImg, binaryImg, MORPH_CLOSE, morphEL);
	contours.clear();
	findContours(binaryImg, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

	int temp = 150;
	int index = -1;
	for (size_t i = 0; i < contours.size(); i++)
	{
		Rect outRect = boundingRect(contours[i]);

		if (outRect.width >= temp)
		{
			temp = outRect.width;
			index = i;
		}
	}
	tempImg.setTo(0);
	if (index != -1)
	{
		drawContours(tempImg, contours, index, Scalar(255), CV_FILLED);
		Rect outRect = boundingRect(contours[index]);

		Rect rect = Rect((outRect.tl().x + outRect.width*0.15), outRect.tl().y , outRect.width*0.4, outRect.width*0.225);
		rect = rect&Rect(0, 0, srcCopy.cols, srcCopy.rows);

		srcCopy(rect).copyTo(srcCopyROI);
		resize(srcCopyROI, srcCopyROI, cv::Size(400, 200));
		outputRect = rect;
	
	}
	else
	{
		Rect rect = Rect(srcCopy.cols*0.25, srcCopy.rows*0.25, srcCopy.cols*0.25, srcCopy.rows*0.25);
		rect = rect&Rect(0, 0, srcCopy.cols, srcCopy.rows);
		srcCopy(rect).copyTo(srcCopyROI);
		resize(srcCopyROI, srcCopyROI, cv::Size(400, 200));
		outputRect = rect;
		
	}
  	
}
void detection::enhanceImg(const Mat&src, Mat&dst)
{
	Mat weightImg;
	weightImg = Mat::zeros(src.size(), CV_32FC1);
	dst = Mat::zeros(src.size(), CV_32FC1);
	Rect sampleRect(0, 0, (int)dst.cols, (int)dst.rows);
	Mat sampleImg;
	src(sampleRect).copyTo(sampleImg);

	Mat morphEL = getStructuringElement(MORPH_RECT, Size(5, 5));
	morphologyEx(sampleImg, sampleImg, MORPH_DILATE, morphEL);

	GaussianBlur(sampleImg, sampleImg, Size(0, 0), 11, 3);
	boxFilter(sampleImg, sampleImg, -1, Size(11, 5));


	for (int i = 0; i < sampleImg.rows; i++)
	{
		Mat tmpImg = sampleImg.row(i);
		double meanVal = mean(tmpImg).val[0];
		double enhanceVal = (mean(tmpImg).val[0]);
		if (meanVal<1)
		{
			enhanceVal = 0;
		}
		enhanceVal = max(enhanceVal, 0.0);
		weightImg.row(i) = enhanceVal;
	}


	dst = src.mul(weightImg);


}

//通过Lab颜色提取电芯区域
void detection::extractBinaryImgLab(const Mat& src, Mat& binaryImg)
{
	Mat srcCopy = src.clone();

	//boxFilter(srcCopy, srcCopy, CV_8UC1, Size(5, 11));
	//GaussianBlur(srcCopy, srcCopy, Size(0, 0), 2, 5);
	cv::cvtColor(srcCopy, srcCopy, COLOR_BGR2XYZ );
	Mat srcMask = Mat::zeros(src.size(), CV_8UC1);
	binaryImg = Mat::zeros(src.size(), CV_8UC1);
	for (int i = 0; i < srcCopy.rows; i++) //time is the number of noise you add
	{
		for (int j = 0; j < srcCopy.cols; j++)
		{

			if (srcCopy.channels() == 1) //single channel
			{
				srcCopy.at<uchar>(i, j);
			}
			else if (srcCopy.channels() == 3) //RGB channel
			{
				int L = srcCopy.at<Vec3b>(i, j)[0];
				int a = srcCopy.at<Vec3b>(i, j)[1];
				int b = srcCopy.at<Vec3b>(i, j)[2];
				int B = src.at<Vec3b>(i, j)[0];
				int G = src.at<Vec3b>(i, j)[1];
				int R = src.at<Vec3b>(i, j)[2];

				//if (abs(a - b)<10 && L>30 && cv::min<int>(a, b)>125 || (L>200 && cv::min<int>(a, b)>127) ||  (L<20 && cv::min<int>(a, b)>130)/*&&cv::min<int>(B,cv::min<int>(G,R))>40*/)
				if (/*cv::min<int>(B, cv::min<int>(G, R))>35 && */((cv::min<int>(L, cv::min<int>(a, b))>30)))
				{
					srcMask.at<uchar>(i, j) = 255;

				}
			}
		}
	}
	/*Mat morphEL = getStructuringElement(MORPH_RECT, Size(3,3));
	morphologyEx(srcMask, srcMask, MORPH_OPEN, morphEL);*/
	Mat morphEL = getStructuringElement(MORPH_RECT, Size(11, 11));
	morphologyEx(srcMask, srcMask, MORPH_CLOSE, morphEL);

	binaryImg = srcMask;
}

featureExtraction.cpp

#pragma once
#include "stdafx.h"
#include"featureExtraction.h"
ExtractionMethode::ExtractionMethode()
{

}

ExtractionMethode::~ExtractionMethode()
{

}

//特征提取函数
void ExtractionMethode::extractFeature(const Mat&src, vector<float>&featureVec)
{ 
	Mat srcCopy =src.clone();
	cv::resize(srcCopy, srcCopy, cv::Size(400, 200));
	featureVec.clear();
	//colourFeature(srcCopy, featureVec);
	colorMom(srcCopy, featureVec);
	extractLBPFeature(srcCopy, featureVec);
	
	return;
}

//提取颜色特征信息
void ExtractionMethode::colourFeature(const Mat&src, vector<float> &LAB)
{
	Mat LabImg;
	int row = src.rows;
	int col = src.cols;
	int srcSize;
	srcSize = row * col;
	cv::cvtColor(src, LabImg, COLOR_BGR2Lab);
	vector<float> LabFeature;
	LabImg.convertTo(LabImg, CV_32FC3);
	float Lsum = 0, asum = 0, bsum = 0;
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			Lsum += LabImg.at<Vec3f>(i, j)[0];
			asum += LabImg.at<Vec3f>(i, j)[1];
			bsum += LabImg.at<Vec3f>(i, j)[2];
		}
	}
	float  lavg = 0.0, aavg = 0.0, bavg = 0.0;
	lavg = Lsum / srcSize;
	aavg = asum / srcSize;
	bavg = bsum / srcSize;
	LabFeature.push_back(lavg);
	LabFeature.push_back(aavg);
	LabFeature.push_back(bavg);

	for (int s = 0; s < LabFeature.size(); s++)
	{
		LAB.push_back(LabFeature[s]);
	}
	vector<float>().swap(LabFeature);
	return;
}

//提取颜色特征信息—计算三阶矩 
double ExtractionMethode::calc3orderMom(Mat &channel)  //计算三阶矩
{
	uchar *p;
	double mom = 0;
	double m = mean(channel)[0];    //计算单通道图像的均值
	int nRows = channel.rows;
	int nCols = channel.cols;
	if (channel.isContinuous())     //连续存储有助于提升图像扫描速度
	{
		nCols *= nRows;
		nRows = 1;
	}
	for (int i = 0; i < nRows; i++) //计算立方和
	{
		p = channel.ptr<uchar>(i);
		for (int j = 0; j < nCols; j++)
			mom += pow((p[j] - m), 3);
	}
	float temp;
	temp = cvCbrt((float)(mom / (nRows*nCols)));    //求均值的立方根
	mom = (double)temp;
	return mom;
}

//提取颜色特征信息—计算9个颜色矩:3个通道的1、2、3阶矩(Lab,RGB)
void ExtractionMethode::colorMom(Mat &img, vector<float> &LAB)
{
	cv::cvtColor(img, img, COLOR_BGR2Lab);
	double *Mom = new double[9];    //存放9个颜色矩
	Mat hsvimg;
	if (img.channels() != 3)
		cout << "Error,input image must be a color image" << endl;
	Mat r(img.rows, img.cols, CV_8U);
	Mat g(img.rows, img.cols, CV_8U);
	Mat b(img.rows, img.cols, CV_8U);
	Mat channels[] = { b, g, r };
	split(img, channels);
	Mat tmp_m, tmp_sd;
	//计算b通道的颜色矩
	meanStdDev(b, tmp_m, tmp_sd);
	Mom[0] = tmp_m.at<double>(0, 0);
	Mom[3] = tmp_sd.at<double>(0, 0);
	Mom[6] = calc3orderMom(b);
	//  cout << Mom[0] << " " << Mom[1] << " " << Mom[2] << " " << endl;
	//计算g通道的颜色矩
	meanStdDev(g, tmp_m, tmp_sd);
	Mom[1] = tmp_m.at<double>(0, 0);
	Mom[4] = tmp_sd.at<double>(0, 0);
	Mom[7] = calc3orderMom(g);
	//  cout << Mom[3] << " " << Mom[4] << " " << Mom[5] << " " << endl;
	//计算r通道的颜色矩
	meanStdDev(r, tmp_m, tmp_sd);
	Mom[2] = tmp_m.at<double>(0, 0);
	Mom[5] = tmp_sd.at<double>(0, 0);
	Mom[8] = calc3orderMom(r);
	//  cout << Mom[6] << " " << Mom[7] << " " << Mom[8] << " " << endl;
	for (int s = 0; s < 9; s++)
	{
		LAB.push_back(Mom[s]);
	}
	delete[] Mom;
	return;
}



//归一化特征向量
void ExtractionMethode::NormFeatureInform(const vector<float>&featureVec, Mat&src, bool isPoleEar, const vector <float>& samplesMeanR, const vector <float>& samplesVarianceR)
{
	int featureVecSize = featureVec.size();
	src.create(1, featureVec.size(), CV_32FC1);
	int row = src.rows;
	int col = src.cols;
	vector <float> _mean = {0.0};
	vector <float> _variance = { 0.0 };
	数据读取
	
	if (isPoleEar)
	{
		_mean = samplesMeanR;
		_variance = samplesVarianceR;
		/*_mean = { 61.7269096f, 129.986008f, 124.385727f,
			59.1149025f, 3.47181058f, 7.54674149f,
			53.8841438f, 2.96848011f, -4.82963848f };

		_variance = { 32.5240593f, 4.24427795f, 10.6079569f,
			33.8971519f, 2.67620277f, 5.75136805f,
			25.8624439f, 3.57374096f, 9.15341854f };*/
	}
	else
	{
		_mean = samplesMeanR;
		_variance = samplesVarianceR;
		/*_mean = { 84.5031967f, 128.992813f, 128.943604f,
			86.2076340f, 3.57340503f, 8.23299980f,
			71.1806030f, 4.67222118f, -6.77685404f };

		_variance = { 25.7978573f, 1.29492974f, 3.39336634f,
			22.9854565f, 1.54723167f, 3.10043240f,
			17.9707928f, 1.75659645f, 9.79730797f };*/
	}
	
	
	for (int i = 0; i < featureVecSize; i++)
	{
		src.at<float>(0, i) = (featureVec[i] - _mean[i]) / _variance[i];
	}
}

//提取局部二值模式特征(LBP)
void ExtractionMethode::extractLBPFeature(const Mat &img, vector<float> &LBP)
{
	Mat samples = img.clone();
	cv::resize(samples, samples, cv::Size(192, 96));
	Mat samp;
	cvtColor(samples, samp, COLOR_BGR2GRAY);
	// 计算样本LBP特征向量矩阵和类别矩阵
	//int lengthOfFeatureVector = (64 / CELLSIZE_LBP)*(32 / CELLSIZE_LBP) * 58; // 计算58种等价模式,特征向量的维数
	int lengthOfFeatureVector = (192 / CELLSIZE_LBP)*(96 / CELLSIZE_LBP) * 9; // 计算9种等价模式,特征向量的维数
	vector<float> featureVector;
	// 计算样本LBP特征向量
	//lbp.ComputerLBPFeatureVector(samp, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);计算58种等价模式
	//lbp.ComputerLBPFeatureVector_Rotation58(samp, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);计算58种旋转不变等价模式
	lbp.ComputerLBPFeatureVector_Rotation(samp, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);//计算9种等价模式
	for (int s = 0; s < featureVector.size(); s++)
	{
		LBP.push_back(featureVector[s]);
	}


}

void ExtractionMethode::PCAAlgorithmTest(const Mat &featureMat, Mat &featureMatPCA)
{
	PCA pca;
	FileStorage fs("PCA.xml", FileStorage::READ);
	pca.read(fs.root());
	fs.release();
	featureMatPCA = pca.project(featureMat);
}

identificationMethod.cpp

#pragma once
#include "stdafx.h"
#include"identificationMethod.h"

Classifiers::Classifiers()
{

}

Classifiers::~Classifiers()
{
	
}

//SVM分类器
void Classifiers::SVMClassifier(Mat& samples, int&detectResult, bool isPoleEar)
{
	//samples.convertTo(samples, CV_32FC1);
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	if (isPoleEar)
	{
		SVM_params = cv::ml::SVM::load("isPoleEarSVM.xml");
	}
	else
	{
		SVM_params = cv::ml::SVM::load("SVM.xml");
	}

	float response = SVM_params->predict(samples);
	detectResult = int(response);
}

//KNN分类器
void Classifiers::KNNClassifier(Mat& samples, int&detectResult, bool isPoleEar)
{
	Ptr<cv::ml::KNearest> knn = cv::ml::KNearest::create();
	if (isPoleEar)
	{
		knn = Algorithm::load<cv::ml::KNearest>("isPoleEarKNN.txt");
	}
	else
	{
		knn = Algorithm::load<cv::ml::KNearest>("KNN.txt");
	}
	
	float response = knn->predict(samples);
	detectResult = int(response); 
}

//BP神经网络
void Classifiers::BPClassifier(Mat& samples, int&detectResult, bool isPoleEar)
{
	
	Ptr<ml::ANN_MLP> bp =  cv::ml::ANN_MLP::create();
	if (isPoleEar)
	{
		bp = ml::ANN_MLP::load("isPoleEarBP.xml");
	}
	else
	{
		bp = ml::ANN_MLP::load("BP.xml");
	}
	
	Mat responseMat;
	bp->predict(samples, responseMat);
	float* p = responseMat.ptr<float>(0);
	if (p[0] >0.5)
	{
		detectResult = 1;
	}
	else if (p[0]<0.5 )
	{
		detectResult = -1;
	}
}

//正态贝叶斯分类器
void Classifiers::BayesClassifier(Mat& samples, int&detectResult, bool isPoleEar )
{
	Ptr<ml::NormalBayesClassifier> BayesModel = cv::ml::NormalBayesClassifier::create();
	if (isPoleEar)
	{
		BayesModel = ml::NormalBayesClassifier::load("isPoleEarBayes.xml");
	}
	else
	{
		BayesModel = ml::NormalBayesClassifier::load("Bayes.xml");
	}


	
	float response = BayesModel->predict(samples);
	detectResult = int(response);
}
//PSOSVM分类器,粒子群算法筛选合理SVM核参数
void Classifiers::PsosvmClassifier(Mat& samples, int&detectResult, bool isPoleEar)
{
	//labels.convertTo(labels, CV_32FC1);
	Ptr<cv::ml::SVM> SVM_params = cv::ml::SVM::create();
	if (isPoleEar)
	{
		SVM_params = cv::ml::SVM::load("isPoleEarPsosvm.xml");
	}
	else
	{
		SVM_params = cv::ml::SVM::load("Psosvm.xml");
	}

	float response = SVM_params->predict(samples);
	detectResult = int(response);
}

LBP.cpp

#pragma once
#include "stdafx.h"
#include"LBP.h"

//srcImage:灰度图
//LBPImage:LBP图

void LBP::LBP_256(const Mat &srcImage, Mat &LBPImage)
{
	// 参数检查,内存分配
	//图片行数,即高度
	int heightOfLBP = srcImage.rows;
	//图片列数,即宽度
	int widthOfLBP = srcImage.cols;
	//图片深度,例如8U、32F
	int depth = srcImage.depth();
	//图片通道数
	int channels = srcImage.channels();
	//CV_Assert()作用:CV_Assert()若括号中的表达式值为false,则返回一个错误信息。此处用于参数检查。
	CV_Assert(depth == CV_8U && channels == 1);
	//创建一个Mat的实例
	LBPImage.create(Size(widthOfLBP, heightOfLBP), CV_8UC1);
	// 计算LBP特征图
	// 扩充原图像边界,便于边界处理
	Mat extendedImage;
	//边界扩展copyMakeBorder
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
	// 计算LBP
	//理解突破点(Mat的data指针):原理:图像矩阵是一个二维数组,不论是灰度图像还是彩色图像,在计算机内存中都是以一维数组的形式存储的。
	//用Mat存储一幅图像时,若图像在内存中是连续存储的(Mat对象的isContinuous == true),则可以将图像的数据看成是一个一维数组,
	//而其data(uchar*)成员就是指向图像数据的第一个字节的,因此可以用data指针访问图像的数据。不要忘了进行强制类型转换。
	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
按照定义,考虑LBP特征序列跳变次数小于2的可能数目,分情况讨论:
(1)0次跳变,只有两种,即00...0或者111...1
(2)   1次跳变,两种可能,0— > 1或者1— > 0,比如00001111...111,或者111....10000
每一种情况都有p - 1种,比如如果是1开头,那么必须要在后面的p - 1个位置中的某一个位置完成一次跳变
两种情况下共2(p - 1)种
(3) 两次跳变
0— > 1— > 0: 第一次跳变分别再位置2,位置3,位置4..., 位置p - 1时,第二次跳变分别有p - 2, p - 3, ...1种可能的跳变位置
所以共有p - 2 + p - 3 + .... + 1 = (p - 1)(p - 2) / 2种模式可能
1— > 0— > 1:同理有(p - 1)(p - 2) / 2种模式可能
所以两次跳变可能有(p - 1)(p - 2)种模式
综上,对于Uniform LBP共有2 + 2(p - 1) + (p - 1)(p - 2) = p(p - 1) + 2种模式可能。
对于3×3邻域内8个采样点来说,二进制模式由原始的256种减少为58种,
即:它把值分为59类,58个uniform pattern为一类,其它的所有值为第59类。
这样直方图从原来的256维变成59维。这使得特征向量的维数更少,并且可以减少高频噪声带来的影响。
(循环二进制)
*/

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()作用:CV_Assert()若括号中的表达式值为false,则返回一个错误信息。此处用于参数检查。
	CV_Assert(depth == CV_8U && channels == 1);
	//创建一个Mat的实例
	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]);
}

// 获取一幅图像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;

	Mat 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);//浅拷贝
			cell = iplImage;
			// 计算
			UniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
		}
	}
}

// 计算每个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;
			
		}

		// 下一行
		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;
	}
}

// 计算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;

}

// 获取循环二进制的最小二进制模式
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::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;
	Mat 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);
			cell = iplImage;
			// 计算
			RotationUniformLBP(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
		}
	}
}

// 计算旋转不变等价模式的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特征向量LBPFeatureVector
	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;
	}
}


// 验证灰度不变+旋转不变+等价模式种类
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;
	}

}

// cellSize:每个cell的大小,如16*16
void LBP::ComputerLBPFeatureVector_Rotation58(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;
	Mat 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);
			cell = iplImage;
			// 计算
			RotationUniformLBP58(cell, LBPImage, featureVector, (y*numberOfCell_X + x));
		}
	}
}

void LBP::RotationUniformLBP58(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);
			cout << minValue << "  ";
			// 计算58种等价模式LBP
			colPointer_ToLBPImage[0] = table[minValue];

			// 下一个像素
			++colPointer_ToExtendedImage;
			++colPointer_ToLBPImage;
		}

		// 下一行
		rowPointer_ToExtendedImage += widthOfExtendedImage;
		rowPointer_ToLBPImage += widthOfLBPImage;
	}
	//cout << binary[0] << binary[1] << binary[2] << binary[3] << binary[4] << binary[5] << binary[6] << binary[7] << "  ";
	// 计算cell的LBP特征向量LBPFeatureVector
	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;
	}
}

poleEarinspect.cpp

// poleEarinspect.cpp : 定义 DLL 应用程序的导出函数。
#pragma once

#include "poleEarinspect.h"

static bool bInitial = true;
// 这是导出变量的一个示例
POLEEARINSPECT_API int npoleEarinspect = 0;

// 这是导出函数的一个示例。
POLEEARINSPECT_API int fnpoleEarinspect(void)
{
	return 42;
}

// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 
CpoleEarinspect::CpoleEarinspect()
{
	return;
}


POLEEARINSPECT_API bool poleEarDetectionInit(const stInputParameters& inPara)
{

	return true;

}

POLEEARINSPECT_API int poleEarDetection(stInputParameters& inPara, stOuputParameters& outPara)
{
	//初始化日志文件
	char cs[1024];
	SYSTEMTIME st = { 0 };
	GetLocalTime(&st);
	sprintf(cs, "D://Save//%d-%d-%d.log", st.wYear, st.wMonth, st.wDay);
	//out.open(cs, std::ios::out | std::ios::app);

	detection inspect;
	if (bInitial)
	{
		if (inPara.iMethode ==1)        //初始化DL算法,加载DL模型
		{
			//inspect.detectionPoleEar(inPara, outPara);
		}

		bInitial = false;
	}

	if (inPara.iMethode == -1)              //PB模型学习算法
	{
		
	}
	else if (inPara.iMethode == 1)
	{
		inspect.detectionPoleEar(inPara, outPara);
	}
	
	return 1;
}


  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

砥锷磨志

你的鼓励将是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值