-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit dfbb95d
Showing
172 changed files
with
349 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/* | ||
!.gitignore | ||
!main.cpp | ||
!Eigenface.cpp | ||
!PCA.cpp | ||
!Eigenface.h | ||
!PCA.h | ||
!yalefaces/ | ||
!README.md | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#include <Eigenface.h> | ||
using cv::Mat; | ||
int judge(const Mat &src,const Mat* eigenVector, const Mat inputAvgVector[], int m) | ||
{ | ||
int ret = 0; | ||
double err_min = -1; | ||
Mat srcFloat,srcFloatNormalized; | ||
src.convertTo(srcFloat,CV_64FC1,1,0); | ||
dbg("\n"); | ||
for (size_t i = 0; i < m; i++) | ||
{ | ||
const Mat &tmp = eigenVector[i]; | ||
srcFloatNormalized = srcFloat - cv::repeat(inputAvgVector[i],1,srcFloat.cols); | ||
Mat srcHat = tmp.t() * (tmp * srcFloatNormalized); | ||
double dis = cv::norm(srcFloatNormalized,srcHat,cv::NORM_L2SQR); | ||
if(err_min == -1 || dis < err_min) | ||
{ | ||
dbg("i=%zd,m=%d,err_min=%lf,dis=%lf\n",i,m,err_min,dis); | ||
err_min = dis; | ||
ret = i; | ||
} | ||
} | ||
return ret; | ||
} | ||
|
||
int train(const Mat src[],size_t cls, Mat outputEigenVector[],Mat outputAvgVector[],double threshold) | ||
{ | ||
Mat *p = new Mat[cls]; | ||
Mat *reducedP = new Mat[cls]; | ||
|
||
for (size_t i = 0; i < cls; i++) | ||
{ | ||
//转double才能计算特征值 | ||
src[i].convertTo(p[i],CV_64FC1); | ||
//求各列的均值,结果为列向量 | ||
cv::reduce(p[i],outputAvgVector[i],1,cv::REDUCE_AVG); | ||
//各列减去均值 | ||
reducedP[i] = p[i] - cv::repeat(outputAvgVector[i],1,src[i].cols); | ||
} | ||
|
||
for (size_t i = 0; i < cls; i++) | ||
outputEigenVector[i] = PCAeigenVector(reducedP[i],threshold); | ||
delete[] p; | ||
delete[] reducedP; | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#include <PCA.h> | ||
|
||
|
||
/** | ||
* @param src 测试图片,列向量 | ||
* @param eigenVector 特征向量,按行组成 | ||
* @param inputAvgVector 均值,列向量 | ||
* @param m eigenVector的维数 | ||
*/ | ||
int judge(const cv::Mat &src,const cv::Mat eigenVector[], const cv::Mat inputAvgVector[], int m); | ||
|
||
|
||
/** | ||
* @param src 矩阵数组,每个元素是一类图片 | ||
* @param cls src的元素个数 | ||
* @param outputEigenVector 输出的特征向量矩阵 | ||
* @param outputAvgVector 输出的均值向量,列向量 | ||
* @param threshold PCA的阈值,默认0.95 | ||
* @return void, always 0 | ||
*/ | ||
int train(const cv::Mat src[],size_t cls, cv::Mat outputEigenVector[],cv::Mat outputAvgVector[],double threshold = 0.95); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#include <PCA.h> | ||
|
||
cv::Mat PCAeigenVector(const cv::Mat &src, double threshold) | ||
{ | ||
cv::Mat eigenVector, eigenValue, covMatrix; | ||
if (src.rows <= src.cols) | ||
{ | ||
dbg("src.rows < src.cols\n"); | ||
covMatrix = src * src.t(); | ||
//这里计算出的特征向量就是行向量,特征值只有一列,并且特征值是降序保存的 | ||
cv::eigen(covMatrix, eigenValue, eigenVector); | ||
} | ||
else | ||
{ | ||
dbg("src.rows > src.cols\n"); | ||
covMatrix = src.t() * src; | ||
cv::eigen(covMatrix, eigenValue, eigenVector); | ||
//上面求出的特征向量是行向量,计算时要改成列向量,算完后再改成行向量 | ||
|
||
//正则化 | ||
cv::Mat eigenVectorNotNormedRow = (src * eigenVector.t()).t(); | ||
cv::Mat eigenVectorNormedRow(eigenVectorNotNormedRow.rows,eigenVectorNotNormedRow.cols,eigenVectorNotNormedRow.type()); | ||
cv::Mat norms(eigenVectorNotNormedRow.rows,1,eigenVectorNotNormedRow.type()); | ||
for (size_t i = 0; i < eigenVectorNotNormedRow.rows; i++) | ||
{ | ||
norms.at<double>(i,0) = cv::norm(eigenVectorNotNormedRow.rowRange(i,i+1),cv::NORM_L2); | ||
} | ||
|
||
eigenVectorNormedRow = eigenVectorNotNormedRow / cv::repeat(norms,1,eigenVectorNotNormedRow.cols); | ||
eigenVector = eigenVectorNormedRow; | ||
/* 无正则化 | ||
cv::Mat tttmp = (src * eigenVector.t()).t(); | ||
eigenVector = tttmp; | ||
*/ | ||
// eigenVector = (src * eigenVector.t()).t(); | ||
} | ||
dbg("eigenVector has cal.\n"); | ||
double sum = 0, nsum = 0; | ||
int k = 1; | ||
for (size_t i = 0; i < eigenValue.rows; i++) | ||
sum += eigenValue.at<double>(i, 0); | ||
for (; k <= eigenValue.rows; k++) | ||
{ | ||
|
||
nsum += eigenValue.at<double>(k-1, 0); | ||
if (nsum / sum >= threshold) | ||
break; | ||
} | ||
|
||
cv::Mat retEigenVector = eigenVector.rowRange(0,k); | ||
return retEigenVector; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#include <opencv2/opencv.hpp> | ||
#ifdef _DEBUG | ||
#define dbg(...) printf(__VA_ARGS__) | ||
#else | ||
#define dbg(...) | ||
#endif | ||
/** | ||
* @param src 待降维的矩阵,由列向量组成,必须是浮点数 | ||
* @param threshold 默认0.95 | ||
* @return 协方差矩阵的特征向量矩阵,行向量 | ||
*/ | ||
cv::Mat PCAeigenVector(const cv::Mat & src, double threshold = 0.95); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
[![LICENSE](https://img.shields.io/badge/license-Anti%20996-blue.svg?style=flat-square)](https://github.com/996icu/996.ICU/blob/master/LICENSE) | ||
# EigenFace | ||
一个Eigenface算法的实现 | ||
|
||
使用`opencv 4.5.2`,`msvc 19.28.29912`编译 | ||
|
||
# 编译命令 | ||
|
||
## Release版 | ||
进入`msvc x64`控制台,将`opencv_world452.lib`放在当前目录,执行 | ||
```shell | ||
cl /EHsc /nologo /std:c++17 /O1 /MD /GF /I . /Fe: main.exe main.cpp Eigenface.cpp PCA.cpp | ||
``` | ||
|
||
## Debug版 | ||
进入`msvc x64`控制台,将`opencv_world452.lib`放在当前目录,执行 | ||
```shell | ||
cl /EHsc /nologo /std:c++17 /Od /MDd /D _DEBUG /GF /I . /Fe: main.exe main.cpp Eigenface.cpp PCA.cpp | ||
``` | ||
# 运行 | ||
|
||
默认用3个类做测试 | ||
```shell | ||
main.exe | ||
``` | ||
|
||
使用10个类做测试 | ||
```shell | ||
main.exe -n=10 | ||
``` | ||
指定70%的数据做训练集 | ||
```shell | ||
main.exe -r=0.7 -n=10 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
#include <Eigenface.h> | ||
#ifdef _DEBUG | ||
#pragma comment(lib, "./opencv_world452d.lib") | ||
#else | ||
#pragma comment(lib, "./opencv_world452.lib") | ||
#endif | ||
using cv::Mat, cv::imread, cv::imshow, cv::imwrite; | ||
using std::cout, std::cin, std::endl, std::string, std::cerr; | ||
|
||
const string keys = | ||
"{r | 0.9 | trainSet percent}" | ||
"{i | .\\yalefaces\\| inputPath}" | ||
"{n | 3 | test case classes}" | ||
"{t | 0.95 |threshold}"; | ||
|
||
int check(bool f) | ||
{ | ||
if (!f) | ||
{ | ||
cerr << "error occur" << endl; | ||
exit(1); | ||
} | ||
return 0; | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
dbg("start"); | ||
|
||
//args parse | ||
cv::CommandLineParser parse(argc, argv, keys); | ||
string pathPrev = parse.get<string>("i"); | ||
double trainSetPercent = parse.get<double>("r"); | ||
double threshold = parse.get<double>("t"); | ||
int n = parse.get<int>("n"); | ||
|
||
std::vector<string> picPost = { | ||
"glasses", | ||
"happy", | ||
"leftlight", | ||
"noglasses", | ||
"normal", | ||
"rightlight", | ||
"sad", | ||
"sleepy", | ||
"surprised", | ||
"wink", | ||
"centerlight"}; | ||
|
||
// | ||
std::vector<std::vector<string>> trainSet; | ||
std::vector<string> testSet; | ||
std::vector<int> labels; | ||
srand(time(nullptr)); | ||
|
||
char buf[255]; | ||
|
||
dbg("trainset start"); | ||
|
||
for (size_t i = 1; i < n + 1; i++) | ||
{ //在每个分类中选 | ||
trainSet.push_back(std::vector<string>()); | ||
int trainCnt = trainSetPercent * 11; | ||
bool trainSelMask[11]; | ||
memset(trainSelMask, 0, sizeof(trainSelMask)); | ||
//选择特定比例的训练集 | ||
for (size_t j = 0; j < trainCnt;) | ||
{ | ||
int ran = rand() % picPost.size(); | ||
if (!trainSelMask[ran]) | ||
{ | ||
sprintf(buf, "%ssubject%02zd.%s.bmp", pathPrev.c_str(), i, picPost[ran].c_str()); | ||
trainSet[i - 1].push_back(buf); | ||
// trainSet.push_back(buf); | ||
trainSelMask[ran] = true; | ||
dbg(buf); | ||
j++; | ||
} | ||
} | ||
//剩下的作为测试集 | ||
for (size_t j = 0; j < 11; j++) | ||
{ | ||
if (!trainSelMask[j]) | ||
{ | ||
sprintf(buf, "%ssubject%02zd.%s.bmp", pathPrev.c_str(), i, picPost[j].c_str()); | ||
testSet.push_back(buf); | ||
labels.push_back(i); | ||
dbg(buf); | ||
} | ||
} | ||
} | ||
|
||
|
||
//n个特征向量矩阵,k*N^2 | ||
Mat *trainResult = new Mat[n]; | ||
|
||
//n个输入训练矩阵,每列一张图,N^2*m | ||
Mat *src = new Mat[n]; | ||
|
||
//均值,N^2*1 | ||
Mat *avgVector = new Mat[n]; | ||
|
||
//中间结果,将src[i]按列拆分就是这个 | ||
Mat *colVectorsByClass = new Mat[trainSetPercent * 11]; | ||
|
||
int kk = 0; | ||
|
||
dbg("序列化开始"); | ||
|
||
for (auto &it : trainSet) | ||
{ | ||
int i = 0; | ||
|
||
for (auto &tpic : it) | ||
{ | ||
dbg("111"); | ||
//循环读取该类的图像到内存中 | ||
Mat tmp = imread(tpic, cv::IMREAD_GRAYSCALE); | ||
|
||
check(tmp.data != nullptr); | ||
dbg(tpic.c_str()); | ||
//序列化矩阵为列向量 | ||
Mat colVector = tmp.reshape(0, tmp.rows * tmp.cols); | ||
//序列化结果保存在数组中 | ||
colVectorsByClass[i] = colVector; | ||
i++; | ||
} | ||
//每一类拼接成一个列向量矩阵 | ||
cv::hconcat(colVectorsByClass, i, src[kk]); | ||
kk++; | ||
} | ||
|
||
dbg("train start"); | ||
train(src, n, trainResult,avgVector, threshold); | ||
dbg("train finish"); | ||
|
||
for (size_t i = 0; i < n; i++) | ||
{ | ||
for (size_t j = 0; j < trainResult[i].rows; j++) | ||
{ | ||
Mat newFaceCol, newFace; | ||
trainResult[i].row(j).convertTo(newFaceCol, CV_8U); | ||
newFace = newFaceCol.reshape(0, 243); | ||
sprintf(buf, "./featureFace/%zd-%zd.jpg", i, j); | ||
imwrite(buf, newFace); | ||
} | ||
} | ||
|
||
/*Mat newFaceCol, newFace; | ||
trainResult[0].row(0).convertTo(newFaceCol, CV_8U); | ||
newFace = newFaceCol.reshape(0, 243); | ||
imshow("newFace", newFace);*/ | ||
// cv::waitKey(); | ||
|
||
int predictSum = 0; | ||
|
||
for (size_t i = 0; i < testSet.size(); i++) | ||
{ | ||
Mat judgeSrc = imread(testSet[i], cv::IMREAD_GRAYSCALE); | ||
check(judgeSrc.data != nullptr); | ||
Mat judgeCol = judgeSrc.reshape(0, judgeSrc.cols * judgeSrc.rows); | ||
int judgeRet = judge(judgeCol, trainResult,avgVector, n); | ||
// cout << testSet[i] << " is judged as in the same class with " << trainSet[judgeRet][0] << endl; | ||
printf("predict:\t%d\tGT:\t%d\n",judgeRet + 1,labels[i]); | ||
if(judgeRet + 1 == labels[i]) | ||
predictSum++; | ||
} | ||
|
||
printf("predict true :%d / %zd\n",predictSum,testSet.size()); | ||
delete[] colVectorsByClass; | ||
delete[] trainResult; | ||
delete[] src; | ||
return 0; | ||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.