Skip to content

Commit 3f8909e

Browse files
committed
Working EigenFace
1 parent 4d20fe6 commit 3f8909e

File tree

1 file changed

+204
-112
lines changed

1 file changed

+204
-112
lines changed

PCA/PCA.cpp

Lines changed: 204 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,42 @@
1212
using namespace cv;
1313
using namespace std;
1414

15+
#define MAX_SLIDER_VALUE 255
16+
#define NUM_EIGEN_FACES 10
17+
18+
19+
Size sz;
1520

1621
// Weights for the different eigenvectors
17-
int wt_0 = 0;
18-
int wt_1 = 0;
19-
int wt_2 = 0;
20-
int wt_3 = 0;
21-
int wt_4 = 0;
22-
int wt_5 = 0;
23-
int wt_6 = 0;
24-
int wt_7 = 0;
25-
int wt_8 = 0;
26-
int wt_9 = 0;
27-
28-
int max_wt =255;
29-
30-
// Matrix for average and eigenvectors
22+
int sliderValues[10];
23+
24+
// Matrices for average (mean) and eigenvectors
3125
Mat average, eigenvectors;
26+
27+
// Initializes all eigenface weights to 0.
28+
// To do this we set the slider values to 128
29+
// because OpenCV does not allow negative values
30+
// for sliders. So we create negative values by
31+
// subtracting 128 from the actual slider value
32+
// Therefore,
33+
// weight = sliderValue - MAX_SLIDER_VALUE/2
34+
35+
void initSliderValues()
36+
{
37+
for(int i = 0; i < NUM_EIGEN_FACES; i++)
38+
{
39+
sliderValues[i] = MAX_SLIDER_VALUE/2;
40+
}
41+
42+
}
43+
44+
3245
// Read jpg files from the directory
3346
void readFileNames(string dirName, vector<string> &imageFnames)
3447
{
48+
49+
cout << "Reading images from " << dirName;
50+
3551
DIR *dir;
3652
struct dirent *ent;
3753
int count = 0;
@@ -69,113 +85,189 @@ void readFileNames(string dirName, vector<string> &imageFnames)
6985
closedir (dir);
7086
}
7187

88+
cout << "... " << files.size() << " files read"<< endl;
89+
7290
}
7391

74-
// Format images according to the requirement of pca function
75-
static Mat formatImagesForPCA(const vector<Mat> &data)
92+
// Reshapes image to a long row vector
93+
static Mat createDataMatrix(const vector<Mat> &images)
7694
{
77-
Mat dst(static_cast<int>(data.size()), data[0].rows*data[0].cols*3, CV_32F);
78-
for(unsigned int i = 0; i < data.size(); i++)
79-
{
80-
Mat image_row = data[i].clone().reshape(1,1);
81-
Mat row_i = dst.row(i);
82-
image_row.convertTo(row_i,CV_32F);
83-
}
84-
return dst;
95+
cout << "Creating data matrix from images ...";
96+
97+
// Allocate space for all images in one data matrix.
98+
// The size of the data matrix is
99+
//
100+
// ( w * h * 3, numImages )
101+
//
102+
// where,
103+
//
104+
// w = width of an image in the dataset.
105+
// h = height of an image in the dataset.
106+
// 3 is for the 3 color channels.
107+
// numImages = number of images in the dataset.
108+
109+
Mat data(static_cast<int>(images.size()), images[0].rows * images[0].cols * 3, CV_32F);
110+
111+
// Turn an image into one row vector in the data matrix
112+
for(unsigned int i = 0; i < images.size(); i++)
113+
{
114+
// Extract image as one long vector of size w x h x 3
115+
Mat image = images[i].reshape(1,1);
116+
117+
// Copy the long vector into one row of the destm
118+
image.copyTo(data.row(i));
119+
120+
}
121+
122+
cout << " DONE" << endl;
123+
return data;
85124
}
86125

87-
// Add weighted eigen vectors to the average image
88-
void calcFinalImage(int ,void *){
89-
Mat temp = average.clone();
90-
Mat eig1 = eigenvectors.row(0).reshape(3,200);
91-
Mat eig2 = eigenvectors.row(1).reshape(3,200);
92-
Mat eig3 = eigenvectors.row(2).reshape(3,200);
93-
Mat eig4 = eigenvectors.row(3).reshape(3,200);
94-
Mat eig5 = eigenvectors.row(4).reshape(3,200);
95-
Mat eig6 = eigenvectors.row(5).reshape(3,200);
96-
Mat eig7 = eigenvectors.row(6).reshape(3,200);
97-
Mat eig8 = eigenvectors.row(7).reshape(3,200);
98-
Mat eig9 = eigenvectors.row(8).reshape(3,200);
99-
Mat eig10 = eigenvectors.row(9).reshape(3,200);
100-
temp = temp + eig1*wt_0;
101-
temp = temp + eig2*wt_1;
102-
temp = temp + eig3*wt_2;
103-
temp = temp + eig4*wt_3;
104-
temp = temp + eig5*wt_4;
105-
temp = temp + eig6*wt_5;
106-
temp = temp + eig7*wt_6;
107-
temp = temp + eig8*wt_7;
108-
temp = temp + eig9*wt_8;
109-
temp = temp + eig10*wt_9;
110-
imshow("Result", temp);
126+
// Calculate final image by adding weighted
127+
// EigenFaces to the mean image.
128+
void calcFinalImage(int ,void *)
129+
{
130+
// Start with the mean image
131+
Mat output = average.clone();
132+
133+
// Add the eigen faces with the weights
134+
for(int i = 0; i < NUM_EIGEN_FACES; i++)
135+
{
136+
Mat eigenFace = eigenvectors.row(i).reshape(3,sz.height);
137+
138+
// OpenCV does not allow slider values to be negative.
139+
// So we use weight = sliderValue - MAX_SLIDER_VALUE / 2
140+
double weight = sliderValues[i] - MAX_SLIDER_VALUE/2;
141+
output = output + eigenFace * weight;
142+
}
111143

144+
resize(output, output, Size(), 2, 2);
145+
146+
imshow("Result", output);
147+
112148
}
113149

114-
int main(int argc, char **argv){
115-
cout << "Hello"<<endl;
116-
string dirName = "../PCA/images";
117-
// Add slash to directory name if missing
118-
if (!dirName.empty() && dirName.back() != '/')
119-
dirName += '/';
120-
// Read images in the directory
121-
vector<string> imageNames;
122-
readFileNames(dirName, imageNames);
123-
124-
// Exit program if no images are found or if the number of image files does not match with the number of point files
125-
if(imageNames.empty())exit(EXIT_FAILURE);
126-
127-
vector <Mat> images;
128-
for(size_t i = 0; i < imageNames.size(); i++)
129-
{
130-
Mat img = imread(imageNames[i]);
131-
if(!img.data)
132-
{
133-
cout << "image " << imageNames[i] << " not read properly" << endl;
134-
}
135-
else
136-
{
137-
img.convertTo(img, CV_32FC3, 1/255.0);
138-
resize(img, img, Size(200,200),0,0, INTER_CUBIC);
139-
images.push_back(img);
140-
}
141-
}
142-
143-
// Initialize average image
144-
average= Mat::zeros(200,200, CV_32FC3);
145-
for(size_t i=0; i<images.size();i++){
146-
average = average+images[i];
147-
}
148-
// Calculate average image
149-
average = average/(images.size());
150+
// Reset slider values
151+
void resetSliderValues(int event, int x, int y, int flags, void* userdata)
152+
{
153+
if (event == EVENT_LBUTTONDOWN)
154+
{
155+
for(int i = 0; i < NUM_EIGEN_FACES; i++)
156+
{
157+
sliderValues[i] = 128;
158+
setTrackbarPos("Weight" + to_string(i), "Trackbars", MAX_SLIDER_VALUE/2);
159+
}
160+
161+
calcFinalImage(0,0);
162+
163+
}
164+
}
150165

151-
for(size_t i=0; i< images.size();i++)
152-
images[i] = images[i]-average;
153166

154-
cout << "Formatting images..."<<endl;
155-
Mat data = formatImagesForPCA(images);
156-
cout << "DONE" << endl;
157-
158-
cout << "Calculating PCA..." << endl;
159-
// Calculate PCA of the data matrix
160-
PCA pca(data, cv::Mat(), PCA::DATA_AS_ROW, 10);
161-
cout << "DONE"<< endl;
162-
eigenvectors = pca.eigenvectors;
163-
164-
namedWindow("Trackbars", CV_WINDOW_AUTOSIZE);
165-
// Show average image
166-
imshow("Average", average);
167-
168-
// Create trackbars
169-
createTrackbar( "Weight0", "Trackbars", &wt_0, max_wt, calcFinalImage);
170-
createTrackbar( "Weight1", "Trackbars", &wt_1, max_wt, calcFinalImage);
171-
createTrackbar( "Weight2", "Trackbars", &wt_2, max_wt, calcFinalImage);
172-
createTrackbar( "Weight3", "Trackbars", &wt_3, max_wt, calcFinalImage);
173-
createTrackbar( "Weight4", "Trackbars", &wt_4, max_wt, calcFinalImage);
174-
createTrackbar( "Weight5", "Trackbars", &wt_5, max_wt, calcFinalImage);
175-
createTrackbar( "Weight6", "Trackbars", &wt_6, max_wt, calcFinalImage);
176-
createTrackbar( "Weight7", "Trackbars", &wt_7, max_wt, calcFinalImage);
177-
createTrackbar( "Weight8", "Trackbars", &wt_8, max_wt, calcFinalImage);
178-
createTrackbar( "Weight9", "Trackbars", &wt_9, max_wt, calcFinalImage);
179-
waitKey(0);
167+
int main(int argc, char **argv)
168+
{
169+
string dirName = "../PCA/images";
170+
171+
// Add slash to directory name if missing
172+
if (!dirName.empty() && dirName.back() != '/')
173+
dirName += '/';
174+
175+
176+
// Read images in the directory
177+
vector<string> imageNames;
178+
readFileNames(dirName, imageNames);
179+
180+
181+
// Exit program if no images are found
182+
if(imageNames.empty())exit(EXIT_FAILURE);
183+
184+
// Read images
185+
vector <Mat> images;
186+
for(size_t i = 0; i < imageNames.size(); i++)
187+
{
188+
Mat img = imread(imageNames[i]);
189+
if(!img.data)
190+
{
191+
cout << "image " << imageNames[i] << " not read properly" << endl;
192+
}
193+
else
194+
{
195+
if ( i == 0)
196+
{
197+
// Set size of images based on the first image.
198+
// The rest of the code assumes all images are of this size.
199+
sz = img.size();
200+
}
201+
// Convert images to floating point type
202+
img.convertTo(img, CV_32FC3, 1/255.0);
203+
images.push_back(img);
204+
205+
// A vertically flipped image is also a valid face image.
206+
// So lets use them as well.
207+
Mat imgFlip;
208+
flip(img, imgFlip, 1);
209+
images.push_back(imgFlip);
210+
}
211+
}
212+
213+
// Initialize average image
214+
average= Mat::zeros(sz, CV_32FC3);
215+
216+
// Calculate average image
217+
for(size_t i=0; i<images.size();i++)
218+
{
219+
average = average+images[i];
220+
}
221+
average = average/(images.size());
222+
223+
// Subtract mean face from every image.
224+
for(size_t i=0; i< images.size();i++)
225+
{
226+
images[i] = images[i]-average;
227+
}
228+
229+
// Create data matrix for PCA.
230+
Mat data = createDataMatrix(images);
231+
232+
// Calculate PCA of the data matrix
233+
cout << "Calculating PCA ...";
234+
PCA pca(data, Mat(), PCA::DATA_AS_ROW, 10);
235+
cout << " DONE"<< endl;
236+
237+
//average = pca.mean;
238+
//average = average.reshape(3,sz.height);
239+
240+
// Find eigen vectors. These are the eigen faces
241+
eigenvectors = pca.eigenvectors;
242+
243+
// Show mean face image
244+
Mat display;
245+
resize(average, display, Size(), 2, 2);
246+
247+
namedWindow("Result", CV_WINDOW_AUTOSIZE);
248+
imshow("Result", display);
249+
250+
imshow("Average", display);
251+
252+
// Initialize sliders to 128
253+
// Note : Actual weights used is ( sliderValue - 128 )
254+
// because OpenCV sliders cannot be negative.
255+
// So 128 corresponds to a weight of 0.
256+
initSliderValues();
257+
258+
// Create trackbars
259+
namedWindow("Trackbars", CV_WINDOW_AUTOSIZE);
260+
for(int i = 0; i < NUM_EIGEN_FACES; i++)
261+
{
262+
createTrackbar( "Weight" + to_string(i), "Trackbars", &sliderValues[i], MAX_SLIDER_VALUE, calcFinalImage);
263+
}
264+
265+
// You can reset the sliders by clicking on the mean image.
266+
setMouseCallback("Result", resetSliderValues);
267+
268+
cout << endl << "Usage:" << endl;
269+
cout << "Change the weights using the sliders" << endl;
270+
cout << "Click on the result window to reset sliders" << endl;
271+
waitKey(0);
180272
}
181273

0 commit comments

Comments
 (0)