opencv读取摄像头训练识别自己的头像

前提

感谢网络上相头的博客和资料,由于版本的原因有些api的更改使得源代码不能直接使用,所以这里放上自己基于opencv3.4做的测试。

流程

  1. 打开摄像头
  2. 保存自己的头像
  3. 整理自己头像模型训练
  4. 识别

浅出

打开摄像头识别自己的头像并保存

  • 新建一个win32工程然后添加相关库的引用可以参考opencv编译后面的内容
  • 打开摄像头的代码如下。原理是打开摄像头识别人脸子如果有保存下来
int main()
{
	VideoCapture cap(0);    //打开默认摄像头
	if(!cap.isOpened())
	{
	    return -1;
	}
	Mat frame;
	Mat edges;
	CascadeClassifier cascade, nestedCascade;
	bool stop = false;
	//训练好的文件名称,放置在可执行文件同目录下
	cascade.load("haarcascade_frontalface_default.xml");
	nestedCascade.load("haarcascade_eye.xml");
	frame = imread("2.jpg");
	detectAndDraw(frame, cascade, nestedCascade, 2, 0);
	while(!stop)
	{
	    cap>>frame;
	    detectAndDraw( frame, cascade, nestedCascade,2,0 );
	    if(waitKey(30) >=0)
	        stop = true;
	}
	waitKey();
	return 0;
}
void detectAndDraw(Mat& img, CascadeClassifier& cascade,
	CascadeClassifier& nestedCascade,
	double scale, bool tryflip)
{
	

	std::vector<Rect> faces;
	Mat frame_gray;
	cvtColor(img, frame_gray, COLOR_BGR2GRAY);
	//检测人脸
	//detectMultiScale函数中smallImg表示的是要检测的输入图像为smallImg,faces表示检测到的人脸目标序列,1.1表示
	//每次图像尺寸减小的比例为1.1,2表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大
	//小都可以检测到人脸),CV_HAAR_SCALE_IMAGE表示不是缩放分类器来检测,而是缩放图像,Size(30, 30)为目标的
	//最小最大尺寸
	cascade.detectMultiScale(frame_gray, faces,
		1.1, 2, 0
		//|CV_HAAR_FIND_BIGGEST_OBJECT
		//|CV_HAAR_DO_ROUGH_SEARCH
		| CV_HAAR_SCALE_IMAGE
		, Size(30, 30));

	for (size_t i = 0; i < faces.size(); i++)
	{
		rectangle(img, faces[i], Scalar(255, 0, 0), 2, 8, 0);
	}

	if (faces.size() == 1)
	{
		Mat faceROI = img(faces[0]);
		Mat myFace;
		resize(faceROI, myFace, Size(92, 112));
		putText(img, to_string(1), faces[0].tl(), 3, 1.2, (0, 0, 255), 2, LINE_AA);

		string filename = format("C:\\Users\\lyy\\Desktop\\att_faces\\s41\\%d.jpg", pic_num);
		imwrite(filename, myFace);
		imshow(filename, myFace);
		waitKey(500);
		destroyWindow(filename);
		pic_num++;
		
	}
	
	imshow("识别结果", img);
}
  • 整理数据
  1. 下载opencv教程给出的数据集AT&T这里面有40个人的脸我们在里面添加一个s41文件夹并把上一步保存的照片放进去测了了jpg的也可以,pgm的更好
  2. 然后整理出图片信息文件 可以用命令
dir /b /s *.pgm *.jpg >at.txt
  1. 然后生成如下格式的文件,生成的办法可以自己手动更改上面的文件,也可以编程来实现语言不限了,官方给出了python脚本文件所以我们这里用python,其实我更熟悉c#,安装python 然后执行python命令 ,格式为python 脚本文件路径
#!/usr/bin/env python  
  
import sys  
import os.path  
 
if __name__ == "__main__":  
  
    #if len(sys.argv) != 2:  
    #    print "usage: create_csv <base_path>"  
    #    sys.exit(1)  
  
    #BASE_PATH=sys.argv[1]  
    BASE_PATH="C:\\Users\\lyy\\Desktop\\att_faces"        
    SEPARATOR=";"    
    fh = open("at.txt",'w')  
  
    for dirname, dirnames, filenames in os.walk(BASE_PATH):  
        for subdirname in dirnames:  
            subject_path = os.path.join(dirname, subdirname)  
            for filename in os.listdir(subject_path):
                subdirname2 = subdirname[::-1]
                subdirname3 = subdirname2[:-1]
                subdirname4 = subdirname3[::-1]
                
                #print subdirname2[:-1]
                label = int(subdirname4)           
                abs_path = "%s/%s" % (subject_path, filename)  
                print "%s%s%d" % (abs_path, SEPARATOR, label)  
                fh.write(abs_path)  
                fh.write(SEPARATOR)  
                fh.write(str(label))  
                fh.write("\n")        
            label = label + 1  
    fh.close()  

这里写图片描述
4. 训练自己

 ```   c++
 int main()
{

	//读取你的CSV文件路径.  
	//string fn_csv = string(argv[1]);  
	string fn_csv = "at.txt";

	// 2个容器来存放图像数据和对应的标签  
	vector<Mat> images;
	vector<int> labels;
	// 读取数据. 如果文件不合法就会出错  
	// 输入的文件名已经有了.  
	try
	{
		read_csv(fn_csv, images, labels);
	}
	catch (cv::Exception& e)
	{
		cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
		// 文件有问题,我们啥也做不了了,退出了  
		exit(1);
	}
	// 如果没有读取到足够图片,也退出.  
	if (images.size() <= 1) {
		string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
		CV_Error(CV_StsError, error_message);
	}

	// 下面的几行代码仅仅是从你的数据集中移除最后一张图片  
	//[gm:自然这里需要根据自己的需要修改,他这里简化了很多问题]  
	Mat testSample = images[images.size() - 1];
	int testLabel = labels[labels.size() - 1];
	images.pop_back();
	labels.pop_back();
	// 下面几行创建了一个特征脸模型用于人脸识别,  
	// 通过CSV文件读取的图像和标签训练它。  
	// T这里是一个完整的PCA变换  
	//如果你只想保留10个主成分,使用如下代码  
	//      cv::createEigenFaceRecognizer(10);  
	//  
	// 如果你还希望使用置信度阈值来初始化,使用以下语句:  
	//      cv::createEigenFaceRecognizer(10, 123.0);  
	//  
	// 如果你使用所有特征并且使用一个阈值,使用以下语句:  
	//      cv::createEigenFaceRecognizer(0, 123.0);  

	Ptr<BasicFaceRecognizer> model = EigenFaceRecognizer::create();
	model->train(images, labels);
	model->save("MyFacePCAModel.xml");

	Ptr<BasicFaceRecognizer> model1 = FisherFaceRecognizer::create();
	model1->train(images, labels);
	model1->save("MyFaceFisherModel.xml");

	Ptr<LBPHFaceRecognizer> model2 = LBPHFaceRecognizer::create();
	model2->train(images, labels);
	model2->save("MyFaceLBPHModel.xml");

	// 下面对测试图像进行预测,predictedLabel是预测标签结果  
	int predictedLabel = model->predict(testSample);
	int predictedLabel1 = model1->predict(testSample);
	int predictedLabel2 = model2->predict(testSample);

	// 还有一种调用方式,可以获取结果同时得到阈值:  
	//      int predictedLabel = -1;  
	//      double confidence = 0.0;  
	//      model->predict(testSample, predictedLabel, confidence);  

	string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
	string result_message1 = format("Predicted class = %d / Actual class = %d.", predictedLabel1, testLabel);
	string result_message2 = format("Predicted class = %d / Actual class = %d.", predictedLabel2, testLabel);
	cout << result_message << endl;
	cout << result_message1 << endl;
	cout << result_message2 << endl;

	getchar();
	//waitKey(0);
	return 0;
}

static Mat norm_0_255(InputArray _src) {
	Mat src = _src.getMat();
	// 创建和返回一个归一化后的图像矩阵:  
	Mat dst;
	switch (src.channels()) {
	case 1:
		cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
		break;
	case 3:
		cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
		break;
	default:
		src.copyTo(dst);
		break;
	}
	return dst;
}

//使用CSV文件去读图像和标签,主要使用stringstream和getline方法  
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
	std::ifstream file(filename.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, path, classlabel;
	while (getline(file, line)) {
		stringstream liness(line);
		getline(liness, path, separator);
		getline(liness, classlabel);
		if (!path.empty() && !classlabel.empty()) {
			images.push_back(imread(path, 0));
			labels.push_back(atoi(classlabel.c_str()));
		}
	}

tips :createEigenFaceRecognizer更改为了EigenFaceRecognizer::create()
** FisherFaceRecognizer::create()与LBPHFaceRecognizer::create()同样具体细节可以参看源码**

  • 识别自己
  1. 加载自己的xml文件然后执行与第一步差不多的代码
int main3()
{
	VideoCapture cap(0);    //打开默认摄像头  
	if (!cap.isOpened())
	{
		return -1;
	}
	Mat frame;
	Mat edges;
	Mat gray;
	CascadeClassifier cascade;
	bool stop = false;
	//训练好的文件名称,放置在可执行文件同目录下  
	cascade.load("haarcascade_frontalface_alt.xml");
	
	Ptr<FaceRecognizer> modelPCA = EigenFaceRecognizer::create();
	modelPCA->read("MyFacePCAModel.xml");
	//3.4取消了load所以这里用的read
	frame = imread("3.jpg");
	detectMyFace(frame, cascade, modelPCA);
	while (!stop)
	{
		cap >> frame;
		detectMyFace(frame, cascade, modelPCA);

		if (waitKey(30) >= 0)
			stop = true;
	}
	
	waitKey(0);
	return 0;
}
void detectMyFace(Mat& img, CascadeClassifier& cascade, Ptr<FaceRecognizer> modelPCA)
{
	
	std::vector<Rect> faces;
	Mat frame_gray;
	cvtColor(img, frame_gray, COLOR_BGR2GRAY);
	//改变图像大小,使用双线性差值  
	//resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);  
	//变换后的图像进行直方图均值化处理  
	//equalizeHist(gray, gray);

	cascade.detectMultiScale(frame_gray, faces,
		1.1, 2, 0
		//|CV_HAAR_FIND_BIGGEST_OBJECT  
		//|CV_HAAR_DO_ROUGH_SEARCH  
		| CV_HAAR_SCALE_IMAGE,
		Size(30, 30));


	Mat face;
	Point text_lb;

	for (size_t i = 0; i < faces.size(); i++)
	{
		if (faces[i].height > 0 && faces[i].width > 0)
		{
			face = frame_gray(faces[i]);
			text_lb = Point(faces[i].x, faces[i].y);

			rectangle(img, faces[i], Scalar(255, 0, 0), 1, 8, 0);
		}
	}

	Mat face_test;

	int predictPCA = 0;
	if (face.rows >= 120)
	{
		resize(face, face_test, Size(92, 112));

	}
	//Mat face_test_gray;  
	//cvtColor(face_test, face_test_gray, CV_BGR2GRAY);  

	if (!face_test.empty())
	{
		//测试图像应该是灰度图  
		predictPCA = modelPCA->predict(face_test);
	}

	cout << predictPCA << endl;
	if (predictPCA == 41)
	{
		string name = "liyangyang";
		putText(img, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255));
	}
	else
	{
		string name = "noliyangyang";
		putText(img, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255));
	}

	imshow("face", img);
}

3.4取消了load所以这里用的read
最后附上结果图
这里写图片描述

源码都附上去了,其中很多都是参考网络上的文档,只有具体的细节和版本不同的函数做了更改。谢谢伟大的互联网。

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值