eigenface c++

自己写了一下eigenface的代码,读取at.txt文件中的图片路径,然后进行pca降维,再进行识别。

原理网上很多,这里就不写了。

主要是为了学习思路,所以矩阵变换,取行列计算之类的都是手写的,代码比较繁杂。 

imread读取灰度图片是CV_8UC1,而矩阵计算时不能直接计算,要转换成CV_64FC1。8UC1的范围时0~255,64FC1范围是0~1,要注意。

这里训练用的图片是orl训练集中的s10~s19 中的1.bmp,共十张,记为第一到第十张图片,所以在识别图片的时候,输出的是他是第几张图片,可以按自己的需求更改代码。

如果有问题或错误欢迎联系,一定及时更改。

#include <iostream>
#include <opencv.hpp>
#include <vector>
#include <string>
#include <opencv2/highgui/highgui_c.h>//CV_Error的头文件
#include <math.h>
#define k_weidu 5//降维后的维度
#define image_col 92
#define image_row 112

using namespace std;
using namespace cv;


string file_name = "H:\\ORL\\at.txt";
string test_file = "H:/ORL/s15/3.bmp";

//输入矩阵 每列为一张图片 如n*m即有m张图片 每张图片的像素为n
void PCA_test(Mat& item);
//读取txt文件中的所有图片路径
Mat read_image(string file_name);
void sort_mat(Mat value, vector<int>& ij);
//将图片矩阵变为一个向量
vector<uchar> mattovec(string image_name);
//
Mat vectomat(vector<uchar> vec);
int main(void)
{
    //读取图片
	Mat item = read_image(file_name);
    //进行降维与训练
	PCA_test(item);
	//输入需要识别的图片
	waitKey(0);
	return 0;
}
//原图片是112*92  输入十张人脸
//item 行数为92*112 列数为照片个数
//返回特征向量
void PCA_test(Mat& item)
{
	//算出m个向量的平均向量 avg
	//初始化赋值0
	Mat item_s = item.clone();
	vector<uchar> avg(item.rows);
	for (int i, j = 0; j < item.rows; j++)
	{
		int sum = 0;
		for (i = 0; i < item.cols; i++)
		{
			sum += item.at<uchar>(j, i);
		}
		avg[j] = sum / item.cols;
	}
	//显示平均脸
	Mat avg_mat = vectomat(avg);
	namedWindow("avg");
	imshow("avg", avg_mat);

	//用原矩阵的每个向量减去平均向量
	//含义:每个人脸向量减去平均向量的向量差
	for (int i, j = 0; j < item.cols; j++)
		for (i = 0; i < item.rows; i++)
		{
			item.at<uchar>(i, j) -= avg[i];
		}
	//原矩阵的转置矩阵
	Mat item_t = item.t();
	//灰度或rgb图像的颜色分量都在0~255之间。
	//CV_8UC1的取值范围正好,因此直接imshow就可以显示图像了。
	//CV_64FC1取值范围远远不止0~255,需要先归一化成0~1.0
	//imshow的时候会把图像x255后再显示。
	item.convertTo(item, CV_64FC1, 1.0 / 255.0);
	//求协方差 协方差矩阵为n*n
	Mat item_cov;
	Mat means1;//协方差,均值
	calcCovarMatrix(item, item_cov, means1, COVAR_NORMAL | COVAR_ROWS);
	//求协方差矩阵的特征值和特征向量
	//计算后 特征值是一个一维列向量
	//特征向量是一个n*n矩阵 每一列对应一个特征值
	//eigenvalue 是Mat<double>
	Mat eigenvalue, eigenvector;
	eigen(item_cov, eigenvalue, eigenvector);
	//n个特征向量
	Mat eigen_mat(item_s.rows, eigenvector.cols, CV_64FC1, Scalar(0));
	//n*m m*m = eigen_mat 特征向量为n*m n维 即为特征脸
	eigen_mat = item * eigenvector;
	eigen_mat.convertTo(eigen_mat, CV_8UC1, 255.0);
	//输出特征脸(每一个特征向量转化成图像)
	for (int i, j = 0; j < eigenvector.cols; j++)
	{
		vector<uchar> eigen_face;
		Mat eigen_face_mat;
		for (i = 0; i < eigen_mat.rows; i++)
		{
			eigen_face.push_back(eigen_mat.at<uchar>(i, j));
		}
		eigen_face_mat = vectomat(eigen_face);
		string a = "eigen_face" + to_string(j);
		namedWindow(a);
		imshow(a, eigen_face_mat);
	}
	vector<int> ij;
	//将特征值排序 大的在前
	Mat v = eigenvalue.clone();
	sort_mat(v, ij);
	//将矩阵降维为N*K
	Mat PCA_k(eigen_mat.rows, k_weidu, CV_8UC1, Scalar(0));
	for (int i, j = 0; j < k_weidu; j++)
	{
		for (i = 0; i < eigen_mat.rows; i++)
		{
			PCA_k.at<uchar>(i, j) = eigen_mat.at<uchar>(i, ij[j]);
		}
	}
	//读入要识别的图片
	Mat im = imread(test_file, 0);
	namedWindow("scr");
	imshow("scr", im);

	Mat im_sub_avg(im.rows * im.cols, 1, CV_8UC1, Scalar(0));
	//将图片转为向量 92*112 1 并计算与平均脸的差值
	for (int i, j = 0; j < im.cols; j++)
	{
		for (i = 0; i < im.rows; i++)
		{
			im_sub_avg.at<uchar>(j * im.rows + i, 0) = im.at<uchar>(i, j) - avg[(int)(j * im.rows + i)];
		}
	}
	//转换成float类型进行计算
	im_sub_avg.convertTo(im_sub_avg, CV_64FC1, 1.0 / 255.0);
	PCA_k.convertTo(PCA_k, CV_64FC1, 1.0 / 255.0);
	Mat PCA_k_t = PCA_k.t();
	//用特征向量表示该脸 K*N N*1 = K*1 
	Mat im_eigen = PCA_k_t * im_sub_avg;
	//对最初的M每个图片同上求K维的权重 并求其与它的欧氏距离
	vector<int> euclidean_metric;
	item.convertTo(item, CV_8UC1,255.0);
	for (int i, j = 0; j < item.cols; j++)
	{
		Mat temp(im.rows * im.cols, 1, CV_8UC1, Scalar(0));
		for (int ii = 0; ii < temp.rows; ii++)
			temp.at<uchar>(ii, 0) = item.at<uchar>(ii, j);
		temp.convertTo(temp, CV_64FC1, 1.0 / 255.0);
		Mat temp_eigen = PCA_k_t * temp;
		//temp_eigen.convertTo(temp_eigen, CV_8UC1,255.0);
		double em = 0;
		for (i = 0; i < temp_eigen.rows; i++)
		{
            //为了防止越界,除个1000,对后边数值比较没有影响
			double x = temp_eigen.at<double>(i, 0);
			double y = im_eigen.at<double>(i, 0);
			double t = y - x;
			em += pow(t, 2);
		}
		euclidean_metric.push_back(em);
		cout << "与第" << j + 1 << "张照片的欧氏距离: " << em << endl;
	}
	int minPosition = min_element(euclidean_metric.begin(), euclidean_metric.end()) - euclidean_metric.begin() + 1;
	cout << "识别人脸的图片为第" << minPosition << endl;
}
Mat read_image(string file_name)
{
	ifstream file(file_name.c_str(), ifstream::in);
	if (!file)
	{
		string error_message = "No valid input file was given, please check the given filename.";
		CV_Error(CV_StsBadArg, error_message);
	}
	string line;
	vector<vector<uchar>> vec;
	while (getline(file, line)) //从文本文件中读取一行字符,未指定限定符默认限定符为“/n”
	{
		vec.push_back(mattovec(line));
	}
	Mat item((vec[0]).size(), vec.size(), CV_8UC1, Scalar(0));
	for (int i, j = 0; j < item.cols; j++)
	{
		for (i = 0; i < item.rows; i++)
		{
			item.at<uchar>(i, j) = vec[j][i];
		}
	}
	return item;
}
//选择排序 大的在前
void sort_mat(Mat value, vector<int>& ij)
{
	for (int i, j = 0; j < value.rows; j++)
	{
		int d = 0;//实时记录最大值的坐标
		//temp实时记录最大值
		double temp = value.at<double>(0, 0);
		for (i = 1; i < value.rows - 1; i++)
		{
			if (value.at<double>(i, 0) > temp)
			{
				temp = value.at<double>(i, 0);
				d = i;
			}
		}
		//遍历后得到最大的 将坐标压入队列
		ij.push_back(d);
		value.at<double>(d, 0) = 0;
	}
}
vector<uchar> mattovec(string image_name)
{
	Mat image = imread(image_name, 0);
	if (image.empty())
	{
		string a = "could not load " + image_name;
		printf(a.c_str());
		exit(0);
	}
	vector<uchar> vec;
	//一列接一列
	for (int i, j = 0; j < image.cols; j++)
	{
		for (i = 0; i < image.rows; i++)
		{
			vec.push_back(image.at<uchar>(i, j));
		}
	}
	return vec;
}
Mat vectomat(vector<uchar> vec)
{
	Mat mat(image_row, image_col, CV_8UC1, Scalar(0));
	for (int i, j = 0; j < image_col; j++)
	{
		for (i = 0; i < image_row; i++)
		{
			mat.at<uchar>(i, j) = vec[j * (int)image_row + i];
		}
	}
	return mat;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

若水菱花

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

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

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

打赏作者

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

抵扣说明:

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

余额充值