Merge commit '719149877e' into merge-2.4 (-s ours)
[platform/upstream/opencv.git] / modules / contrib / doc / facerec / src / facerec_demo.cpp
1 /*
2  * Copyright (c) 2011. Philipp Wagner <bytefish[at]gmx[dot]de>.
3  * Released to public domain under terms of the BSD Simplified license.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *   * Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *   * Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *   * Neither the name of the organization nor the names of its contributors
13  *     may be used to endorse or promote products derived from this software
14  *     without specific prior written permission.
15  *
16  *   See <http://www.opensource.org/licenses/bsd-license>
17  */
18
19 #include "opencv2/core.hpp"
20 #include "opencv2/contrib.hpp"
21 #include "opencv2/highgui.hpp"
22
23
24 #include <iostream>
25 #include <fstream>
26 #include <sstream>
27
28 using namespace cv;
29 using namespace std;
30
31 static Mat norm_0_255(InputArray _src) {
32     Mat src = _src.getMat();
33     // Create and return normalized image:
34     Mat dst;
35     switch(src.channels()) {
36     case 1:
37         cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
38         break;
39     case 3:
40         cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
41         break;
42     default:
43         src.copyTo(dst);
44         break;
45     }
46     return dst;
47 }
48
49 static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
50     std::ifstream file(filename.c_str(), ifstream::in);
51     if (!file) {
52         string error_message = "No valid input file was given, please check the given filename.";
53         CV_Error(CV_StsBadArg, error_message);
54     }
55     string line, path, classlabel;
56     while (getline(file, line)) {
57         stringstream liness(line);
58         getline(liness, path, separator);
59         getline(liness, classlabel);
60         if(!path.empty() && !classlabel.empty()) {
61             images.push_back(imread(path, 0));
62             labels.push_back(atoi(classlabel.c_str()));
63         }
64     }
65 }
66
67 int main(int argc, const char *argv[]) {
68     // Check for valid command line arguments, print usage
69     // if no arguments were given.
70     if (argc != 2) {
71         cout << "usage: " << argv[0] << " <csv.ext>" << endl;
72         exit(1);
73     }
74     // Get the path to your CSV.
75     string fn_csv = string(argv[1]);
76     // These vectors hold the images and corresponding labels.
77     vector<Mat> images;
78     vector<int> labels;
79     // Read in the data. This can fail if no valid
80     // input filename is given.
81     try {
82         read_csv(fn_csv, images, labels);
83     } catch (cv::Exception& e) {
84         cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
85         // nothing more we can do
86         exit(1);
87     }
88     // Quit if there are not enough images for this demo.
89     if(images.size() <= 1) {
90         string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
91         CV_Error(CV_StsError, error_message);
92     }
93     // Get the height from the first image. We'll need this
94     // later in code to reshape the images to their original
95     // size:
96     int height = images[0].rows;
97     // The following lines simply get the last images from
98     // your dataset and remove it from the vector. This is
99     // done, so that the training data (which we learn the
100     // cv::FaceRecognizer on) and the test data we test
101     // the model with, do not overlap.
102     Mat testSample = images[images.size() - 1];
103     int testLabel = labels[labels.size() - 1];
104     images.pop_back();
105     labels.pop_back();
106     // The following lines create an Eigenfaces model for
107     // face recognition and train it with the images and
108     // labels read from the given CSV file.
109     // This here is a full PCA, if you just want to keep
110     // 10 principal components (read Eigenfaces), then call
111     // the factory method like this:
112     //
113     //      cv::createEigenFaceRecognizer(10);
114     //
115     // If you want to create a FaceRecognizer with a
116     // confidennce threshold, call it with:
117     //
118     //      cv::createEigenFaceRecognizer(10, 123.0);
119     //
120     Ptr<FaceRecognizer> model = createFisherFaceRecognizer();
121     model->train(images, labels);
122     // The following line predicts the label of a given
123     // test image:
124     int predictedLabel = model->predict(testSample);
125     //
126     // To get the confidence of a prediction call the model with:
127     //
128     //      int predictedLabel = -1;
129     //      double confidence = 0.0;
130     //      model->predict(testSample, predictedLabel, confidence);
131     //
132     string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
133     cout << result_message << endl;
134     // Sometimes you'll need to get/set internal model data,
135     // which isn't exposed by the public cv::FaceRecognizer.
136     // Since each cv::FaceRecognizer is derived from a
137     // cv::Algorithm, you can query the data.
138     //
139     // First we'll use it to set the threshold of the FaceRecognizer
140     // to 0.0 without retraining the model. This can be useful if
141     // you are evaluating the model:
142     //
143     model->set("threshold", 0.0);
144     // Now the threshold of this model is set to 0.0. A prediction
145     // now returns -1, as it's impossible to have a distance below
146     // it
147     predictedLabel = model->predict(testSample);
148     cout << "Predicted class = " << predictedLabel << endl;
149     // Here is how to get the eigenvalues of this Eigenfaces model:
150     Mat eigenvalues = model->getMat("eigenvalues");
151     // And we can do the same to display the Eigenvectors (read Eigenfaces):
152     Mat W = model->getMat("eigenvectors");
153     // From this we will display the (at most) first 10 Eigenfaces:
154     for (int i = 0; i < min(10, W.cols); i++) {
155         string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
156         cout << msg << endl;
157         // get eigenvector #i
158         Mat ev = W.col(i).clone();
159         // Reshape to original size & normalize to [0...255] for imshow.
160         Mat grayscale = norm_0_255(ev.reshape(1, height));
161         // Show the image & apply a Jet colormap for better sensing.
162         Mat cgrayscale;
163         applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
164         imshow(format("%d", i), cgrayscale);
165     }
166     waitKey(0);
167
168     return 0;
169 }