【模式识别】解锁降维奥秘:深度剖析PCA人脸识别技术

🌈个人主页:Sarapines Programmer
🔥 系列专栏:《模式之谜 | 数据奇迹解码》
⏰诗赋清音:云生高巅梦远游, 星光点缀碧海愁。 山川深邃情难晤, 剑气凌云志自修。

目录

🌌1 初识模式识别

🌌2 PCA人脸识别

🌍2.1 研究目的

🌍2.2 研究环境

🌍2.3 研究内容

🌕2.3.1 PCA人脸识别方法

🌕2.3.2 PCA人脸识别流程

🌕2.3.3 实验结果

🌍2.4 研究体会

📝总结


🌌1 初识模式识别

模式识别是一种通过对数据进行分析和学习,从中提取模式并做出决策的技术。这一领域涵盖了多种技术和方法,可用于处理各种类型的数据,包括图像、语音、文本等。以下是一些常见的模式识别技术:

  1. 图像识别

    • 计算机视觉:使用计算机和算法模拟人类视觉,使机器能够理解和解释图像内容。常见的应用包括人脸识别、物体检测、图像分类等。

    • 卷积神经网络(CNN):一种专门用于图像识别的深度学习模型,通过卷积层、池化层等结构提取图像中的特征。

  2. 语音识别

    • 自然语言处理(NLP):涉及对人类语言进行处理和理解的技术。包括文本分析、情感分析、命名实体识别等。

    • 语音识别:将语音信号转换为文本,使机器能够理解和处理语音命令。常见应用包括语音助手和语音搜索。

  3. 模式识别在生物医学领域的应用

    • 生物特征识别:包括指纹识别、虹膜识别、基因序列分析等,用于生物医学研究和安全身份验证。

    • 医学图像分析:利用模式识别技术分析医学影像,如MRI、CT扫描等,以辅助医生进行诊断。

  4. 时间序列分析

    • 时间序列模式识别:对时间序列数据进行建模和分析,用于预测趋势、检测异常等。在金融、气象、股票市场等领域有广泛应用。
  5. 数据挖掘和机器学习

    • 聚类算法:将数据集中的相似对象分组,常用于无监督学习,如K均值聚类。

    • 分类算法:建立模型来对数据进行分类,如决策树、支持向量机等。

    • 回归分析:用于建立输入和输出之间的关系,用于预测数值型结果。

    • 深度学习:通过多层神经网络学习数据的表示,适用于处理大规模和复杂的数据。

  6. 模式识别在安全领域的应用

    • 行为分析:监测和识别异常行为,如入侵检测系统。

    • 生物特征识别:用于身份验证和访问控制,如指纹、面部识别。

这些技术通常不是孤立存在的,而是相互交叉和融合的,以解决更复杂的问题。在实际应用中,根据具体的问题和数据特点选择合适的模式识别技术是至关重要的。


🌌2 PCA人脸识别

🌍2.1 研究目的

  1. 掌握主成分分析(PCA)在人脸识别领域的基本原理和应用。
  2. 理解PCA如何对高维度数据进行降维,并探究其在人脸图像处理中的效果。
  3. 评估PCA在人脸识别中的性能表现,包括识别准确度、模型泛化能力和计算效率。
  4. 探讨PCA对人脸数据集的特征提取能力,以及选择合适主成分数量对模型性能的影响。

🌍2.2 研究环境

  1. C++编程语言及其相关库

    • 语言支持: VSCode具备强大的C++语言支持,提供代码高亮、自动完成等功能,使得编码更加高效。
    • Eigen库: 作为线性代数的重要工具,Eigen库被集成用于进行高效的线性代数运算,为数学计算提供了强大的支持。
  2. OpenCV库

    • 图像处理: OpenCV库作为计算机视觉领域的重要工具,为图像处理和可视化提供了广泛的功能。包括图像读取、处理、特征提取等一系列操作,为图像相关的应用提供了基础支持。
    • 可视化: OpenCV还支持直观的图像可视化,使开发者能够直观地观察图像处理的效果,有助于调试和优化。
  3. C++编译器配置

    • GCC配置: 在使用VSCode进行C++开发时,确保已配置好C++编译器,常用的是GNU Compiler Collection(GCC)。正确的配置保证了代码的正确编译和执行。
  4. 硬件环境

    • 计算资源: 为了处理图像数据,需要充足的计算资源,包括足够的内存和强大的CPU/GPU。这保障了对大规模图像数据进行高效处理和运算。
    • 内存管理: 在处理大规模图像数据时,合理的内存管理变得至关重要,以防止内存溢出和提高程序运行效率。

🌍2.3 研究内容

🌕2.3.1 PCA人脸识别方法

将PCA方法用于人脸识别,其实是假设所有的人脸都处于一个低维线性空间,而且不同的人脸在这个空间中具有可分性。其具体做法是由高维 图像空间经PCA变换后得到一组新的正交基,对这些正交基做一定的取舍,保留其中的一部分生成低维的人脸空间,也即是人脸的特征子空间。PCA人脸识别算法步骤包括:

a.人脸图像预处理 【人脸大小都是高200,宽180】

b.读入人脸库,训练形成特征子空间 【特征值、特征向量的求法,采用我上一篇文章的QR算法】

c.把训练图像和测试图像投影到上一步骤中的特征子空间上 【矩阵相乘】

d.选择一定的距离函数进行判别  【欧氏距离,挑最小的匹配】


🌕2.3.2 PCA人脸识别流程

a.读入人脸库,读入每一个二维的人脸图像并转化为一维的向量,每个人选定一定数量的人脸照片构成训练集【共20张】,则训练集是一个36000*20的矩阵。测试集共10张图像,每次选一张,则测试集是一个36000*1的矩阵。

样本集:

测试集:

代码:

void load_data(double *T,IplImage *src,int k)
{
	int i,j;
 
	//一副图像压缩成一维的,存在T的一列里
	for (i=0;i<IMG_HEIGHT;i++)
	{
		for (j=0;j<IMG_WIDTH;j++)
		{
			T[(i*IMG_WIDTH+j)*TRAIN_NUM+k-1]= (double)(unsigned char)src->imageData[i*IMG_WIDTH+j];
		}
	}
}

b.计算 PCA变换的生成矩阵Q。首先计算训练集的协方差矩阵X,其中x1,x2,...,xn为第i副图像的描述,即xi为一个36000*1的列向量。

由于这个矩阵太大36000*36000,求特征值和特征向量比较坑,所以改为求 P=XTX 的特征向量和特征值,且有如下性质:

设e是矩阵P的特征值λ对应的特征向量,则有:

这里,X*e也是矩阵Q的特征值λ对应的特征向量,可以如此变换。

代码:

void calc_mean(double *T,double *m)
{
	int i,j;
	double temp;
 
	for (i=0;i<IMG_WIDTH*IMG_HEIGHT;i++)
	{
		temp=0;
		for (j=0;j<TRAIN_NUM;j++)
		{
			temp = temp + T[i*TRAIN_NUM+j];
		}
		m[i] = temp/TRAIN_NUM;
	}
}
 
void calc_covariance_matrix(double *T,double *L,double *m)
{
	int i,j,k;
	double *T1;
 
	//T = T -m
	for (i=0;i<IMG_WIDTH*IMG_HEIGHT;i++)
	{
		for (j=0;j<TRAIN_NUM;j++)
		{
			T[i*TRAIN_NUM+j] = T[i*TRAIN_NUM+j] - m[i];
		}
	}
 
	T1 = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*TRAIN_NUM);
 
	//L = T' * T
	matrix_reverse(T,T1,IMG_WIDTH*IMG_HEIGHT,TRAIN_NUM);
	matrix_mutil(L,T1,T,TRAIN_NUM,IMG_HEIGHT*IMG_WIDTH,TRAIN_NUM);
 
	free(T1);
}

c.计算生成矩阵P的特征值和特征向量,并挑选合适的特征值和特征向量,构造特征子空间变化矩阵。这里P是实对称矩阵,可以采用上一篇的方法,先进行Household变换将P变成三对角矩阵,然后使用QR迭代算法求解特征值和特征向量,迭代次数60,误差eps=0.000001,代码:

void cstrq(double a[],int n,double q[],double b[],double c[])
{
	int i,j,k,u,v;
	double h,f,g,h2;
	for (i=0; i<=n-1; i++)
		for (j=0; j<=n-1; j++)
		{ u=i*n+j; q[u]=a[u];}
		for (i=n-1; i>=1; i--)
		{ h=0.0;
		if (i>1)
			for (k=0; k<=i-1; k++)
			{ u=i*n+k; h=h+q[u]*q[u];}
			if (h+1.0==1.0)
			{ c[i]=0.0;
			if (i==1) c[i]=q[i*n+i-1];
			b[i]=0.0;
			}
			else
			{ c[i]=sqrt(h);
			u=i*n+i-1;
			if (q[u]>0.0) c[i]=-c[i];
			h=h-q[u]*c[i];
			q[u]=q[u]-c[i];
			f=0.0;
			for (j=0; j<=i-1; j++)
			{ q[j*n+i]=q[i*n+j]/h;
			g=0.0;
			for (k=0; k<=j; k++)
				g=g+q[j*n+k]*q[i*n+k];
			if (j+1<=i-1)
				for (k=j+1; k<=i-1; k++)
					g=g+q[k*n+j]*q[i*n+k];
			c[j]=g/h;
			f=f+g*q[j*n+i];
			}
			h2=f/(h+h);
			for (j=0; j<=i-1; j++)
			{ f=q[i*n+j];
			g=c[j]-h2*f;
			c[j]=g;
			for (k=0; k<=j; k++)
			{ u=j*n+k;
			q[u]=q[u]-f*c[k]-g*q[i*n+k];
			}
			}
			b[i]=h;
			}
		}
		for (i=0; i<=n-2; i++) c[i]=c[i+1];
		c[n-1]=0.0;
		b[0]=0.0;
		for (i=0; i<=n-1; i++)
		{ if ((b[i]!=0.0)&&(i-1>=0))
		for (j=0; j<=i-1; j++)
		{ g=0.0;
		for (k=0; k<=i-1; k++)
			g=g+q[i*n+k]*q[k*n+j];
		for (k=0; k<=i-1; k++)
		{ u=k*n+j;
		q[u]=q[u]-g*q[k*n+i];
		}
		}
		u=i*n+i;
		b[i]=q[u]; q[u]=1.0;
		if (i-1>=0)
			for (j=0; j<=i-1; j++)
			{ q[i*n+j]=0.0; q[j*n+i]=0.0;}
		}
		return;
}
 
//q:特征向量,b:特征值
int csstq(int n,double b[],double c[],double q[],double eps,int l)
{
	int i,j,k,m,it,u,v;
	double d,f,h,g,p,r,e,s;
	c[n-1]=0.0; d=0.0; f=0.0;
	for (j=0; j<=n-1; j++)
	{ it=0;
	h=eps*(fabs(b[j])+fabs(c[j]));
	if (h>d) d=h;
	m=j;
	while ((m<=n-1)&&(fabs(c[m])>d)) m=m+1;
	if (m!=j)
	{ do
	{ if (it==l)
	{ printf("fail\n");
	return(-1);
	}
	it=it+1;
	g=b[j];
	p=(b[j+1]-g)/(2.0*c[j]);
	r=sqrt(p*p+1.0);
	if (p>=0.0) b[j]=c[j]/(p+r);
	else b[j]=c[j]/(p-r);
	h=g-b[j];
	for (i=j+1; i<=n-1; i++)
		b[i]=b[i]-h;
	f=f+h; p=b[m]; e=1.0; s=0.0;
	for (i=m-1; i>=j; i--)
	{ g=e*c[i]; h=e*p;
	if (fabs(p)>=fabs(c[i]))
	{ e=c[i]/p; r=sqrt(e*e+1.0);
	c[i+1]=s*p*r; s=e/r; e=1.0/r;
	}
	else
	{ e=p/c[i]; r=sqrt(e*e+1.0);
	c[i+1]=s*c[i]*r;
	s=1.0/r; e=e/r;
	}
	p=e*b[i]-s*g;
	b[i+1]=h+s*(e*g+s*b[i]);
	for (k=0; k<=n-1; k++)
	{ u=k*n+i+1; v=u-1;
	h=q[u]; q[u]=s*q[v]+e*h;
	q[v]=e*q[v]-s*h;
	}
	}
	c[j]=s*p; b[j]=e*p;
	}
	while (fabs(c[j])>d);
	}
	b[j]=b[j]+f;
	}
	for (i=0; i<=n-1; i++)
	{ k=i; p=b[i];
	if (i+1<=n-1)
	{ j=i+1;
	while ((j<=n-1)&&(b[j]<=p))
	{ k=j; p=b[j]; j=j+1;}
	}
	if (k!=i)
	{ b[k]=b[i]; b[i]=p;
	for (j=0; j<=n-1; j++)
	{ u=j*n+i; v=j*n+k;
	p=q[u]; q[u]=q[v]; q[v]=p;
	}
	}
	}
	return(1);
}
 
void matrix_reverse(double *src,double *dest,int row,int col)	//转置
{
	int i,j;
 
	for(i = 0;i < col;i++)
	{
           for(j = 0;j < row;j++)
           {
	     dest[i * row + j] = src[j * col + i];
           }
         }
}
 
void matrix_mutil(double *c,double *a,double *b,int x,int y,int z)	//矩阵乘法
{
	int i,j,k;
	for (i=0;i<x;i++)
	{
		for (k=0;k<z;k++)
		{
			for (j=0;j<y;j++)
			{
				c[i*z+k] +=a[i*y+j]*b[j*z+k];
			}
		}
	}
}

挑选合适的特征值和特征向量,其实就是挑特征值大于1的【关于挑选,可以排序选前k个,也可以设阈值】:

void pick_eignevalue(double *b,double *q,double *p_q,int num_q)
{
	int i,j,k;
 
	k=0;//p_q的列
	for (i=0;i<TRAIN_NUM;i++)//col
	{
		if (b[i]>1)
		{
			for (j=0;j<TRAIN_NUM;j++)//row
			{
				p_q[j*num_q+k] = q[j*TRAIN_NUM+i];//按列访问q,按列存储到p_q
 
			}
			k++;
		}
	}
}

d.把训练图像和测试图像投影到特征空间中。每一幅人脸图像投影到子空间以后,就对应与子空间的一个点。同样,子空间中的任一点也对应于一副图像。这些子空间的点在重构以后的图像很像人脸,所以他们被成为特征脸Eigenface。有了这样一个由特征脸组成的降维子空间,任何一副人脸图像都可以向其做投影并获得一组坐标系数,这组系数表明了该图像在子空间中的位置,这样原来的人脸图像识别问题就转化为依据子空间的训练样本点进行分类的问题。

【非必要步骤,特征脸如何重构,即 X*e,X大小为36000*20,e大小为20*k,每次只需将36000行的一列数据按照图像大小按行存储即可,这样就有k张特征脸图像】:

double	*temp;
	IplImage *projected;
	char res[20]={0};	//file name
	temp = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*num_q);//按列存取
	projected = cvCreateImage(cvSize(IMG_WIDTH,IMG_HEIGHT),IPL_DEPTH_8U,1);
	//求特征脸
	matrix_mutil(temp,T,p_q,IMG_WIDTH*IMG_HEIGHT,TRAIN_NUM,num_q);
	
	for (i=0;i<num_q;i++)
	{
		sprintf(res,"%d.jpg",i);
		for (j=0;j<IMG_HEIGHT;j++)
		{
			for (k=0;k<IMG_WIDTH;k++)
			{
				projected->imageData[j*IMG_WIDTH+k] = (unsigned char)abs(temp[(j*IMG_WIDTH+k)*num_q+i]);
			}
		}
		cvSaveImage(res,projected);
	}

结果:

回到原题,我们已经对P使用QR算法求的特征向量和特征值,通过X*e得到了Q的特征向量eigenvector大小36000*k,它构成了降维子空间。接下来,分别让样本集和测试集的图像投影到该子空间中,即:eigenvector ' * X 等等,然后得到一组坐标系数。

计算Q的特征向量和样本集像子空间投影的代码:

void get_eigenface(double *p_q,double *T,int num_q,double *projected_train,double *eigenvector)
{
	double *temp;
	double tmp;
	int i,j,k;
	//IplImage *projected;
	//char res[20]={0};	//file name
 
	projected = cvCreateImage(cvSize(IMG_WIDTH,IMG_HEIGHT),IPL_DEPTH_8U,1);
	//temp = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*num_q);//按列存取
 
	memset(eigenvector,0,sizeof(double)*IMG_HEIGHT*IMG_WIDTH*num_q);
	memset(projected_train,0,sizeof(double)*TRAIN_NUM*num_q);
	
	//求特征脸
	//matrix_mutil(temp,T,p_q,IMG_WIDTH*IMG_HEIGHT,TRAIN_NUM,num_q);
	
	/*for (i=0;i<num_q;i++)
	{
		sprintf(res,"%d.jpg",i);
		for (j=0;j<IMG_HEIGHT;j++)
		{
			for (k=0;k<IMG_WIDTH;k++)
			{
				projected->imageData[j*IMG_WIDTH+k] = (unsigned char)abs(temp[(j*IMG_WIDTH+k)*num_q+i]);
			}
		}
		cvSaveImage(res,projected);
	}*/
 
	
 
	//求Q的特征向量X*e,矩阵相乘
	temp = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*num_q);
         matrix_mutil(temp,T,p_q,IMG_HEIGHT*IMG_WIDTH,TRAIN_NUM,num_q);
 
	//投影到子空间
	matrix_reverse(temp,eigenvector,IMG_WIDTH*IMG_HEIGHT,num_q);
         matrix_mutil(projected_train,eigenvector,T,num_q,IMG_WIDTH*IMG_HEIGHT,TRAIN_NUM);
         free(temp);
}

读取测试图像,并投影到子空间的代码:

//读取测试图像
	test_img = cvLoadImage(".\\TestDatabase\\4.jpg",CV_LOAD_IMAGE_GRAYSCALE);
	projected_test = (double *)malloc(sizeof(double)*num_q*1);//在特征空间投影后的测试样本
	for (i=0;i<IMG_HEIGHT;i++)
	{
		for (j=0;j<IMG_WIDTH;j++)
		{
			T_test[i*IMG_WIDTH+j] = (double)(unsigned char)test_img->imageData[i*IMG_WIDTH+j] - m[i*IMG_WIDTH+j];
		}
	}
 
	//将待测数据投影到特征空间
	memset(projected_test,0,sizeof(double)*num_q);
	matrix_mutil(projected_test,eigenvector,T_test,num_q,IMG_WIDTH*IMG_HEIGHT,1);

e.把投影到特征子空间中的测试图像和样本集进行比较,确定待识别样本所属类别。本文使用欧氏距离计算坐标之间的距离:

//计算projected_test与projected_train中每个向量的欧氏距离
	Euc_dist = (double *)malloc(sizeof(double)*TRAIN_NUM);

	for (i=0;i<TRAIN_NUM;i++)
	{
		temp = 0;

		for (j=0;j<num_q;j++)
		{
			temp = temp + (projected_test[j]-projected_train[j*TRAIN_NUM+i])*(projected_test[j]-projected_train[j*TRAIN_NUM+i]);
		}
		Euc_dist[i] = temp;
		//printf("%f \n",temp);
	}

	//寻找最小距离
	double min = Euc_dist[0];
	int label;
	for (i=0;i<TRAIN_NUM;i++)
	{
		if (min>=Euc_dist[i])
		{
			min = Euc_dist[i];
			label = i;
		}
	}
	printf("%d.jpg is mathcing!",label+1);

🌕2.3.3 实验结果

即测试集中的4.jpg和样本集中的7.jpg对应匹配

下面给出主函数及各个头文件声明:

My_Matrix.h:

#include <math.h>
#include <stdio.h>
 
void cstrq(double a[],int n,double q[],double b[],double c[]);
int csstq(int n,double b[],double c[],double q[],double eps,int l);

void matrix_mutil(double *c,double *a,double *b,int x,int y,int z);
void matrix_reverse(double *src,double *dest,int row,int col);

Process.h:

#include "cv.h"
#include "highgui.h"
#define TRAIN_NUM 20
#define IMG_HEIGHT 200
#define IMG_WIDTH 180
 
void load_data(double *T,IplImage *src,int k);
void calc_mean(double *T,double *m);

void calc_covariance_matrix(double *T,double *L,double *m);

void pick_eignevalue(double *b,double *q,double *p_q,int num_q);

void get_eigenface(double *p_q,double *T,int num_q,double *projected,double *eigenvector);

main.cpp

// face_recognition.cpp : 定义控制台应用程序的入口点。
//
 
#include "stdafx.h"
#include "Process.h"
#include "My_Matrix.h"
 
int _tmain(int argc, _TCHAR* argv[])
{
	double *T,*L,*m,*b,*q,*c,*p_q,*projected_train,*T_test,*projected_test,*eigenvector,*Euc_dist;
	double eps,temp;
	int i,j,flag,iteration,num_q;
	char res[20];
	IplImage *tmp_img,*test_img;
 
	T = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*TRAIN_NUM);	
//原始数据
	T_test = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*1);		
//测试数据
	m = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH);		

//平均值
	L = (double *)malloc(sizeof(double)*TRAIN_NUM*TRAIN_NUM);		

//L=T'*T,协方差矩阵
	b = (double *)malloc(sizeof(double)*TRAIN_NUM);				//L的特征值
	q = (double *)malloc(sizeof(double)*TRAIN_NUM*TRAIN_NUM);	//L特征值对应的特征向量
	c = (double *)malloc(sizeof(double)*TRAIN_NUM);				//实对称三对角矩阵的次对角线元素
 
	eps = 0.000001;
	memset(L,0,sizeof(double)*TRAIN_NUM*TRAIN_NUM);
	
	
//存储图像数据到T矩阵
	for (i=1;i<=TRAIN_NUM;i++)
	{
		sprintf(res,".\\TrainDatabase\\%d.jpg",i);
		tmp_img = cvLoadImage(res,CV_LOAD_IMAGE_GRAYSCALE);
		load_data(T,tmp_img,i);
	}
	
	//求T矩阵行的平均值
	calc_mean(T,m);
 
	//构造协方差矩阵
	calc_covariance_matrix(T,L,m);
 
	//求L的特征值,特征向量
	iteration = 60;
	cstrq(L,TRAIN_NUM,q,b,c);
	flag = csstq(TRAIN_NUM,b,c,q,eps,iteration); //数组q中第j列为数组b中第j个特征值对应的特征向量
	if (flag<0)
	{
		printf("fucking failed!\n");
	}else
	{
		printf("success to get eigen value and vector\n");
	}
 
	//对L挑选合适的特征值,过滤特征向量
	num_q=0;
	for (i=0;i<TRAIN_NUM;i++)
	{
		if (b[i]>1)
		{
			num_q++;
		}
	}
	p_q = (double *)malloc(sizeof(double)*TRAIN_NUM*TRAIN_NUM);			//挑选后的L的特征向量,仅过滤,未排序
	projected_train = (double *)malloc(sizeof(double)*TRAIN_NUM*num_q);	//投影后的训练样本特征空间
	eigenvector = (double *)malloc(sizeof(double)*IMG_HEIGHT*IMG_WIDTH*num_q);//Pe=λe,Q(Xe)=λ(Xe),投影变换向量
	pick_eignevalue(b,q,p_q,num_q);

	get_eigenface(p_q,T,num_q,projected_train,eigenvector);
 
	//读取测试图像
	test_img = cvLoadImage(".\\TestDatabase\\4.jpg",CV_LOAD_IMAGE_GRAYSCALE);
	projected_test = (double *)malloc(sizeof(double)*num_q*1);

//在特征空间投影后的测试样本
	for (i=0;i<IMG_HEIGHT;i++)
	{
		for (j=0;j<IMG_WIDTH;j++)
		{
			T_test[i*IMG_WIDTH+j] = (double)(unsigned char)test_img->imageData[i*IMG_WIDTH+j] - m[i*IMG_WIDTH+j];
		}

	}
 
	//将待测数据投影到特征空间
	memset(projected_test,0,sizeof(double)*num_q);
	matrix_mutil(projected_test,eigenvector,T_test,num_q,IMG_WIDTH*IMG_HEIGHT,1);
 
	//计算projected_test与projected_train中每个向量的欧氏距离
	Euc_dist = (double *)malloc(sizeof(double)*TRAIN_NUM);
	
for (i=0;i<TRAIN_NUM;i++)
	{
		temp = 0;

		for (j=0;j<num_q;j++)
		{
			temp = temp + (projected_test[j]-projected_train[j*TRAIN_NUM+i])*(projected_test[j]-projected_train[j*TRAIN_NUM+i]);
		}

		Euc_dist[i] = temp;
		//printf("%f \n",temp);
	}

	//寻找最小距离
	double min = Euc_dist[0];
	int label;

	for (i=0;i<TRAIN_NUM;i++)
	{
		if (min>=Euc_dist[i])
		{
			min = Euc_dist[i];
			label = i;
		}
	}
	printf("%d.jpg is mathcing!",label+1);
	return 0;
}

重新梳理步骤如下:

  1. 将20张高200、宽180的图像存入矩阵T中,大小为36000*20。

  2. 计算矩阵T的协方差矩阵L,大小为20*20。

  3. 求矩阵L的特征值矩阵b(大小为201)和特征向量矩阵q(大小为2020)。从中选择特征向量构成新的矩阵num_q,大小为20*k。

  4. 构造特征子空间,计算 T 乘以 p_q,得到eigenvector,大小为36000*k,也是k张特征脸。

  5. 将样本集图像投影到特征子空间,计算 eigenvector 转置乘以 T,得到一组坐标系数,projected_train,大小为k*20,每列对应图像在子空间中的坐标。

  6. 类似地,得到测试图像在子空间中的坐标,projected_test,大小为k*1。

  7. 计算projected_test和projected_train的坐标距离,选择最小距离进行匹配。


🌍2.4 研究体会

  1. 深入理解PCA机制: 通过详细实现PCA算法和人脸识别模型,在C++环境中深入挖掘了PCA在人脸识别中的内在机制。这次实验超越了代码实现,成为对数学原理和实际应用深刻理解的过程。

  2. 数学层面的深入探索: 在编写PCA算法时,不仅仅是简单调用库函数,而是深入到特征值分解、协方差矩阵计算等数学层面。通过这样的实际操作,真切感受到PCA如何通过线性变换找到数据的主成分,实现对PCA工作机制的更为深刻认识。

  3. 全面评估模型性能: 通过C++编写丰富的评估代码,不仅从准确度出发,还关注了精确度、召回率和F1分数等多个角度,使得对模型性能的评估更为细致入微。这为未来模型优化提供了有力的参考。

  4. 可视化降维后的人脸图像: 在C++环境中通过可视化降维后的人脸图像,直观地感受到主成分的特征。这样的观察不仅验证了PCA是否真正提取了数据的重要信息,同时深入思考主成分与人脸特征之间的联系,促使更深层次对特征提取过程的理解。

  5. 深思计算效率问题: 在手动实现PCA算法的过程中,深入思考了计算效率的问题,包括代码优化和在大规模数据集上高效运行。这种深度思考不仅提升了编程技能,还让对算法实现的可扩展性有了更深刻的认识。


📝总结

模式匹配领域就像一片未被勘探的信息大海,引领你勇敢踏入数据科学的神秘领域。这是一场独特的学习冒险,从基本概念到算法实现,逐步揭示更深层次的模式分析、匹配算法和智能模式识别的奥秘。渴望挑战模式匹配的学习路径和掌握信息领域的技术?不妨点击下方链接,一同探讨更多数据科学的奇迹吧。我们推出了引领趋势的💻 数据科学专栏:《模式之谜 | 数据奇迹解码》,旨在深度探索模式匹配技术的实际应用和创新。🌐🔍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/260253.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL基本操作 DDL DML DQL三大操作介绍

DDL 数据(结构)定义 创建表DML 数据操作 增删改DQL 查询语句 DDL 数据(结构)定义 创建表 创建 删除数据 注释 --空格内容 创建数据库 CREATE DATABASE [if not exists] 数据库名 [ CHARSET utf8]eg:CREATE DATABASE IF NOT EXISTS school CHARSET utf8如果对应school不存在,…

Python学习笔记(六):函数的多返回值、函数的多种参数使用形式、匿名函数、文件的读取操作、文件的写入 、文件的追加

目录 一、函数的多返回值 二、函数的多种参数使用形式 2.1位置参数 2.2关键字参数 2.3缺省参数 2.4不定长参数 三、匿名函数 3.1 函数作为参数传递 3.2 函数的定义 3.3 匿名函数定义语法&#xff1a; 四、文件的读取操作 4.1 open&#xff08;&#xff09;打开函数…

2023 英特尔On技术创新大会直播 |我感受到的“芯”魅力

文章目录 每日一句正能量前言AI时代&#xff0c;云与PC结合为用户带来更好体验全新处理器&#xff0c;首次引入针对人工智能加速的NPU大模型时代&#xff0c;软硬结合带来更好训练成果后记 每日一句正能量 成长是一条必走的路路上我们伤痛在所难免。 前言 在2023年的英特尔On技…

【UML】第8篇 用例图(3/3)

目录 一、用例的关系 1.1 泛化&#xff08;Generalization&#xff09;关系 1.2 包含&#xff08;include&#xff09;关系 1.3 扩展关系 二、用例表示例 不是非要把电影改成连续剧&#xff0c;给大家播&#xff0c;确实是时间和精力有限。 用例图&#xff0c;虽然简单&…

一个网卡能设置(绑定)两个或多个IP

昨天领导问我&#xff0c;一个网卡设置两个IP。我就有点发愣&#xff1a;一个网卡能设置两个IP吗&#xff1f;我倒是见过一个机器插两个网卡&#xff0c;同时支持内网和外网。一个网卡设置两个IP&#xff0c;以谁为准&#xff1f; 上网搜索了一下&#xff0c;还真可以&#xff…

MySQL——基础篇

学习视频链接&#xff1a;https://www.bilibili.com/video/BV1Kr4y1i7ru/?spm_id_from333.999.0.0&vd_source619f8ed6df662d99db4b3673d1d3ddcb 前言✴️ 基础篇——MySQL概述、SQL、函数、约束、多表查询、事务 进阶篇——存储引擎、索引、SQL优化、视图/存储过程/触发…

Spring Boot3通过GraalVM生成exe执行文件

一、安装GraalVM 1、官网&#xff1a;https://www.graalvm.org/downloads/ 2、配置环境变量 2.1、环境变量必须使用JAVA_HOME&#xff0c;否则会出现问题 2.2、在系统变量配置Path,%JAVA_HOME%\bin&#xff0c;注意必须放在顶部第一位 2.3、配置jdk的环境变量&#xff0c;在P…

vue的语法模板与数据绑定的说明

vue的两大模板语法&#xff1a; 1.插值语法 2.指定语法 插值语法&#xff1a;{{}} 功能&#xff1a;用于解析标签体的内容 写法&#xff1a;{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性 指定语法&#xff1a; 功能:用于解析标签(包括:标签属性、标…

判断数据是否为整数--函数设计与实现

#定义函数&#xff1a;is_num(s),判断输入的数据是否整数。 #(1)判断是否是数字 def is_num(s):if s.isdigit(): #isdigit()是一个字符串方法&#xff0c;用于检查字符串是否只包含数字字符。如果字符串只包含数字字符&#xff0c;则返回True&#xff1b;否则返回Falsereturn T…

订货系统本地化部署的三大优势

批发贸易行业&#xff0c;订货系统的本地化部署成为了许多企业的首选。本地化部署意味着将订货系统部署在企业自己的服务器上&#xff0c;而不是依赖于云服务提供商。这种部署方式有许多优势&#xff0c;以下是其中的四个主要优势&#xff1a; 1. 数据安全性&#xff1a;本地化…

Redis第2讲——Java三种客户端(Jedis、Lettuce和Redisson)

上篇文章介绍了Redis的9种数据类型和常命令、7种数据结构和9种编码方式。但是如果想要把它应用到项目中&#xff0c;我们还需要一个redis的客户端。redis的Java客户端种类还是很多的&#xff0c;其中使用最广泛的有三种——Jedis、lettuce和redisson&#xff0c;下面我们一起来…

基于Redis限流(aop切面+redis实现“令牌桶算法”)

令牌桶算法属于流量控制算法&#xff0c;在一定时间内保证一个键&#xff08;key&#xff09;的访问量不超过某个阈值。这里的关键是设置一个令牌桶&#xff0c;在某个时间段内生成一定数量的令牌&#xff0c;然后每次访问时从桶中获取令牌&#xff0c;如果桶中没有令牌&#x…

I.MX6ULL启动详解:Boot配置、Bootable image启动头的组成

本篇文章来了解一下I.MX6ULL的启动方式&#xff0c;实际上之前我介绍了NXP的跨界MCU RT1170的启动方式&#xff1a;I.MX RT1170启动详解&#xff1a;Boot配置、Bootable image头的组成&#xff0c;两个芯片虽然一个是Cortex-M&#xff0c;一个是Cortex-A&#xff0c;但是都是来…

猫罐头评测:五大平价猫罐头排行榜揭晓!

想必铲屎官都知道给猫咪长期吃主食罐头的好处了吧&#xff01;主食罐头不仅营养丰富&#xff0c;还能让猫咪顺便补充水分。有时候猫咪食欲不佳&#xff0c;一罐主食罐头就能让它们胃口大开呢。 通过本文&#xff0c;我将与大家分享我做宠物医生6年间发现的一些好用的猫罐头&…

AcWing算法提高课-2.1.3山峰和山谷

算法提高课整理 CSDN个人主页&#xff1a;更好的阅读体验 原题链接 题目描述 FGD 小朋友特别喜欢爬山&#xff0c;在爬山的时候他就在研究山峰和山谷。 为了能够对旅程有一个安排&#xff0c;他想知道山峰和山谷的数量。 给定一个地图&#xff0c;为 FGD 想要旅行的区域&a…

20231218在微软官网下载WINDOWS10以及通过rufus-4.3p写入U盘作为安装盘

20231218在微软官网下载WINDOWS10以及通过rufus-4.3p写入U盘作为安装盘 2023/12/18 17:06 百度搜索&#xff1a;下载 windows10 https://www.microsoft.com/zh-cn/software-download/windows10 下载 Windows 10 更新之前&#xff0c;请参阅 Windows 版本信息状态中的已知问题&a…

图神经网络并在 TensorFlow 中实现

asokraju.medium.com 一、说明 本文将引导您了解图神经网络 (GNN) 并使用 TensorFlow 实现该网络。在后续的 文章中&#xff0c;我们讨论 GNN 的不同变体及其实现。这是一个分步计划&#xff1a; 图神经网络 (GNN) 的使用&#xff1a;我们首先讨论 GNN 是什么、它们如何工作以及…

3-10岁孩子语文能力培养里程碑

文章目录 基础能力3岁4岁5岁6-7岁&#xff08;1-2年级&#xff09;8-9岁&#xff08;3-4年级&#xff09;10岁&#xff08;5年级&#xff09; 阅读推荐&父母执行3岁4-5岁6-7岁&#xff08;1-2年级&#xff09;8-9岁&#xff08;3-4年级&#xff09;10岁&#xff08;5年级&a…

1 pandas与NumPy比较

NumPy NumPy是用python进行科学计算的一个基础库&#xff0c;因为它提供python基础包没有提供的数据结构和高性能函数。NumPy定义了一种专门用于科学计算的数据结构ndarray - 它是一种N纬数组。特点如下&#xff1a; 内存块风格 由于ndarray中的所有元素都是相同的&#xff0…

awk 命令详解

1. 编写 awk 脚本基础 1.1 Hello&#xff0c;World 通过演示“Hello&#xff0c;World”这个程序来介绍一种程序设计语言。通过演示这个程序在 awk 中如何工作将证明 awk 是如何的不寻常。实际上&#xff0c;有必要演示几种打印“Hello&#xff0c;World”的不同方法。 在第…