未写完
采用C++,opencv+opencv contrib 4.1.0
对手机相册内人物opencv人脸识别,身份识别学习
最近事情多,介绍就先不介绍了
photocut.c
#include "pch.h"
#include <opencv2\opencv.hpp>
#include <vector>
#include "CameraFace.h"
#include "PhotoFace.h"
#include <iostream>
//#include<ctime>
#include<stdio.h>
#include "BrowseDir.h"
#include<string>
using namespace std;
using namespace cv;
int read_img(const string& dir, vector<Mat> &images)
{
CStatDir statdir;
if (!statdir.SetInitDir(dir.c_str()))
{
cout << "Direct " << dir << " not exist!" << endl;
return 0;
}
int cls_id = dir[dir.length() - 1] - '0';
vector<char*>file_vec = statdir.BeginBrowseFilenames("*.*");
int i, s = file_vec.size();
for (i = 0; i < s; i++)
{
Mat graymat = imread(file_vec[i], 1);
//graymat.reshape(1,1);//flatten to one row
images.push_back(graymat);
}
return s;
}
vector<pair<char*, Mat>> read_img(const string& dir)
{
CStatDir statdir;
pair<char*, Mat> pfi;
vector<pair<char*, Mat>> Vp;
if (!statdir.SetInitDir(dir.c_str()))
{
cout << "Direct " << dir << " not exist!" << endl;
return Vp;
}
int cls_id = dir[dir.length() - 1] - '0';
vector<char*>file_vec = statdir.BeginBrowseFilenames("*.*");
int i, s = file_vec.size();
for (i = 0; i < s; i++)
{
pfi.first = file_vec[i];
pfi.second = imread(file_vec[i]);
Vp.push_back(pfi);
}
return Vp;
}
void saveImage(cv::Mat image, const String& outPutPath, int index)
{
//定义保存图像的名字
string strSaveName;
char buffer[256];
sprintf(buffer, "D%04d", index);
strSaveName = buffer;
//定义保存图像的完整路径
string strImgSavePath = outPutPath + strSaveName;
//定义保存图像的格式
strImgSavePath += ".jpeg";
//strImgSavePath += ".png";
//保存操作
imwrite(strImgSavePath.c_str(), image);
}
Mat rename(const String& srcfile, const String& srctype, const String& dstfile, const String& dsttype)
{
//string str = to_string(i);
Mat img_1 = imread(srcfile + srctype, IMREAD_COLOR);
imwrite(dstfile + dsttype, img_1);
return img_1;
}
int PhotoFaceCut(const String& file, const String& srcpath, const String& dstpath)
{
CascadeClassifier cascada;
//cascada.load("haarcascade_frontalface_alt2.xml");
cascada.load(".\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml");
Mat frame, myFace,frame_nochange;
static int pic_Allnum = 1;
int pic_num = 0;
//读图像
//char place[_MAX_PATH] = { 0 };
frame = imread(file, IMREAD_COLOR);
frame_nochange = frame.clone();
/*namedWindow("raw", 0);
imshow("raw", frame);*/
cv::waitKey(500);//等待500us
vector<Rect> faces;//vector容器存检测到的faces
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);//转灰度化,减少运算
clock_t startTime, endTime;
startTime = clock();//计时开始
cascada.detectMultiScale(frame_gray, faces, 1.1, 4, CASCADE_DO_ROUGH_SEARCH, Size(30, 30), Size(1000, 1000));
endTime = clock();//计时结束
//printf("检测到人脸个数:%d\n", faces.size());
printf("检测到人脸个数:%d 。时间%f\n", faces.size(), (double)(endTime - startTime) / CLOCKS_PER_SEC);
//1.frame_gray表示的是要检测的输入图像 2.faces表示检测到的人脸目标序列,3. 1.1表示每次图像尺寸减小的比例
//4. 4表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸
/*5.flags–要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,
函数将会使用Canny边缘检测来排除边缘过多或过少的区域,
因为这些区域通常不会是人脸所在区域;opencv3 以后都不用这个参数了*/
//6. Size(100, 100)为目标的最小尺寸 一般为30*30 是最小的了 也够了
//7. Size(500, 500)为目标的最大尺寸 其实可以不用这个,opencv会自动去找这个最大尺寸
//适当调整5,6,7两个参数可以用来排除检测结果中的干扰项。
//识别到的脸用矩形圈出
for (int m = 0; m < faces.size(); m++)
{
FaceSize(frame, faces[m], 0.2);
rectangle(frame, faces[m], Scalar(255, 0, 0), 2, 8, 0);
putText(frame, to_string(pic_num), faces[m].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
pic_num++;
}
/*namedWindow("detected", 0);
imshow("detected", frame);*/
namedWindow("detected", 0);
imshow("detected", frame);
for (int i = 0; i < faces.size(); i++)
{
//适度放大范围
Mat faceROI = frame_gray(faces[i]);//在图中将圈出的脸所在区域裁剪出
Mat faceROI_1 = frame_nochange(faces[i]);//在灰度图中将圈出的脸所在区域裁剪出
//cout << faces[0].x << endl;//测试下face[0].x
resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
//resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
//putText(frame, to_string(pic_num), faces[n].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
//dstpath
string savefilename1 = format("./test/jyl/photo/gray/%d.jpeg", pic_Allnum); //存放在当前项目文件夹以1-10.jpg 命名,format就是转为字符串
imwrite(savefilename1, myFace);//存在当前目录下
//imshow(savefilename1, myFace);//显示下size后的脸
string savefilename2 = format("./test/jyl/photo/rgb/%d.jpeg", pic_Allnum); //存放在当前项目文件夹以1-10.jpg 命名,format就是转为字符串
imwrite(savefilename2, faceROI_1);//存在当前目录下
//imshow(savefilename2, faceROI_1);//显示下size后的脸
//cv::waitKey(500);//等待500us
//destroyAllWindows();
//destroyWindow(savefilename1);//:销毁指定的窗口
//destroyWindow(savefilename2);//:销毁指定的窗口
pic_Allnum++;
}
return 0;
}
//int PhotoCertainFaceCut(const String& file, const String& srcpath, const String& dstpath)
//{
// CascadeClassifier cascada;
// //cascada.load("haarcascade_frontalface_alt2.xml");
// cascada.load(".\\etc\\haarcascades\\haarcascade_frontalface_alt2.xml");
//
// Mat frame, myFace, frame_nochange;
// static int pic_Allnum = 1;
// int pic_num = 0;
//
// //读图像
// //char place[_MAX_PATH] = { 0 };
//
// frame = imread(file, IMREAD_COLOR);
// frame_nochange = frame.clone();
// /*namedWindow("raw", 0);
// imshow("raw", frame);*/
// cv::waitKey(500);//等待500us
//
// vector<Rect> faces;//vector容器存检测到的faces
//
// Mat frame_gray;
// cvtColor(frame, frame_gray, COLOR_BGR2GRAY);//转灰度化,减少运算
//
// clock_t startTime, endTime;
// startTime = clock();//计时开始
// cascada.detectMultiScale(frame_gray, faces, 1.1, 4, CASCADE_DO_ROUGH_SEARCH, Size(30, 30), Size(1000, 1000));
// endTime = clock();//计时结束
// //printf("检测到人脸个数:%d\n", faces.size());
// printf("检测到人脸个数:%d 。时间%f\n", faces.size(), (double)(endTime - startTime) / CLOCKS_PER_SEC);
// //1.frame_gray表示的是要检测的输入图像 2.faces表示检测到的人脸目标序列,3. 1.1表示每次图像尺寸减小的比例
// //4. 4表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸
// /*5.flags–要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,
// 函数将会使用Canny边缘检测来排除边缘过多或过少的区域,
// 因为这些区域通常不会是人脸所在区域;opencv3 以后都不用这个参数了*/
// //6. Size(100, 100)为目标的最小尺寸 一般为30*30 是最小的了 也够了
// //7. Size(500, 500)为目标的最大尺寸 其实可以不用这个,opencv会自动去找这个最大尺寸
// //适当调整5,6,7两个参数可以用来排除检测结果中的干扰项。
//
//
// //识别到的脸用矩形圈出
//
// for (int m = 0; m < faces.size(); m++)
// {
// FaceSize(frame, faces[m], 0.2);
// rectangle(frame, faces[m], Scalar(255, 0, 0), 2, 8, 0);
// putText(frame, to_string(pic_num), faces[m].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
// pic_num++;
// }
// /*namedWindow("detected", 0);
// imshow("detected", frame);*/
// namedWindow("detected", 0);
// imshow("detected", frame);
//
// for (int i = 0; i < faces.size(); i++)
// {
// //适度放大范围
//
// Mat faceROI = frame_gray(faces[i]);//在图中将圈出的脸所在区域裁剪出
// Mat faceROI_1 = frame_nochange(faces[i]);//在灰度图中将圈出的脸所在区域裁剪出
// //cout << faces[0].x << endl;//测试下face[0].x
//
// resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
// //resize(faceROI, myFace, Size(92, 112));//将兴趣域size为92*112
//
// //putText(frame, to_string(pic_num), faces[n].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面写序号
//
// //dstpath
// string savefilename1 = format("./test/abc/photo/gray/%d.jpeg", pic_Allnum); //存放在当前项目文件夹以1-10.jpg 命名,format就是转为字符串
// imwrite(savefilename1, myFace);//存在当前目录下
// //imshow(savefilename1, myFace);//显示下size后的脸
//
// string savefilename2 = format("./test/abc/photo/rgb/%d.jpeg", pic_Allnum); //存放在当前项目文件夹以1-10.jpg 命名,format就是转为字符串
// imwrite(savefilename2, faceROI_1);//存在当前目录下
// //imshow(savefilename2, faceROI_1);//显示下size后的脸
//
// //cv::waitKey(500);//等待500us
// //destroyAllWindows();
// //destroyWindow(savefilename1);//:销毁指定的窗口
// //destroyWindow(savefilename2);//:销毁指定的窗口
// pic_Allnum++;
//
// }
// return 0;
//}
int FaceSize(Mat& img, Rect& faces, float scalar)
{
if ((faces.x - (faces.width)*scalar) > 0 && (faces.y - (faces.height)*scalar)>0 && ((faces.x + faces.width) + faces.width*scalar) < img.cols && ((faces.y + faces.height) + faces.height*scalar) < img.cols)
{
faces = faces + Point(-int(faces.width*scalar), -int(faces.height*scalar)); //平移,也就是左上顶点的x坐标-100,y坐标+100
faces = faces + Size(int(faces.width*scalar), int(faces.height*scalar)); //缩放,左上顶点不变,宽度-100,高度+100
}
else if(((faces.x - (faces.width)*(scalar/2)) > 0 && (faces.y - (faces.height)*(scalar / 2)) > 0 && ((faces.x + faces.width) + faces.width*(scalar / 2)) < img.cols && ((faces.y + faces.height) + faces.height*(scalar / 2)) < img.cols))
{
faces = faces + Point(-int(faces.width*(scalar / 2)), -int(faces.height*(scalar / 2))); //平移,也就是左上顶点的x坐标-100,y坐标+100
faces = faces + Size(int(faces.width*(scalar / 2)), int(faces.height*(scalar / 2))); //缩放,左上顶点不变,宽度-100,高度+100
}
else if (((faces.x - (faces.width)*(scalar / 4)) > 0 && (faces.y - (faces.height)*(scalar / 4)) > 0 && ((faces.x + faces.width) + faces.width*(scalar / 4)) < img.cols && ((faces.y + faces.height) + faces.height*(scalar / 4)) < img.cols))
{
faces = faces + Point(-int(faces.width*(scalar / 4)), -int(faces.height*(scalar / 4))); //平移,也就是左上顶点的x坐标-100,y坐标+100
faces = faces + Size(int(faces.width*(scalar / 4)), int(faces.height*(scalar / 4))); //缩放,左上顶点不变,宽度-100,高度+100
}
else
{
//faces = faces + Point(-int(5), -int(5)); //平移,也就是左上顶点的x坐标-100,y坐标+100
//faces = faces + Size(int(5), int(5)); //缩放,左上顶点不变,宽度-100,高度+100
return -1;
}
return 0;
}
train.c
#include "pch.h"
#include <iostream>
#include<stdio.h>
#include <opencv2\opencv.hpp>
#include<opencv2\face\facerec.hpp> //opencv3需要
#include<opencv2\core.hpp>
#include<opencv2\face.hpp>
#include<opencv2\highgui.hpp>
#include<opencv2\imgproc.hpp>
#include <math.h>
//使用void read_csv()这个函数必须的三个头文件
#include <iostream>
#include <fstream>
#include <sstream>
//#include "Train.h"
using namespace std;
using namespace cv;
using namespace cv::face;
vector<Mat> images;
vector<int> labels;
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);//c_str()函数可用可不用,无需返回一个标准C类型的字符串
if (!file)
{
string error_message = "No valid input file was given, please check the given filename.";
cout << error_message << endl;
waitKey(0);
return ;
}
string line, path, classlabel;
while (getline(file, line)) //从文本文件中读取一行字符,未指定限定符默认限定符为“/n”
{
stringstream liness(line);//这里采用stringstream主要作用是做字符串的分割
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()));
}
}
}
Mat FuncTrain(Mat src, int label)
{
normalize(src, src, 0, 255, NORM_MINMAX, CV_8UC1);
images.push_back(src);
labels.push_back(label);
return src;
}
int Train(const string& SavePath)
{
//读取你的CSV文件路径.
//string fn_csv = string(argv[1]);
string fn_csv = ".\\face\\etc\\at.csv";
// 2个容器来存放图像数据和对应的标签
vector<Mat> images;
vector<int> labels;
// 读取数据. 如果文件不合法就会出错
// 输入的文件名已经有了.
try
{
read_csv(fn_csv, images, labels); //从csv文件中批量读取训练数据
}
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!";
cout << error_message << endl;
waitKey(0);
return -1;
//CV_Error(CV_StsError, error_message);
}
for (int i = 0; i < images.size(); i++)
{
if (images[i].size() != Size(92, 112))
{
cout << i << endl;
cout << images[i].size() << endl;
}
}
// 下面的几行代码仅仅是从你的数据集中移除最后一张图片,作为测试图片
//[gm:自然这里需要根据自己的需要修改,他这里简化了很多问题]
Mat testSample = images[images.size() - 1];
int testLabel = labels[labels.size() - 1];
images.pop_back();//删除最后一张照片,此照片作为测试图片
labels.pop_back();//删除最有一张照片的labels
// 下面几行创建了一个特征脸模型用于人脸识别,
// 通过CSV文件读取的图像和标签训练它。
// T这里是一个完整的PCA变换
//如果你只想保留10个主成分,使用如下代码
// cv::createEigenFaceRecognizer(10);
//
// 如果你还希望使用置信度阈值来初始化,使用以下语句:
// cv::createEigenFaceRecognizer(10, 123.0);
//
// 如果你使用所有特征并且使用一个阈值,使用以下语句:
// cv::createEigenFaceRecognizer(0, 123.0);
//创建一个PCA人脸分类器,暂时命名为model吧,创建完成后
//调用其中的成员函数train()来完成分类器的训练
Ptr<cv::face::BasicFaceRecognizer> model = EigenFaceRecognizer::create();
model->train(images, labels);
model->save(SavePath+"MyFacePCAModel.xml");//保存路径可自己设置,但注意用“\\”
cout << "MyFacePCAModel.xml OK" << endl;
Ptr<BasicFaceRecognizer> model1 = FisherFaceRecognizer::create();
model1->train(images, labels);
model1->save(SavePath+"MyFaceFisherModel.xml");
cout << "MyFaceFisherModel.xml OK" << endl;
Ptr<LBPHFaceRecognizer> model2 = LBPHFaceRecognizer::create();
model2->train(images, labels);
model2->save(SavePath+"MyFaceLBPHModel.xml");
cout << "MyFaceLBPHModel.xml OK" << endl;
// 下面对测试图像进行预测,predictedLabel是预测标签结果
//注意predict()入口参数必须为单通道灰度图像,如果图像类型不符,需要先进行转换
//predict()函数返回一个整形变量作为识别标签
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;
}
int PhotoResize(const string& FileSrcPath, const string& FileDstPath,int width,int height,int gray=1)
{
if (gray == 1)
{
Mat img_gray = imread(FileSrcPath, 0);
resize(img_gray, img_gray, Size(width, height));//92 112
imwrite(FileDstPath, img_gray);
}
else if(gray == 0)
{
Mat img = imread(FileSrcPath, 1);
resize(img, img, Size(width, height));//92 112
imwrite(FileDstPath, img);
}
return 0;
}