OpenCV + CPP 系列(圩七)人脸识别经典算法

一、简介

主要使用OpenCV自带的经典人脸特征进行识别:

  • Fisherface(LDA)特征
    Eigenface(PCA)特征
    这两个特征预处理时,图像大小必须一致

  • LBPHface(LBPH)特征
    该特征优点是不会受到光照、缩放、旋转和平移的影响。

三个特征使用的创建方法:

  • static Ptr<FisherFaceRecognizer> create (int num_components = 0, double threshold = DBL_MAX)
    static Ptr<EigenFaceRecognizer> create (int num_components = 0, double threshold = DBL_MAX)

  • static Ptr<LBPHFaceRecognizer> create(int radius=1, int neighbors=8, int grid_x=8, int grid_y=8, double threshold = DBL_MAX)

二、测试

头文件 FaceRecognize.h

#pragma once
#include <opencv2/opencv.hpp>
#include <opencv2/face.hpp>
#include <iostream>
#include <fstream>

using namespace cv;
using namespace std;


class FaceRecognize {
public:
	void face_recog_demo1(const char* csv_path);
	void face_recog_demo2(const char* csv_path, const char* model_path);
};

主函数main.cpp

#include "FaceRecognize.h"


int main(int argc, char**argv) {
	const char* csv_path = "D:/opencv-c++/orl_faces/images_info.txt";
	const char* model_path = "D:/opencv-c++/orl_faces/images_info.xml";

	FaceRecognize face_recognize;
	//face_recognize.face_recog_demo1(csv_path);
	face_recognize.face_recog_demo2(csv_path, model_path);

	waitKey(0);
	destroyAllWindows();
	return 0;
}

其中images_info.txt格式如下:
D:/opencv-c++/orl_faces/s1/1.pgm 1(文件路径\t标签)

FisherFace 和 EigenFace 特征
void load_image_sample(const char* csv_path, vector<Mat>& images, vector<int>& labels) {
	ifstream file(csv_path, ifstream::in);
	if (!file) {
		cout << "could not read files." << endl;
		exit(-1);
	}
	string line, imgPath, classLabel;
	while (getline(file, line)) {
		stringstream lines(line);
		getline(lines, imgPath, '\t');
		getline(lines, classLabel);
		if (!imgPath.empty() && !classLabel.empty()) {
			images.push_back(imread(imgPath, 0));
			labels.push_back(atoi(classLabel.c_str()));
		}
	}
}

void FaceRecognize::face_recog_demo1(const char* csv_path) {
	vector<Mat> images;
	vector<int> labels;
	images.reserve(400);          //准备了400个样本
	labels.reserve(400);
	load_image_sample(csv_path, images, labels);
	if (images.size() < 1 || labels.size() < 1) {
		printf("invalid image path...\n");
		exit(-1);
	}

	int height = images[0].rows;
	int width = images[0].cols;
	printf("查看第一个样本信息:height : %d, width : %d\n", height, width);

	// 弹出最后最后一个样本作为测试数据
	Mat testImage = images[images.size() - 1];
	int testLable = labels[labels.size() - 1];
	images.pop_back();
	labels.pop_back();

	//训练
	//Ptr<cv::face::BasicFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
	Ptr<cv::face::BasicFaceRecognizer> model = cv::face::EigenFaceRecognizer::create();
	model->train(images, labels);

	//预测
	int predictLabel = model->predict(testImage);
	printf("actual label : %d, predict label :  %d\n", testLable, predictLabel);

	// 可视化均值脸
	Mat eigenValues  = model->getEigenValues();
	Mat eigenVectors = model->getEigenVectors();
	Mat meanValue = model->getMean();
	cout << "meanValue = " << meanValue.size() << endl;
	Mat meanFace = meanValue.reshape(1, height);
	Mat dst;
	if (meanFace.channels() == 1) {
		normalize(meanFace, dst, 0, 255, NORM_MINMAX, CV_8UC1);
	}
	else if (meanFace.channels() == 3) {
		normalize(meanFace, dst, 0, 255, NORM_MINMAX, CV_8UC3);
	}
	imshow("Mean Face", dst);

	// 可视化特征脸
	for (int i = 0; i < min(16, eigenVectors.cols); i++) {
		Mat ev = eigenVectors.col(i).clone();
		Mat grayscale;
		Mat eigenFace = ev.reshape(1, height);
		if (eigenFace.channels() == 1) {
			normalize(eigenFace, grayscale, 0, 255, NORM_MINMAX, CV_8UC1);
		}
		else if (eigenFace.channels() == 3) {
			normalize(eigenFace, grayscale, 0, 255, NORM_MINMAX, CV_8UC3);
		}
		Mat colorface;
		applyColorMap(grayscale, colorface, COLORMAP_BONE);
		string winTitle = "eigenface_" + to_string(i);
		imshow(winTitle, colorface);
	}

	// PCA特征重建查看:https://wangsp.blog.csdn.net/article/details/120361267
	// 重建人脸
	for (int num = 0; num < min(eigenVectors.cols, 16); num++) {
		Mat evs = eigenVectors.col(num);
		Mat projection = LDA::subspaceProject(evs, meanValue, images[0].reshape(1, 1));
		Mat reconstruction = LDA::subspaceReconstruct(evs, meanValue, projection);
		Mat result = reconstruction.reshape(1, height);
		if (result.channels() == 1) {
			normalize(result, reconstruction, 0, 255, NORM_MINMAX, CV_8UC1);
		}
		else if (result.channels() == 3) {
			normalize(result, reconstruction, 0, 255, NORM_MINMAX, CV_8UC3);
		}
		string winTitle = "recon_face_" + to_string(num);
		imshow(winTitle, reconstruction);
	}
}
LBPHFace 特征
void lbphface_train(const char* model_path, vector<Mat>& mats, vector<int>& labels) {
	if (mats.empty() || labels.empty() || mats.size() != labels.size()) {
		printf("传入参数错误,请检查!");
		exit(-1);
	}
	// 创建模型 -> 训练模型:(矩阵向量,标签向量) -> 保存模型:(文件名)
	Ptr<cv::face::LBPHFaceRecognizer> model = cv::face::LBPHFaceRecognizer::create();
	model->train(mats, labels);
	model->save(model_path);
	printf("训练模型完成!");
}


void lbphface_update(const char* model_path, vector<Mat>& mats, vector<int>& labels) {
	if (mats.empty() || labels.empty() || mats.size() != labels.size()) {
		printf("传入参数错误,请检查!");
		exit(-1);
	}
	// 加载模型 -> 更新训练模型 -> 保存模型
	Ptr<cv::face::LBPHFaceRecognizer> model = Algorithm::load<cv::face::LBPHFaceRecognizer>(model_path);
	model->update(mats, labels);
	model->save(model_path);
	printf("更新模型完成!");
}



void lbphface_predict(const char* model_path, vector<Mat>& testImages, vector<int>& pred_label) {
	Ptr<cv::face::LBPHFaceRecognizer> model = Algorithm::load<cv::face::LBPHFaceRecognizer>(model_path);
	if (model->empty()) {
		cout << "face model load faied!" << endl;
		exit(-1);
	}
	if (testImages.empty()) {
		printf("传入预测矩阵错误!");
		exit(-1);
	}

	int label = -1;                 //标签变量
	double confidence = 0;          //置信值
	for (int i = 0; i < testImages.size(); i++) {
		//预测分类结果,参数:(图矩阵,标签变量,置信概率变量)
		model->predict(testImages[i], label, confidence);
		cout << "confi_value = " << confidence << endl;
		cout << "predi_label = " << label << endl;
		if (confidence > 80) {
			label = -1;          //标签给-1(表示未知)
		}
		pred_label.push_back(label);
	}
	printf("人脸预测完成!");
	
	int radius = model->getRadius();
	int neibs = model->getNeighbors();
	int grad_x = model->getGridX();
	int grad_y = model->getGridY();
	double t = model->getThreshold();

	printf("radius : %d\n", radius);
	printf("neibs  : %d\n", neibs);
	printf("grad_x : %d\n", grad_x);
	printf("grad_y : %d\n", grad_y);
	printf("threshold : %.2f\n", t);
}


void FaceRecognize::face_recog_demo2(const char* csv_path, const char* model_path) {
	vector<Mat> images;
	vector<int> labels;
	images.reserve(401);
	labels.reserve(401);
	load_image_sample(csv_path, images, labels);
	if (images.size() < 1 || labels.size() < 1) {
		printf("invalid image path...\n");
		exit(-1);
	}

	int height = images[0].rows;
	int width = images[0].cols;
	printf("查看第一个样本信息:height : %d, width : %d\n", height, width);

	// 弹出最后最后一个样本作为测试数据
	Mat testImage = images[images.size() - 1];
	int testLable = labels[labels.size() - 1];
	images.pop_back();
	labels.pop_back();

	lbphface_train(model_path, images, labels);
	vector<int> pred_label;
	vector<Mat> testImages;
	testImages.push_back(testImage);
	lbphface_predict(model_path, testImages, pred_label);
	cout << "pred_label[0] = " << pred_label[0] << endl;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SongpingWang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值