[译]人脸检测与人脸识别简介

2023-11-09

From: http://www.shervinemami.co.cc/faceRecognition.html

Translated by 11

“人脸识别”是一个在计算机视觉和生物特征识别领域十分活跃的话题。这个主题已经被给力地研究了25年,并且最终在安全、机器人学、人机交互、数码摄像机、游戏和娱乐领域得到了广泛应用。

“人脸识别”大致可分为两个阶段:

1.人脸检测 搜索一幅图像,寻找一切人脸区域(此处以绿色矩形显示),然后进行图像处理,清理脸部图像以便于更好地识别。

 

2.人脸识别 把上一阶段检测处理得到的人脸图像与数据库中的已知人脸进行比对,判定人脸对应的人是谁(此处以红色文本显示)。

2002年后,人脸检测已经可以相当可靠地运作。比如OpenCV的Face Detector,对于一个人直视摄像头得到的较清晰图片,大约有90-95%的准确度。通常来说,当人以侧面对准摄像头或与摄像头成一定角度时,较难检测到人脸,有时需要3D Head Pose Estimation。假如图片亮度不是很好,也较难检测到人脸。脸部的部分区域比另一部分区域明亮,带有阴影,模糊,或者戴眼镜,也会影响检测效果。

然而,人脸识别却比人脸检测不可靠得多,一般只有30-70%的准确度。20世纪90年代以来,人脸识别一直是一个很重要的研究领域,但仍然十分不可靠,并且每一年都有更多的识别技术被创造,如文章底部所列出的(Alternatives to Eigenfaces such as 3D face recognition or recognition from video.)

我将向你展示如何使用“特征脸”(Eigenfaces),也称为主元分析法(Principal Component Analysis or PCA)。相对于普通的神经网络方法(Neural Networks)和Fisher Faces方法来说,这是一个简单和流行的对图片进行的二维人脸识别的方法。

要学习特征脸方法的理论,你需要阅读Face Recognition With Eigenface from Servo Magazine (April 2007),可能还需要一些数学算法。

首先我将向你解释,怎样实现特征脸的命令行离线训练(offline training from the command-line),基于Servo Magazine tutorial and source-code (May 2007)。

之后,我将说明如何将此扩展成为从网络摄像头进行实时的在线训练:-)

使用OpenCV的Face Detector检测人脸

如上所述,人脸识别的第一个阶段是人脸检测。OpenCV库使得使用它的Haar Cascade Face Detector(也称为Viola-Jones方法)检测正面人脸变得相当简单。

OpenCV里的“cvHaarDetectObjects”函数执行人脸检测,但是这个函数直接用没有意义,所以最好用这个包装好的函数:

// Perform face detection on the input image, using the given Haar Cascade.
// Returns a rectangle for the detected region in the given image.
CvRect detectFaceInImage(IplImage *inputImg, CvHaarClassifierCascade* cascade)
{
	// Smallest face size.
	CvSize minFeatureSize = cvSize(20, 20);
	// Only search for 1 face.
	int flags = CV_HAAR_FIND_BIGGEST_OBJECT | CV_HAAR_DO_ROUGH_SEARCH;
	// How detailed should the search be.
	float search_scale_factor = 1.1f;
	IplImage *detectImg;
	IplImage *greyImg = 0;
	CvMemStorage* storage;
	CvRect rc;
	double t;
	CvSeq* rects;
	CvSize size;
	int i, ms, nFaces;

	storage = cvCreateMemStorage(0);
	cvClearMemStorage( storage );

	// If the image is color, use a greyscale copy of the image.
	detectImg = (IplImage*)inputImg;
	if (inputImg->nChannels > 1) {
		size = cvSize(inputImg->width, inputImg->height);
		greyImg = cvCreateImage(size, IPL_DEPTH_8U, 1 );
		cvCvtColor( inputImg, greyImg, CV_BGR2GRAY );
		detectImg = greyImg;	// Use the greyscale image.
	}

	// Detect all the faces in the greyscale image.
	t = (double)cvGetTickCount();
	rects = cvHaarDetectObjects( detectImg, cascade, storage,
			search_scale_factor, 3, flags, minFeatureSize);
	t = (double)cvGetTickCount() - t;
	ms = cvRound( t / ((double)cvGetTickFrequency() * 1000.0) );
	nFaces = rects->total;
	printf("Face Detection took %d ms and found %d objectsn", ms, nFaces);

	// Get the first detected face (the biggest).
	if (nFaces > 0)
		rc = *(CvRect*)cvGetSeqElem( rects, 0 );
	else
		rc = cvRect(-1,-1,-1,-1);	// Couldn't find the face.

	if (greyImg)
		cvReleaseImage( &greyImg );
	cvReleaseMemStorage( &storage );
	//cvReleaseHaarClassifierCascade( &cascade );

	return rc;	// Return the biggest face found, or (-1,-1,-1,-1).
}

// Perform face detection on the input image, using the given Haar Cascade.
// Returns a rectangle for the detected region in the given image.
CvRect detectFaceInImage(IplImage *inputImg, CvHaarClassifierCascade* cascade)
{
	// Smallest face size.
	CvSize minFeatureSize = cvSize(20, 20);
	// Only search for 1 face.
	int flags = CV_HAAR_FIND_BIGGEST_OBJECT | CV_HAAR_DO_ROUGH_SEARCH;
	// How detailed should the search be.
	float search_scale_factor = 1.1f;
	IplImage *detectImg;
	IplImage *greyImg = 0;
	CvMemStorage* storage;
	CvRect rc;
	double t;
	CvSeq* rects;
	CvSize size;
	int i, ms, nFaces;

	storage = cvCreateMemStorage(0);
	cvClearMemStorage( storage );

	// If the image is color, use a greyscale copy of the image.
	detectImg = (IplImage*)inputImg;
	if (inputImg->nChannels > 1) {
		size = cvSize(inputImg->width, inputImg->height);
		greyImg = cvCreateImage(size, IPL_DEPTH_8U, 1 );
		cvCvtColor( inputImg, greyImg, CV_BGR2GRAY );
		detectImg = greyImg;	// Use the greyscale image.
	}

	// Detect all the faces in the greyscale image.
	t = (double)cvGetTickCount();
	rects = cvHaarDetectObjects( detectImg, cascade, storage,
			search_scale_factor, 3, flags, minFeatureSize);
	t = (double)cvGetTickCount() - t;
	ms = cvRound( t / ((double)cvGetTickFrequency() * 1000.0) );
	nFaces = rects->total;
	printf("Face Detection took %d ms and found %d objectsn", ms, nFaces);

	// Get the first detected face (the biggest).
	if (nFaces > 0)
		rc = *(CvRect*)cvGetSeqElem( rects, 0 );
	else
		rc = cvRect(-1,-1,-1,-1);	// Couldn't find the face.

	if (greyImg)
		cvReleaseImage( &greyImg );
	cvReleaseMemStorage( &storage );
	//cvReleaseHaarClassifierCascade( &cascade );

	return rc;	// Return the biggest face found, or (-1,-1,-1,-1).
}


现在如果你想要在一张图片里寻找人脸,只需要简单地调用“detectFaceInImage”函数。你也需要指定OpenCV使用的人脸分类器(Face Classifier)。比如,OpenCV自带了一些用于正面脸的分类器,也有一些用于侧面脸的,还有眼睛检测,鼻检测,嘴检测,全身检测等等。你实际上可以任意把其它的分类检测器用于此函数,甚至创造你自己定制的分类检测器,比如车或人的检测(阅读此处),但既然正脸检测是唯一十分可靠的,这将是我们唯一要讨论的。

对于正面人脸检测,你应该选取这些OpenCV自带的haar级联分类器(Haar Cascade Classifiers,in the “datahaarcascades” folder)。

  • “haarcascade_frontalface_default.xml”
  • “haarcascade_frontalface_alt.xml”
  • “haarcascade_frontalface_alt2.xml”
  • “haarcascade_frontalface_alt_tree.xml”
每个haar级联分类器都将给出略微不同的结果,这依赖于你的环境因素,所以你甚至可以用全部分类器,把结果结合在一起(如果你想要做尽可能多地检测)。有一些更多的用于眼睛,头部,嘴巴,鼻子的分类器在 Modesto’s page下载。
你可以在你的程序里这样做来进行人脸检测:
// Haar Cascade file, used for Face Detection.
char *faceCascadeFilename = "haarcascade_frontalface_alt.xml";
// Load the HaarCascade classifier for face detection.
CvHaarClassifierCascade* faceCascade;
faceCascade = (CvHaarClassifierCascade*)cvLoad(faceCascadeFilename, 0, 0, 0);
if( !faceCascade ) {
	printf("Couldnt load Face detector '%s'n", faceCascadeFilename);
	exit(1);
}

// Grab the next frame from the camera.
IplImage *inputImg = cvQueryFrame(camera);

// Perform face detection on the input image, using the given Haar classifier
CvRect faceRect = detectFaceInImage(inputImg, faceCascade);

// Make sure a valid face was detected.
if (faceRect.width > 0) {
	printf("Detected a face at (%d,%d)!n", faceRect.x, faceRect.y);
}

.... Use 'faceRect' and 'inputImg' ....

// Free the Face Detector resources when the program is finished
cvReleaseHaarClassifierCascade( &cascade );


对脸部图像进行预处理以便于识别

现在你已经检测到一张人脸,你可以使用那张人脸图片进行人脸识别。然而,假如你尝试这样简单地从一张普通图片直接进行人脸识别的话,你将会至少损失10%的准确率!

在一个人脸识别系统中,应用多种预处理技术对将要识别的图片进行标准化处理是极其重要的。多数人脸识别算法对光照条件十分敏感,所以假如在暗室训练,在明亮的房间就可能不会被识别出来等等。这个问题可归于“lumination dependent”,并且还有其它很多例子,比如脸部也应当在图片的一个十分固定的位置(比如眼睛位置为相同的像素坐标),固定的大小,旋转角度,头发和装饰,表情(笑,怒等),光照方向(向左或向上等),这就是在进行人脸识别前,使用好的图片预处理过滤器十分重要的原因。你还应该做一些其它事情,比如去除脸部周围的多余像素(如用椭圆遮罩,只显示其内部的人脸区域而不是头发或图片背景,因为他们的变化多于脸部区域)。

为简单起见,我展示给你的人脸识别系统是使用灰度图像的特征脸方法。所以我将向你说明怎样简单地把彩色图像转化为灰度图像,并且之后简单地使用直方图均衡化(Histogram Equalization)作为一种自动的标准化脸部图像亮度和对比度的方法。为了得到更好的结果,你可以使用彩色人脸识别(color face recognition,ideally with color histogram fitting in HSV or another color space instead of RGB),或者使用更多的预处理,比如边缘增强(edge enhancement),轮廓检测(contour detection),手势检测(motion detection),等等。这份代码把图片调整成一个标准的大小,但是可能会改变脸的纵横比(aspect ratio)。你可以阅读我这里的教程HERE,来了解怎样调整图像大小而不改变它的纵横比。

你可以看到一个预处理阶段的例子:

这是把一幅RGB格式的图像或灰度图像转变为灰度图像的基本代码。它还把图像调整成了固定的维度,然后应用直方图均衡化来实现固定的亮度和对比度。

// Either convert the image to greyscale, or use the existing greyscale image.
IplImage *imageGrey;
if (imageSrc->nChannels == 3) {
	imageGrey = cvCreateImage( cvGetSize(imageSrc), IPL_DEPTH_8U, 1 );
	// Convert from RGB (actually it is BGR) to Greyscale.
	cvCvtColor( imageSrc, imageGrey, CV_BGR2GRAY );
}
else {
	// Just use the input image, since it is already Greyscale.
	imageGrey = imageSrc;
}

// Resize the image to be a consistent size, even if the aspect ratio changes.
IplImage *imageProcessed;
imageProcessed = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
// Make the image a fixed size.
// CV_INTER_CUBIC or CV_INTER_LINEAR is good for enlarging, and
// CV_INTER_AREA is good for shrinking / decimation, but bad at enlarging.
cvResize(imageGrey, imageProcessed, CV_INTER_LINEAR);

// Give the image a standard brightness and contrast.
cvEqualizeHist(imageProcessed, imageProcessed);

.....  Use 'imageProcessed' for Face Recognition ....

if (imageGrey)
	cvReleaseImage(&imageGrey);
if (imageProcessed)
	cvReleaseImage(&imageProcessed);


把“特征脸”用于人脸识别

现在你已经有了一张经过预处理后的脸部图片,你可以使用特征脸(PCA)进行人脸识别。OpenCV自带了执行PCA操作的”cvEigenDecomposite()”函数,然而你需要一个图片数据库(训练集)告诉机器怎样识别当中的人。

所以你应该收集每个人的一组预处理后的脸部图片用于识别。比如,假如你想要从10人的班级当中识别某个人,你可以为每个人存储20张图片,总共就有200张大小相同(如100×100像素)的经预处理的脸部图片。

特征脸的理论在Servo Magazine的两篇文章(Face Recognition with Eigenface)中解释了,但我仍会在这里尝试着向你解释。

我们使用“主元分析”把你的200张训练图片转换成一个代表这些训练图片主要区别的“特征脸”集。首先它将会通过获取每个像素的平均值,生成这些图片的“平均人脸图片”。然后特征脸将会与“平均人脸”比较。第一个特征脸是最主要的脸部区别,第二个特征脸是第二重要的脸部区别,等等……直到你有了大约50张代表大多数训练集图片的区别的特征脸。

 

 

 

 

 

在上面这些示例图片中你可以看到平均人脸和第一个以及最后一个特征脸。它们是从一个四人的每人30幅图片的训练集中生成的。注意到,平均人脸显示的是一个普通人的平滑脸部结构,排在最前的一些特征脸显示了一些主要的脸部特征,而最后的特征脸(比如Eigenface 119)主要是图像噪声。你可以在下面看到前32张特征脸。

使用主元分析法进行人脸识别

简单地说,特征脸方法(Principal Component Analysis)计算出了训练集中图片的主要区别,并且用这些“区别”的组合来代表每幅训练图片。

比如,一张训练图片可能是如下的组成:

(averageFace) + (13.5% of eigenface0) – (34.3% of eigenface1) + (4.7% of eigenface2) + … + (0.0% of eigenface199).

一旦计算出来,就可以认为这张训练图片是这200个比率(ratio):

{13.5-34.34.7, …, 0.0}.

用特征脸图片分别乘以这些比率,并加上平均人脸图片 (average face),从这200个比率还原这张训练图片是完全可以做到的。但是既然很多排在后面的特征脸是图像噪声或者不会对图片有太大作用,这个比率表可以被降低到只剩下最主要的,比如前30个,不会对图像质量有很大影响。所以现在可以用30个特征脸,平均人脸图片,和一个含有30个比率的表,来代表全部的200张训练图片。

有趣的是,这意味着,我们已经找到了一种方法把200张图片压缩成31张图片再加上一点点数据,而不丢失很多的图像质量。但是这个教程是关于人脸识别的,而不是图像压缩的,所以我们将会忽略它:-)

在另一幅图片中识别一个人,可以应用相同的PCA计算,使用相同的200个特征脸来寻找200个代表输入图片的比率。并且仍然可以只保留前30个比率而忽略其余的比率,因为它们是次要的。然后通过搜索这些比率的表,寻找在数据库中已知的20个人,来看谁的前30个比率与输入图片的前30个比率最接近。这就是寻找与输入图片最相似的训练图片的基本方法,总共提供了200张训练图片。

离线命令行训练的实现

为了实现离线训练,也就是通过命令行(command-line)使用文件作为输入输出,我使用了与Servo Magazine里Face Recognition with Eigenface相同的实现,所以你可以先阅读这篇文章,但是我做了一些小的改动。

基本上,从训练图片创建一个人脸识别数据库,就是创建一个列出图片文件和每个文件代表的人的文本文件。

比如,你可以把这些输入一个名为”4_images_of_2_people.txt”的文本文件:

1 Shervin dataShervinShervin1.bmp
1 Shervin dataShervinShervin2.bmp
1 Shervin dataShervinShervin3.bmp
1 Shervin dataShervinShervin4.bmp
2 Chandan dataChandanChandan1.bmp
2 Chandan dataChandanChandan2.bmp
2 Chandan dataChandanChandan3.bmp
2 Chandan dataChandanChandan4.bmp

它告诉这个程序,第一个人的名字叫“Shervin”,而Shervin的四张预处理后的脸部图像在”dataShervin”文件夹,第二个人的名字叫”Chandan”,在”dataChandan”中有她的四张图片。这个程序可以使用”loadFaceImgArray()”函数把这些图片加载到一个图片数组中。注意,为了简单起见,它不允许空格或特殊字符出现在人名中,<?>所以你可能想要实现这一功能,或者把人名中的空格用下划线代替(比如 Shervin_Emami)。

为了从这些加载好的图片中创建一个数据库,你可以使用OpenCV的”cvCalcEigenObjects()”和”cvEigenDecomposite()”函数,比如:

// Tell PCA to quit when it has enough eigenfaces.
CvTermCriteria calcLimit = cvTermCriteria( CV_TERMCRIT_ITER, nEigens, 1);

// Compute average image, eigenvectors (eigenfaces) and eigenvalues (ratios).
cvCalcEigenObjects(nTrainFaces, (void*)faceImgArr, (void*)eigenVectArr,
	CV_EIGOBJ_NO_CALLBACK, 0, 0, &calcLimit,
	pAvgTrainImg, eigenValMat->data.fl);

// Normalize the matrix of eigenvalues.
cvNormalize(eigenValMat, eigenValMat, 1, 0, CV_L1, 0);

// Project each training image onto the PCA subspace.
CvMat projectedTrainFaceMat = cvCreateMat( nTrainFaces, nEigens, CV_32FC1 );
int offset = projectedTrainFaceMat->step / sizeof(float);
for(int i=0; i<nTrainFaces; i++) {
	cvEigenDecomposite(faceImgArr[i], nEigens, eigenVectArr, 0, 0,
		pAvgTrainImg, projectedTrainFaceMat->data.fl + i*offset);
}


现在你有了:

  • 平均人脸图片”pAvgTrainImg”,
  • 包含特征脸图片的数组”eigenVectArr[]“(如:假如你使用了nEigens=200 张训练图片,将得到200 个特征脸),
  • 特征值矩阵 (即特征脸的比率,eigenface ratios) 每张图片的”projectedTrainFaceMat” 。
现在这些可以被储存成一个文件,也就是人脸识别的数据库。代码中的”storeTrainingData()”函数将会把这些数据储存到”facedata.xml“文件里,它可以随时被重新载入来识别经训练过的人。代码中也有一个”storeEigenfaceImages()”的函数,生成前面提到的图片,平均人脸图片被保存到”out_averageImage.bmp”,特征脸被保存到”out_eigenfaces.bmp”。

 

离线命令行识别的实现

在离线训练阶段,系统尝试从一个文本文件中的列表读取若干张图像中的人脸,并进行识别。为了实现它,我仍然使用Servo Magazine的Face Recognition with Eigenface的实现,在此基础上扩展。

用于离线训练的相同格式的文本文件也可用于离线识别。这个文本文件列出了用于测试的图像文件和对应于这张图像的正确的人名。随后这个程序就对每一幅图片进行识别,并且检验文本文件中的真实值(图片对应的正确人名)来确认其是否识别正确,并统计它的准确率。

离线识别的实现几乎与离线训练完全相同:

1. 读取原来的用于训练的文本文件(现在用于识别),把若干个图片文件(预处理后的脸部图片)和名字载入一个图片数组。这些在代码中用“loadFaceImgArray()”函数执行。

2. 平均人脸,特征脸和特征值(比率)使用函数“loadTrainingData()” 从人脸识别数据库文件(the face recognition database fil)“facedata.xml”载入。

3. 使用OpenCV的函数“cvEigenDecomposite()”,每张输入的图片都被投影到PCA子空间,来观察哪些特征脸的比率最适合于代表这张图片。

4. 现在有了特征值(特征脸图片的比率)代表这张输入图片,程序需要查找原始的训练图片,找出拥有最相似比率的图片。这些用数学的方法在“findNearestNeighbor()”函数中执行,采用的是“欧几里得距离(Euclidean Distance)”,但是它只是基本地检查输入图片与每张训练图片的相似性,找到最相似的一张:一张在欧几里得空间上与输入图片距离最近的图片。就像在 Servo Magazine的文章上提到的那样,如果使用马氏距离( the Mahalanobis space,需要在代码里定义 USE_MAHALANOBIS_DISTANCE),你可以得到更准确的结果。

5. 在输入图片与最相似图片之间的距离用于确定可信度(confidence),作为是否识别出某人的指导。1.0的可信度意味着完全相同,0.0或者负的可信度意味着非常不相似。但是需要注意,我在代码中用到的可信度公式只是一个非常基本的可信度测量,不是很可靠,但是我觉得多数人会想要看到一个粗略的可信度值。你可能发现它对你的图片给出错误的值,所以你可以禁用它(比如:把可信度设为恒定的1.0)。

一旦指导哪张训练图片和输入图片最相似,并假定可信度值不是太低(应该至少是0.6或更高),那么它就指出了那个人是谁,换句话说,它识别出了那个人!

摄像头实时识别的实现

要让一个摄像头视频流输入取代文件列表是十分简单的。基本上,你只要从摄像头抓取一帧,而不是读取一个文件,并且一直运行下去直到用户退出,而不是等待文件读取到头就行了。OpenCV为此提供了“cvCreateCameraCapture()”函数(或cvCaptureFromCAM())。

从摄像头抓取一帧可以简单地用下面的函数实现:

// Grab the next camera frame. Waits until the next frame is ready, and
// provides direct access to it, so do NOT modify or free the returned image!
// Will automatically initialize the camera on the first frame.
IplImage* getCameraFrame(CvCapture* &camera)
{
	IplImage *frame;
	int w, h;

	// If the camera hasn't been initialized, then open it.
	if (!camera) {
		printf("Acessing the camera ...\n");
		camera = cvCreateCameraCapture( 0 );
		if (!camera) {
			printf("Couldn't access the camera.\n");
			exit(1);
		}
		// Try to set the camera resolution to 320 x 240.
		cvSetCaptureProperty(camera, CV_CAP_PROP_FRAME_WIDTH, 320);
		cvSetCaptureProperty(camera, CV_CAP_PROP_FRAME_HEIGHT, 240);
		// Get the first frame, to make sure the camera is initialized.
		frame = cvQueryFrame( camera );
		if (frame) {
			w = frame->width;
			h = frame->height;
			printf("Got the camera at %dx%d resolution.\n", w, h);
		}
		// Wait a little, so that the camera can auto-adjust its brightness.
		Sleep(1000);	// (in milliseconds)
	}

	// Wait until the next camera frame is ready, then grab it.
	frame = cvQueryFrame( camera );
	if (!frame) {
		printf("Couldn't grab a camera frame.\n");
		exit(1);
	}
	return frame;
}


这个函数可以这样用:

CvCapture* camera = 0;	// The camera device.
while ( cvWaitKey(10) != 27 ) {	// Quit on "Escape" key.
	IplImage *frame = getCameraFrame(camera);
	...
}
// Free the camera.
cvReleaseCapture( &camera );


请注意,假如你是为windows操作系统开发,你可以使用 Theo Watson 的 videoInput Library v0.1995 达到两倍于这些代码的速度。它使用了DirectShow硬件加速,然而OpenCV使用VFW已经15年不变了!
把我已经解释的这些部分放到一起,人脸识别系统运行步骤如下:

1. 从摄像头抓取一帧图片。

2. 转换彩色图片帧为灰度图片帧。

3. 检测灰度图片帧的人脸。

4. 处理图片以显示人脸区域(使用 cvSetImageROI() 和 cvCopyImage())。

5. 预处理脸部图片。

6. 识别图片中的人。

摄像头实时训练的实现

现在你已经有了一个用摄像头实时识别人脸的方法,但是要学习新人脸,你不得不关闭这个程序,把摄像头的图片保存成图片文件,更新图片列表,使用离线命令行训练的方法,然后以实时摄像头识别的模式再次运行这个程序。所以实际上,你完全可以用程序来执行实时的摄像头训练!

这里就是用摄像头视频流把一个新的人加入人脸识别数据库而不关闭程序的一个最简单的方法:

1. 从摄像头收集一些图片(预处理后的脸部图片),也可以同时执行人脸识别。

2. 用“cvSaveImage()”函数保存这些脸部图片作为图片文件存入磁盘。

3. 加入每张脸部图片的文件名到训练图片列表(用于离线命令行训练的文本文件)的底部。

4. 一旦你准备实时训练,你将从所有图片文件形成的数据库重新训练。这个文本文件列出了新加入的训练图片文件,并且这些图片被电脑存为了图片文件,所以实时训练工作起来跟离线训练一样。

5. 但是在重新训练之前,释放任何正在使用的资源和重新初始化也很必要。应该像你重新启动了这个程序一样。比如,在图片被存储成文件并且加入训练列表的文本文件后,你应该再执行相同的离线训练(包括从训练列表文件载入图片,用PCA方法找出新训练集的特征脸和比率)之前释放特征脸数组。 这个实时训练的方法相当低效,因为假如在训练集中有50个人,而你多加了一个人,它将为51个人重新训练,这是非常不好的,因为训练的时间随着用户或图片数量的增加呈指数级增长。但是假如你只是处理百来张图片,它不需要多少秒就可以完成。

文件下载请转到原文

The article source is http://www.shervinemami.co.cc/faceRecognition.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[译]人脸检测与人脸识别简介 的相关文章

  • 如何使用 python、openCV 计算图像中的行数

    我想数纸张 所以我正在考虑使用线条检测 我尝试过一些方法 例如Canny HoughLines and FLD 但我只得到处理过的照片 我不知道如何计算 有一些小线段就是我们想要的线 我用过len lines or len contours
  • setImageCompressionQuality 与 setCompressionQuality 之间有什么区别 - Imagick

    我在Imagick中找到了两种设置图像压缩质量的方法 A 设置图像压缩质量 B 设置压缩质量 所以我想知道哪一个是最好的以及为什么在以下条件下 我读到了setCompressionQuality方法仅适用于新图像 我正在尝试压缩文件 jpe
  • 如何将图像显示为缩略图

    我有一个QTreeView显示硬盘驱动器和目录 我也有一个QListView显示图像文件如下 但我想将图像显示为缩略图 如下所示 My code mainWidget mainWidget QWidget parent QWidget pa
  • 如何去除给定图像中的噪声,使 ocr 输出完美?

    我已经对这个孟加拉文本图像进行了大津阈值处理 并使用 tesseract 进行 OCR 但输出非常糟糕 我应该应用什么预处理来消除噪音 我也想校正图像 因为它有轻微的倾斜 我的代码如下 import tesserocr from PIL i
  • 如何在 Visual Studio 中搜索并让它忽略注释掉的内容?

    我正在 Visual Studio 2005 中重构 C 代码库 我现在已经完成了这个过程的一半 我已经注释掉了很多旧代码并替换或移动了它 现在我正在搜索 看看下一步必须更改 但搜索功能不断为我带来我不再关心的旧注释掉的内容 我还不想删除旧
  • Oracle Blob 在 PHP 页面中作为 img src

    我有一个网站当前使用文件服务器上的图像 这些图像显示在页面上 用户可以根据需要拖放每个图像 这是使用 jQuery 完成的 图像包含在列表中 每张图片都非常标准 img src network path image png height 8
  • 左对齐图像和居中文本在 div 内的同一级别?

    HTML br div class UpperTitle img src align left CableSolve Web Dashboard Version 0 1 1 div br CSS UpperTitle text align
  • 在Android内存中存储gif图像

    我对安卓还很陌生 我想将图像保存到内存中 然后从内存中检索图像并将其加载到图像视图中 我已使用以下代码成功将图像存储在内存中 void saveImage String fileName img cnt jpg File file new
  • 将图像列保存到 SQL Server 2000 中的文件

    我在 SQL Server 2000 中有一个包含图像列的表 我需要将图像数据保存到文件系统上的文件中 在 SQL Server 2005 中 我可以使用 ADODB Stream 对象进行文件 I O 但这在 SQL Server 200
  • 2d 图像点和 3d 网格之间的交点

    Given 网格 源相机 我有内在和外在参数 图像坐标 2d Output 3D 点 是从相机中心发出的光线穿过图像平面上的 2d 点与网格的交点 我试图找到网格上的 3d 点 This is the process From Multip
  • 如何使用 Perl CGI 脚本提供图像?

    我的 Google fu 让我失望了 如何使用 Perl 提供已生成的图像 Example img src getimage pl getimage pl 里有什么 干得好 usr bin perl w my file inner nav
  • 使用 ImageMagick/convert 创建半透明 PNG

    我有 PNG 文件 我想将整个图像转换为半透明 该图像将在 KML 文件中引用为 Google 地球 地图中使用的图标叠加层 使用 ImageMagick 向我建议了以下示例convert命令 但似乎都不起作用 第一个示例会导致错误 usr
  • WinForms - 加载表单时如何使用 PaintEventArgs 运行函数?

    我试图理解图形 在 Graphics FromImage 文档中 它有这样的示例 private void FromImageImage PaintEventArgs e Create image Image imageFile Image
  • 二值图像中骨架上两点之间的最短路径

    我有一个二进制图像 其中包含图像的一个像素宽度骨架 您可能基本上知道 在这个二值图像中 我在骨架上有 1 在其他地方有 0 如何找到骨架上两个非零元素之间的最短距离 路径也应该在骨架本身上 我想使用 A star 算法的 C 实现 我找到了
  • Opencv Mat内存管理

    内存管理对于图像类至关重要 在opencv中 图像类是cv Mat 它有一个微妙的内存管理方案 假设我已经有了自己的图像类SelfImage class SelfImage public int width int height unsig
  • OpenCV Mat 和 Leptonica Pix 之间的转换

    我需要在 C 中在 OpenCV Mat 图像和 Leptonica Pix 图像格式之间进行转换 这用于 8 位灰度图像的二值化 我发现发现了 ikaliga的回答 https stackoverflow com a 25929320 2
  • Java-如何将黑白图像加载到二进制中?

    我在 FSE 模式下使用 Java 和 swing 我想将完全黑白图像加载为二进制格式 最好是二维数组 并将其用于基于掩码的每像素碰撞检测 我什至不知道从哪里开始 过去一个小时我一直在研究 但没有找到任何相关的东西 只需将其读入Buffer
  • 如何垂直对齐div内的图像

    如何在包含的内容中对齐图像div Example 在我的示例中 我需要将 img in the div with class frame div class frame style height 25px img src http jsfi
  • cv2.VideoWriter:请求一个元组作为 Size 参数,然后拒绝它

    我正在使用 OpenCV 4 0 和 Python 3 7 创建延时视频 构造 VideoWriter 对象时 文档表示 Size 参数应该是一个元组 当我给它一个元组时 它拒绝它 当我尝试用其他东西替换它时 它不会接受它 因为它说参数不是
  • 如何在 Qt 应用程序中通过终端命令运行分离的应用程序?

    我想使用命令 cd opencv opencv 3 0 0 alpha samples cpp cpp example facedetect lena jpg 在 Qt 应用程序中按钮的 clicked 方法上运行 OpenCV 示例代码

随机推荐