-
Notifications
You must be signed in to change notification settings - Fork 1
/
recognition.cpp
executable file
·177 lines (143 loc) · 8.51 KB
/
recognition.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*****************************************************************************
* Face Recognition using Eigenfaces or Fisherfaces
******************************************************************************
* by Shervin Emami, 5th Dec 2012
* http://www.shervinemami.info/openCV.html
******************************************************************************
* Ch8 of the book "Mastering OpenCV with Practical Computer Vision Projects"
* Copyright Packt Publishing 2012.
* http://www.packtpub.com/cool-projects-with-opencv/book
*****************************************************************************/
//////////////////////////////////////////////////////////////////////////////////////
// recognition.cpp, by Shervin Emami (www.shervinemami.info) on 30th May 2012.
// Train the face recognition system on a given dataset, and recognize the person from a given image.
//////////////////////////////////////////////////////////////////////////////////////
// Requires OpenCV v2.4.1 or later (from June 2012), otherwise the FaceRecognizer will not compile or run!
//////////////////////////////////////////////////////////////////////////////////////
#include "recognition.h" // Train the face recognition system and recognize a person from an image.
#include "ImageUtils.h" // Shervin's handy OpenCV utility functions.
// Start training from the collected faces.
// The face recognition algorithm can be one of these and perhaps more, depending on your version of OpenCV, which must be atleast v2.4.1:
// "FaceRecognizer.Eigenfaces": Eigenfaces, also referred to as PCA (Turk and Pentland, 1991).
// "FaceRecognizer.Fisherfaces": Fisherfaces, also referred to as LDA (Belhumeur et al, 1997).
// "FaceRecognizer.LBPH": Local Binary Pattern Histograms (Ahonen et al, 2006).
Ptr<FaceRecognizer> learnCollectedFaces(const vector<Mat> preprocessedFaces, const vector<int> faceLabels, const string facerecAlgorithm)
{
Ptr<FaceRecognizer> model;
cout << "Learning the collected faces using the [" << facerecAlgorithm << "] algorithm ..." << endl;
// Make sure the "contrib" module is dynamically loaded at runtime.
// Requires OpenCV v2.4.1 or later (from June 2012), otherwise the FaceRecognizer will not compile or run!
bool haveContribModule = initModule_contrib();
if (!haveContribModule) {
cerr << "ERROR: The 'contrib' module is needed for FaceRecognizer but has not been loaded into OpenCV!" << endl;
exit(1);
}
// Use the new FaceRecognizer class in OpenCV's "contrib" module:
// Requires OpenCV v2.4.1 or later (from June 2012), otherwise the FaceRecognizer will not compile or run!
model = Algorithm::create<FaceRecognizer>(facerecAlgorithm);
if (model.empty()) {
cerr << "ERROR: The FaceRecognizer algorithm [" << facerecAlgorithm << "] is not available in your version of OpenCV. Please update to OpenCV v2.4.1 or newer." << endl;
exit(1);
}
// Do the actual training from the collected faces. Might take several seconds or minutes depending on input!
model->train(preprocessedFaces, faceLabels);
return model;
}
// Convert the matrix row or column (float matrix) to a rectangular 8-bit image that can be displayed or saved.
// Scales the values to be between 0 to 255.
Mat getImageFrom1DFloatMat(const Mat matrixRow, int height)
{
// Make it a rectangular shaped image instead of a single row.
Mat rectangularMat = matrixRow.reshape(1, height);
// Scale the values to be between 0 to 255 and store them as a regular 8-bit uchar image.
Mat dst;
normalize(rectangularMat, dst, 0, 255, NORM_MINMAX, CV_8UC1);
return dst;
}
// Show the internal face recognition data, to help debugging.
void showTrainingDebugData(const Ptr<FaceRecognizer> model, const int faceWidth, const int faceHeight)
{
try { // Surround the OpenCV calls by a try/catch block so we don't crash if some model parameters aren't available.
// Show the average face (statistical average for each pixel in the collected images).
Mat averageFaceRow = model->get<Mat>("mean");
printMatInfo(averageFaceRow, "averageFaceRow");
// Convert the matrix row (1D float matrix) to a regular 8-bit image.
Mat averageFace = getImageFrom1DFloatMat(averageFaceRow, faceHeight);
printMatInfo(averageFace, "averageFace");
imshow("averageFace", averageFace);
// Get the eigenvectors
Mat eigenvectors = model->get<Mat>("eigenvectors");
printMatInfo(eigenvectors, "eigenvectors");
// Show the best 20 eigenfaces
for (int i = 0; i < min(20, eigenvectors.cols); i++) {
// Create a column vector from eigenvector #i.
// Note that clone() ensures it will be continuous, so we can treat it like an array, otherwise we can't reshape it to a rectangle.
// Note that the FaceRecognizer class already gives us L2 normalized eigenvectors, so we don't have to normalize them ourselves.
Mat eigenvectorColumn = eigenvectors.col(i).clone();
//printMatInfo(eigenvectorColumn, "eigenvector");
Mat eigenface = getImageFrom1DFloatMat(eigenvectorColumn, faceHeight);
//printMatInfo(eigenface, "eigenface");
imshow(format("Eigenface%d", i), eigenface);
}
// Get the eigenvalues
Mat eigenvalues = model->get<Mat>("eigenvalues");
printMat(eigenvalues, "eigenvalues");
//int ncomponents = model->get<int>("ncomponents");
//cout << "ncomponents = " << ncomponents << endl;
vector<Mat> projections = model->get<vector<Mat> >("projections");
cout << "projections: " << projections.size() << endl;
for (int i = 0; i < (int)projections.size(); i++) {
printMat(projections[i], "projections");
}
//labels = model->get<Mat>("labels");
//printMat(labels, "labels");
} catch (cv::Exception e) {
//cout << "WARNING: Missing FaceRecognizer properties." << endl;
}
}
// Generate an approximately reconstructed face by back-projecting the eigenvectors & eigenvalues of the given (preprocessed) face.
Mat reconstructFace(const Ptr<FaceRecognizer> model, const Mat preprocessedFace)
{
// Since we can only reconstruct the face for some types of FaceRecognizer models (ie: Eigenfaces or Fisherfaces),
// we should surround the OpenCV calls by a try/catch block so we don't crash for other models.
try {
// Get some required data from the FaceRecognizer model.
Mat eigenvectors = model->get<Mat>("eigenvectors");
Mat averageFaceRow = model->get<Mat>("mean");
int faceHeight = preprocessedFace.rows;
// Project the input image onto the PCA subspace.
Mat projection = subspaceProject(eigenvectors, averageFaceRow, preprocessedFace.reshape(1,1));
//printMatInfo(projection, "projection");
// Generate the reconstructed face back from the PCA subspace.
Mat reconstructionRow = subspaceReconstruct(eigenvectors, averageFaceRow, projection);
//printMatInfo(reconstructionRow, "reconstructionRow");
// Convert the float row matrix to a regular 8-bit image. Note that we
// shouldn't use "getImageFrom1DFloatMat()" because we don't want to normalize
// the data since it is already at the perfect scale.
// Make it a rectangular shaped image instead of a single row.
Mat reconstructionMat = reconstructionRow.reshape(1, faceHeight);
// Convert the floating-point pixels to regular 8-bit uchar pixels.
Mat reconstructedFace = Mat(reconstructionMat.size(), CV_8U);
reconstructionMat.convertTo(reconstructedFace, CV_8U, 1, 0);
//printMatInfo(reconstructedFace, "reconstructedFace");
return reconstructedFace;
} catch (cv::Exception e) {
//cout << "WARNING: Missing FaceRecognizer properties." << endl;
return Mat();
}
}
// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity(const Mat A, const Mat B)
{
if (A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols) {
// Calculate the L2 relative error between the 2 images.
double errorL2 = norm(A, B, CV_L2);
// Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
double similarity = errorL2 / (double)(A.rows * A.cols);
return similarity;
}
else {
//cout << "WARNING: Images have a different size in 'getSimilarity()'." << endl;
return 100000000.0; // Return a bad value
}
}