Overloaded PCA constructor and ( ) operator to implement Feature#2287 - PCA that...
[profile/ivi/opencv.git] / samples / cpp / pca.cpp
1 /*
2 * pca.cpp
3 *
4 *  Author: 
5 *  Kevin Hughes <kevinhughes27[at]gmail[dot]com>
6 *
7 *  Special Thanks to:
8 *  Philipp Wagner <bytefish[at]gmx[dot]de>
9 *
10 * This program demonstrates how to use OpenCV PCA with a 
11 * specified amount of variance to retain. The effect
12 * is illustrated further by using a trackbar to
13 * change the value for retained varaince.
14 *
15 * The program takes as input a text file with each line
16 * begin the full path to an image. PCA will be performed
17 * on this list of images. The author recommends using
18 * the first 15 faces of the AT&T face data set:
19 * http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html
20
21 * so for example your input text file would look like this:
22
23 *        <path_to_at&t_faces>/orl_faces/s1/1.pgm
24 *        <path_to_at&t_faces>/orl_faces/s2/1.pgm
25 *        <path_to_at&t_faces>/orl_faces/s3/1.pgm
26 *        <path_to_at&t_faces>/orl_faces/s4/1.pgm
27 *        <path_to_at&t_faces>/orl_faces/s5/1.pgm
28 *        <path_to_at&t_faces>/orl_faces/s6/1.pgm
29 *        <path_to_at&t_faces>/orl_faces/s7/1.pgm
30 *        <path_to_at&t_faces>/orl_faces/s8/1.pgm
31 *        <path_to_at&t_faces>/orl_faces/s9/1.pgm
32 *        <path_to_at&t_faces>/orl_faces/s10/1.pgm
33 *        <path_to_at&t_faces>/orl_faces/s11/1.pgm
34 *        <path_to_at&t_faces>/orl_faces/s12/1.pgm
35 *        <path_to_at&t_faces>/orl_faces/s13/1.pgm
36 *        <path_to_at&t_faces>/orl_faces/s14/1.pgm
37 *        <path_to_at&t_faces>/orl_faces/s15/1.pgm
38 *
39 */
40
41 #include <iostream>
42 #include <fstream>
43 #include <sstream>
44
45 #include <opencv2/core/core.hpp>
46 #include <opencv2/highgui/highgui.hpp>
47
48 using namespace cv;
49 using namespace std;
50
51 ///////////////////////
52 // Functions
53 void read_imgList(const string& filename, vector<Mat>& images) {
54     std::ifstream file(filename.c_str(), ifstream::in);
55     if (!file) {
56         string error_message = "No valid input file was given, please check the given filename.";
57         CV_Error(CV_StsBadArg, error_message);
58     }
59     string line;
60     while (getline(file, line)) {
61         images.push_back(imread(line, 0));
62     }
63 }
64
65 Mat formatImagesForPCA(const vector<Mat> &data)
66 {
67     Mat dst(data.size(), data[0].rows*data[0].cols, CV_32F);
68     for(unsigned int i = 0; i < data.size(); i++)
69     {
70         Mat image_row = data[i].clone().reshape(1,1);
71         Mat row_i = dst.row(i);
72         image_row.convertTo(row_i,CV_32F);    
73     }
74     return dst;
75 }
76
77 Mat toGrayscale(InputArray _src) {
78     Mat src = _src.getMat();
79     // only allow one channel
80     if(src.channels() != 1) {
81         CV_Error(CV_StsBadArg, "Only Matrices with one channel are supported");
82     }
83     // create and return normalized image
84     Mat dst;
85     cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
86     return dst;
87 }
88
89 struct params
90 {
91     Mat data;
92     int ch;
93     int rows;
94     PCA pca;
95     string winName;
96 };
97
98 void onTrackbar(int pos, void* ptr) 
99 {    
100     cout << "Retained Variance = " << pos << "%   ";
101     cout << "re-calculating PCA..." << std::flush;
102     
103     double var = pos / 100.0;
104     
105     struct params *p = (struct params *)ptr;
106     
107     p->pca = PCA(p->data, cv::Mat(), CV_PCA_DATA_AS_ROW, var);
108     
109     Mat point = p->pca.project(p->data.row(0));
110     Mat reconstruction = p->pca.backProject(point);
111     reconstruction = reconstruction.reshape(p->ch, p->rows);
112     reconstruction = toGrayscale(reconstruction);
113     
114     imshow(p->winName, reconstruction);
115     cout << "done!   # of principal components: " << p->pca.eigenvectors.rows << endl;
116 }
117
118
119 ///////////////////////
120 // Main
121 int main(int argc, char** argv) 
122 {
123     if (argc != 2) {
124         cout << "usage: " << argv[0] << " <image_list.txt>" << endl;
125         exit(1);
126     }
127     
128     // Get the path to your CSV.
129     string imgList = string(argv[1]);
130     
131     // vector to hold the images
132     vector<Mat> images;
133     
134     // Read in the data. This can fail if not valid
135     try {
136         read_imgList(imgList, images);
137     } catch (cv::Exception& e) {
138         cerr << "Error opening file \"" << imgList << "\". Reason: " << e.msg << endl;
139         exit(1);
140     }
141     
142     // Quit if there are not enough images for this demo.
143     if(images.size() <= 1) {
144         string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
145         CV_Error(CV_StsError, error_message);
146     }
147         
148     // Reshape and stack images into a rowMatrix
149     Mat data = formatImagesForPCA(images);
150     
151     // perform PCA
152     PCA pca(data, cv::Mat(), CV_PCA_DATA_AS_ROW, 0.95); // trackbar is initially set here, also this is a common value for retainedVariance
153     
154     // Demonstration of the effect of retainedVariance on the first image 
155     Mat point = pca.project(data.row(0)); // project into the eigenspace, thus the image becomes a "point"
156     Mat reconstruction = pca.backProject(point); // re-create the image from the "point"
157     reconstruction = reconstruction.reshape(images[0].channels(), images[0].rows); // reshape from a row vector into image shape
158     reconstruction = toGrayscale(reconstruction); // re-scale for displaying purposes
159     
160     // init highgui window
161     string winName = "Reconstruction | press 'q' to quit";
162     namedWindow(winName, CV_WINDOW_NORMAL);
163     
164     // params struct to pass to the trackbar handler
165     params p;
166     p.data = data;
167     p.ch = images[0].channels();
168     p.rows = images[0].rows;
169     p.pca = pca;
170     p.winName = winName;
171     
172     // create the tracbar
173     int pos = 95;
174     createTrackbar("Retained Variance (%)", winName, &pos, 100, onTrackbar, (void*)&p); 
175     
176     // display until user presses q
177     imshow(winName, reconstruction);
178     
179     char key = 0;
180     while(key != 'q')
181         key = waitKey();
182    
183    return 0; 
184 }