|
12 | 12 | using namespace cv;
|
13 | 13 | using namespace std;
|
14 | 14 |
|
| 15 | +#define MAX_SLIDER_VALUE 255 |
| 16 | +#define NUM_EIGEN_FACES 10 |
| 17 | + |
| 18 | + |
| 19 | +Size sz; |
15 | 20 |
|
16 | 21 | // 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 |
31 | 25 | 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 | + |
32 | 45 | // Read jpg files from the directory
|
33 | 46 | void readFileNames(string dirName, vector<string> &imageFnames)
|
34 | 47 | {
|
| 48 | + |
| 49 | + cout << "Reading images from " << dirName; |
| 50 | + |
35 | 51 | DIR *dir;
|
36 | 52 | struct dirent *ent;
|
37 | 53 | int count = 0;
|
@@ -69,113 +85,189 @@ void readFileNames(string dirName, vector<string> &imageFnames)
|
69 | 85 | closedir (dir);
|
70 | 86 | }
|
71 | 87 |
|
| 88 | + cout << "... " << files.size() << " files read"<< endl; |
| 89 | + |
72 | 90 | }
|
73 | 91 |
|
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) |
76 | 94 | {
|
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; |
85 | 124 | }
|
86 | 125 |
|
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 | + } |
111 | 143 |
|
| 144 | + resize(output, output, Size(), 2, 2); |
| 145 | + |
| 146 | + imshow("Result", output); |
| 147 | + |
112 | 148 | }
|
113 | 149 |
|
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 | +} |
150 | 165 |
|
151 |
| - for(size_t i=0; i< images.size();i++) |
152 |
| - images[i] = images[i]-average; |
153 | 166 |
|
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); |
180 | 272 | }
|
181 | 273 |
|
0 commit comments