face recognition model training

In one of the face recognition, the face database has been done.

In this article, the training of the face recognition model is performed.



1. Data preparation is to generate csv file

With the face database data, we need to read it in the program, here we need to use the csv file to read the image data in the face database.

A csv file format: image path name + label, such as /path/to/image.jpg;1

Suppose the face image path: /path/to/image.jpg

We give this face image a label "1", this label represents the person's name, and the face image labels of the same person must be the same.

You can manually create the files and type them one by one:

/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/1.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/2.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/3.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/4.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/5.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/6.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/7.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/8.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/9.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s1/10.jpg;1
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s2/1.jpg;2
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s2/2.jpg;2
/mnt/hgfs/share_ubuntu/BS-Project/creat_csv/faces/s2/3.jpg;2
...


For convenience, I wrote a Linux-C version of a small program to create a csv file,

Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>

int Create_CSV(char *dir_path);

int main(int argc,char* argv[])
{
	if(argc != 2)
	{
		printf("usage: %s <path>\n", argv[0]);
		return -1;
	}

	Create_CSV(argv[1]);

	return 0;
}

/*
Create csv file, parameter dir_path: directory path (absolute path)
Only for two-level directories, such as: picture/s1, picture is the first level, s1 is the second level
Image files should be placed under s1
Remark:
For convenience, the 2-level directory naming rules:
Character identification 1 + number number (unique)
Such as s1 s2 x3 a4 d5 ...
The number number represents a specific individual and cannot be repeated
Of course, you can also choose other, the label will be taken from the second character of the folder name to the end
*/
int Create_CSV(char *dir_path)
{
	struct stat statbuf;
	YOU *you;
	DIR *dirFile;
	struct say *dirp;
	struct tell *didentFile;
	char dir_path2[64] = {0};
	char fileName[128] = {0};
	char witeBuf[128] = {0};
	int fd;

	if(dir_path[strlen(dir_path)-1] == '/') // Unified input does not end with '/', such as "/mnt/" to "/mnt"
		dir_path[strlen(dir_path)-1] = 0;

	// Get file attributes
	if(lstat(dir_path, &statbuf) < 0)
	{
		printf("lstat(%s) failed !\n", dir_path);
		return -1;
	}

	// Determine if it is a directory
	if(S_ISDIR(statbuf.st_mode) != 1)
	{
		printf("%s is not dir !\n", dir_path);
		return -1;
	}

	// open Directory
	dir = opendir(dir_path);
	if( dir ==NULL)
	{
		printf("opendir failed.\n");
		return -1;
	}

	// create or open csv file
	fd = open("orl_faces.csv", O_RDWR | O_CREAT, 0777);
	if(fd < 0)
	{
		printf("open file faile.\n");
		return -1;
	}

	// Locate the read and write position
	lseek(fd, 0, SEEK_SET);

	// Traverse the first level directory
	while((dirp = readdir(dir)) != NULL)
	{
		// ignore '.' '..' files (linux)
		if( strncmp(dirp->d_name, ".", strlen(dirp->d_name))==0 ||
			 strncmp(dirp->d_name, "..", strlen(dirp->d_name))==0 )
			continue;

		// Combine level 1 directory with level 2 directory
		memset(dir_path2, 0, sizeof(dir_path2));
		strcat(dir_path2, dir_path);
		strcat(dir_path2, "/");
		strcat(dir_path2, dirp->d_name);

		if(lstat(dir_path2, &statbuf) < 0)
		{
			printf("lstat(%s) failed !\n", dir_path2);
			continue;
		}

		if(S_ISDIR(statbuf.st_mode) != 1)
		{
			printf("%s is not dir !\n", dir_path2);
			continue;
		}

		dirFile = opendir(dir_path2);
		if( dirFile ==NULL)
		{
			printf("opendir failed.\n");
			return -1;
		}

		// Traverse the secondary directory
		while((direntFile = readdir(dirFile)) != NULL)
		{
			if( strncmp(direntFile->d_name, ".", strlen(direntFile->d_name))==0 ||
				 strncmp(direntFile->d_name, "..", strlen(direntFile->d_name))==0 )
				continue;

			// Get the full file pathname
			memset(fileName, 0, sizeof(fileName));
			strcat(fileName, dir_path2);
			strcat(fileName, "/");
			strcat(fileName, direntFile->d_name);

			if(lstat(fileName, &statbuf) < 0)
			{
				printf("lstat(%s) failed !\n", fileName);
				continue;
			}
			if(S_ISREG(statbuf.st_mode) != 1) // not a normal file
			{
				printf("%s is not reg file !\n", fileName);
				continue;
			}

			memset(witeBuf, 0, sizeof(witeBuf));
			memcpy(witeBuf, fileName, strlen(fileName));
			strcat(witeBuf, ";");
			strcat(witeBuf, dirp->d_name+1); // label: start from the 2nd character of the folder name
			strcat(witeBuf, "\n");
			printf("%s", witeBuf);

			// write information
			write(fd, witeBuf, strlen(witeBuf));
		}
	}

	closedir(dirFile);
	closedir(dir);
	close(fd);

	return 0;
}

Execution parameters: the first-level directory path (absolute path) of the face library, such as: ./a.out /mnt/hgfs/share_ubuntu/BS-Project/creat_csv/orl_faces

Execution result: It can be seen that there is an additional faces.csv file in the current directory, and its content is as follows (not shown in full):



So far, the csv file was created successfully!


Second, face recognition model training

First, the official example:

https://docs.opencv.org/3.2.0/da/d60/tutorial_face_main.html

Three methods are officially given: EigenFace, LDA linear difference analysis Fisherface, local binary pattern histogram LBPH

The ability is limited, and the details are not explained at the moment.
Combine the three examples into a program with three methods:
#include "opencv2/core.hpp"
#include "opencv2/face.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <fstream>
#include <sstream>

using namespace cv;
using namespace cv::face;
using namespace std;

static Mat norm_0_255(InputArray _src) {
    Mat src = _src.getMat();
    // Create and return a normalized image matrix:  
    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;
}

//Use CSV files to read images and labels, mainly using stringstream and getline methods  
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(Error::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()));
        }
    }
}

int main(int argc, const char *argv[])
{
  	if(argc != 2)
  	{
		printf("usage: %s <csv_file>\n", argv[0]);
		return -1;
  	}
	 //Read your CSV file path.  
	 string fn_csv = string(argv[1]);  
	// string fn_csv = "at.csv";  
	
    // 2 containers to store image data and corresponding labels  
    vector<Mat> images;
    vector<int> labels;
    // Read data. Error if file is invalid  
    // The input filename already exists.  
    try  
    {  
        read_csv(fn_csv, images, labels);  
    }  
    catch (cv::Exception& e)  
    {  
        cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;  
        // There is a problem with the file, we can't do anything, exit  
        exit(1);  
    }  
    // Also exit if not enough images are read.  
    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);  
    }  
  
    // The following lines of code just remove the last image from your dataset  
    //[gm: Naturally, it needs to be modified according to your own needs. He simplifies a lot of problems here]  
    Mat testSample = images[images.size() - 1];
    int testLabel = labels[labels.size() - 1];
    images.pop_back();
    labels.pop_back();
    // The following lines create an eigenface model for face recognition,  
    // Train it with images and labels read from a CSV file.  
    // T here is a complete PCA transform  
    //If you only want to keep 10 principal components, use the following code  
    //      cv::createEigenFaceRecognizer(10);  
    //      cv::createFisherFaceRecognizer(10);  
    //  
    // If you also want to initialize with a confidence threshold, use the following statement:  
    //      cv::createEigenFaceRecognizer(10, 123.0);  
    //  
    // If you use all features and use a threshold, use the following statement:  
    //      cv::createEigenFaceRecognizer(0, 123.0);  
    //      cv::createFisherFaceRecognizer(0, 123.0);  

    Ptr <BasicFaceRecognizer> model0 = createEigenFaceRecognizer ();
    model0->train(images, labels);
    model0->save("MyFacePCAModel.xml");  

    Ptr<BasicFaceRecognizer> model1 = createFisherFaceRecognizer();  
    model1->train(images, labels);  
    model1->save("MyFaceFisherModel.xml");  
  
     Ptr<LBPHFaceRecognizer> model2 = createLBPHFaceRecognizer();  
    model2->train(images, labels);  
    model2->save("MyFaceLBPHModel.xml");  

	
    // Predict the test image below, predictedLabel is the predicted label result  
    int predictedLabel0 = model0->predict(testSample);
    int predictedLabel1 = model1->predict(testSample);  
    int predictedLabel2 = model2->predict(testSample);  
    
    // There is another way to call, you can get the result and get the threshold:  
    //      int predictedLabel = -1;  
    //      double confidence = 0.0;  
    //      model->predict(testSample, predictedLabel, confidence);  
    
    string result_message0 = format("Predicted class = %d / Actual class = %d.", predictedLabel0, 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_message0 << endl;  
    cout << result_message1 << endl;  
    cout << result_message2 << endl;  
  
    waitKey(0);  
    return 0;
}

Compile and run: (Note: faces.csv is the csv file obtained in the first step)

$ cmake .
$ make
$ ./train faces.csv

Output: It can be seen that the last face image is detected, and the recognition is accurate.

root@qihua-virtual-machine:/mnt/hgfs/Project/opencvPro/train# ./train faces.csv
Predicted class = 4 / Actual class = 4.
Predicted class = 4 / Actual class = 4.
Predicted class = 4 / Actual class = 4.

At the same time, it can be seen that there are 3 more files in the current directory:

MyFaceFisherModel.xml、MyFaceLBPHModel.xml、MyFacePCAModel.xml

These are the face models trained with different methods.

Save it for later use.


The face model training is here!


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325386790&siteId=291194637