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

news2024/11/26 1:55:04

🌈个人主页: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算法的过程中,深入思考了计算效率的问题,包括代码优化和在大规模数据集上高效运行。这种深度思考不仅提升了编程技能,还让对算法实现的可扩展性有了更深刻的认识。


📝总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二维码智慧门牌管理系统:实现数据通信安全

文章目录 前言一、传输隧道加密技术二、传输数据加密技术三、数据接入鉴权技术 前言 随着信息技术的蓬勃发展&#xff0c;智慧门牌管理系统已成为现代商业和家庭重要的一部分。然而&#xff0c;系统普及的同时也带来了数据通信安全的突出问题。为了解决这一挑战&#xff0c;二…

【设计模式-2.5】创建型——建造者模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型设计模式中的最后一个&#xff0c;建造者模式&#xff1b; 入学报道 创建型模式&#xff0c;关注于对象的创建&#xff0c;建造者模式也不例外。假设现在有一个场景&#xff0c;高校开学&#xff0c;学生、教师、职工都…

【新版HI3559AV100开发注意事项(二)】

#新版HI3559AV100开发注意事项&#xff08;二&#xff09; 十一、请问海思HI3559AV100 SPC030资料里面的HI3559ADMEB_VER_C_PCB.pcb是用什么软件打开啊&#xff1f; 答&#xff1a;PADS VX 2.2 Altium designer 十二、hi3559级联问题请教 在SDK的文档中只看到了两块Hi3559板…

ARM GIC(一) cortex-A 处理器中断简介

对于ARM的处理器,中断给处理器提供了触觉,使处理器能够感知到外界的变化,从而实时的处理。本系列博文,是以ARM cortex-A系列处理器,来介绍ARM的soc中,中断的处理。 ARM cortex-A系列处理器,提供了4个管脚给soc,实现外界中断的传递。分别是: nIRQ: 物理普通中断 nF…

利用Spark构建房价分析与推荐系统:基于58同城数据的大数据实践

利用Spark构建房价分析与推荐系统&#xff1a;基于58同城数据的大数据实践 基于Spark的房价数据分析预测推荐系统引言技术栈功能概述项目实现1. 数据爬取与处理2. 大数据分析与可视化3. 房价预测模型4. 协同过滤推荐系统5. Web应用开发6. 数据管理与用户管理 总结与展望 基于Sp…

优维科技荣获第二届中国赛宝信息技术应用创新优秀解决方案三等奖

近日&#xff0c;“第二届中国赛宝信息技术应用创新优秀解决方案”评选活动圆满结束。优维科技所提交的《Hyperlnsight超融合持续观测解决方案》、《EasyOps一体化运维平台》从全国近300份申报方案中脱颖而出&#xff0c;荣获2023中国赛宝信息技术应用创新优秀解决方案奖。 本…

Python - 深夜数据结构与算法之 Tree

目录 一.引言 二.树与二叉树简介 1.Tree 树 2.Binary Tree 二叉树 3.Binary Search Tree 二叉搜索树 三.经典算法实战 1.In-Order-Traversal [94] 2.Pre-Order-Traversal [144] 3.Fib [509] 4.N-Tree-Pre-Order-Traversal [589] 5.N-Tree-Post-Order-Traversal [590…

改变传媒格局的新趋势

在如今信息高速发展的时代&#xff0c;人们早已进入了一个以手机为中心的智能化时代。随着科技的迅猛发展&#xff0c;手机无人直播成为了一种新兴的传媒形态&#xff0c;正逐渐改变着传媒格局。本文将从手机无人直播的定义、发展背景和影响等方面进行探讨。 首先&#xff0c;…

浏览器缓存机制(详)

目录 1&#xff0c;缓存的分类1.1&#xff0c;按缓存位置1&#xff0c;Service Worker2&#xff0c;Memory Cache3&#xff0c;Disk Cache4&#xff0c;Push Cache 1.2&#xff0c;按缓存类型强缓存ExpiresCache-control 协商缓存Last-Modified & If-Modified-SinceEtag &a…

【优化】XXLJOB修改为使用虚拟线程

【优化】XXLJOB修改为使用虚拟线程 新建这几个目录 类&#xff0c; 去找项目对应的xxljob的源码 主要是将 new Thread 改为 虚拟线程 Thread.ofVirtual().name("VT").unstarted 以下代码是 xxljob 2.3.0版本 举一反三 去修改对应版本的代码 <!-- 定…

UG螺旋线命令的使用

螺旋线按照螺距类型可以分为两种类型&#xff1a; 1、等螺距螺旋线 2、变螺距螺旋线 等螺距螺旋线 变螺距螺旋线 沿矢量螺旋线 沿矢量螺旋线-线性大小和螺距 沿矢量螺旋线-沿脊线的线性 沿矢量螺旋线-沿脊线的线性 当我们想模拟弹簧被拉伸或压缩状态时&#xff0c;可以使用…

Python-基于fastapi实现SSE流式返回(类似GPT)

最近在做大模型对话相关功能&#xff0c;需要将对话内容流式返回给前端页面&#xff08;类似GPT的效果&#xff09;。下面直接说下如何实现&#xff1a; 1.首先导入fastapi和sse流式返回所需要的包 from fastapi import APIRouter, Response, status from sse_starlette.sse …

智能化运输与航空航天:发展历程、问题与未来趋势

导言 智能化运输与航空航天是当前科技领域的研究热点之一&#xff0c;本文将深入研究这一领域的发展历程、遇到的问题、解决过程&#xff0c;以及未来的可用范围。同时&#xff0c;我们将探讨各国在这一领域的应用情况和未来的研究趋势&#xff0c;分析在哪些方面能够取胜&…

Java操作Word修订功能:启用、接受、拒绝、获取修订

Word的修订功能是一种在文档中进行编辑和审阅的功能。它允许多个用户对同一文档进行修改并跟踪这些修改&#xff0c;以便进行审查和接受或拒绝修改。修订功能通常用于团队合作、专业编辑和文件审查等场景。 本文将从以下几个方面介绍如何使用免费工具Free Spire.Doc for Java在…

数据挖掘体系介绍

数据挖掘是什么&#xff1f; 简而言之&#xff0c;对数据进行挖掘&#xff0c;从中提取出有效的信息。一般我们会把这种信息通过概念、规则、规律、模式等有组织的方式展示出来&#xff0c;形成所谓的知识。特别是在这个大数据时代&#xff0c;当数据多到一定程度&#xff0c;…

shell 数组的详细用法

简介 数组是一种数据结构&#xff0c;用于存储和处理一组相关的数据元素。数组可以包含多个值&#xff0c;每个值都有一个索引&#xff0c;用于标识和访问它们。 目录 1. 数组的基本用法 1.1. 定义数组的方式 1.1.1. 直接赋值 1.1.2. declare声明数组 1.1.3. 索引赋值 1.…

山景DU561—32位高性能音频处理器(DSP)芯片

音频处理可以更好地捕捉和处理声音和音乐&#xff1b;而DSP音频处理芯片是一种利用数字信号处理技术进行音频处理的专用芯片&#xff1b;可用于多种应用&#xff0c;从音乐拾音到复杂的音频信号处理&#xff0c;和声音增强。 由工采网代理的山景DU561是一款集成多种音效算法高…

YOLOv5改进 | 卷积篇 | 通过RFAConv重塑空间注意力(深度学习的前沿突破)

一、本文介绍 本文给大家带来的改进机制是RFAConv&#xff0c;全称为Receptive-Field Attention Convolution&#xff0c;是一种全新的空间注意力机制。与传统的空间注意力方法相比&#xff0c;RFAConv能够更有效地处理图像中的细节和复杂模式(适用于所有的检测对象都有一定的…

CentOS安装Python解释,CentOS设置python虚拟环境,linux设置python虚拟环境

一、安装python解释器 1、创建解释器安装的目录&#xff1a;/usr/local/python39 cd /usr/local mkdir python39 2、下载依赖 yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel xz-devel …

MyBatis——MyBatis的ORM映射和MyBatis的配置文件升级

1.MyBatis的ORM映射 拷贝之前的工程&#xff1a; 1.1.什么是ORM映射 MyBatis只能自动维护库表”列名“与”属性名“相同时的对应关系&#xff0c;二者不同时无法自动ORM&#xff0c;如下&#xff1a; 1.2.列的别名 在SQL中使用 as 为查询字段添加列别名&#xff0c;以匹配…