学习机器学习课程,你肯定会接触到一些传统的分类器,如: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;
}