source: liacs/MIR2010/SourceCode/main.cpp@ 96

Last change on this file since 96 was 96, checked in by Rick van der Zwet, 15 years ago

reference implementation working with all categories

File size: 13.7 KB
Line 
1// main.cpp : Defines the entry point for the console application.
2//
3
4#include "config.h"
5#include <commdlg.h>
6#include <conio.h>
7#include <iostream>
8
9#include "cximage/ximage.h"
10#include "xbrowseforfolder.h"
11// function prototypes
12bool CalculateDescriptors(const char *basedir);
13bool CategorizeDescriptors(const char *basedir);
14bool DetermineWinterSportSelect();
15bool DetermineWinterSportBatch(const char *basedir);
16bool LoadAverages(const char *basedir);
17
18#define TRAINING_DIR_DEBUG "C:\\Documents and Settings\\rvdzwet\\Desktop\\liacs\\MIR2010\\trainingSet"
19#define TESTSET_DIR_DEBUG "C:\\Documents and Settings\\rvdzwet\\Desktop\\liacs\\MIR2010\\testSet"
20#define DEBUG 0
21// number of bins to use for each color
22// Note: number of bins must be a whole fraction of 256. if we would
23// use 256 bins for each color then a single histogram would be
24// 64MB, so we should choose a more sensible number
25#define BIN_COUNT 32
26
27// names of categories
28#define CATEGORY_SIZE 8
29#define CATEGORY_1 "cat1.crowd"
30#define CATEGORY_2 "cat2.skijump"
31#define CATEGORY_3 "cat3.snowboarding"
32#define CATEGORY_4 "opt1.bobsleigh"
33#define CATEGORY_5 "opt2.icehockey"
34#define CATEGORY_6 "opt3.speedskating"
35#define CATEGORY_7 "opt4.downhillski"
36#define CATEGORY_8 "opt5.curling"
37
38const char *categories[CATEGORY_SIZE] = { CATEGORY_1, CATEGORY_2, CATEGORY_3,
39 CATEGORY_4, CATEGORY_5, CATEGORY_6, CATEGORY_7, CATEGORY_8};
40float average[CATEGORY_SIZE][BIN_COUNT*BIN_COUNT*BIN_COUNT];
41
42// Some prototyping foo on functions
43#define _return(err_msg, retval) printf("DEBUG: %s\n",err_msg); cin.get(); return(retval);
44
45bool verbose = false;
46
47bool file_exists(const char * filename)
48{
49 if (FILE * file = fopen(filename, "r"))
50 {
51 fclose(file);
52 return true;
53 }
54 return false;
55}
56
57
58int main(int argc, char **argv)
59{
60 char dbdir[MAX_PATH];
61 char testdir[MAX_PATH];
62
63#if defined(TRAINING_DIR_DEBUG)
64 if (strcpy_s(dbdir, TRAINING_DIR_DEBUG) != 0) {
65 _return("Cannot copy TRAINING_DIR_DEBUG",1);
66 }
67 if (strcpy_s(testdir, TESTSET_DIR_DEBUG) != 0) {
68 _return("Cannot copy TESTSET_DIR_DEBUG",2);
69 }
70#else
71 // ask the user for the image database
72 // in the image directory, the images are stored in categorized
73 // folders. store the descriptors we calculate in the same
74 // folder as the image
75 if (XBrowseForFolder(NULL, "Please select image database folder", NULL, dbdir, sizeof(dbdir)) == FALSE)
76 return 0;
77 if (strlen(dbdir) == 0)
78 return 0;
79 if (XBrowseForFolder(NULL, "Please select image testset folder", NULL, testdir, sizeof(testdir)) == FALSE)
80 return 0;
81 if (strlen(testdir) == 0)
82 return 0;
83
84#endif
85
86#if DEBUG
87 if (!LoadAverages(dbdir)){
88 _return("Unable to load averages",1);
89 }
90
91 if (!DetermineWinterSportBatch(testdir)) {
92 _return("could not run winter sport batch",1);
93 }
94 return(0);
95#endif
96
97 // ask the user which option to use
98 while (true)
99 {
100 //system("cls");
101 cout << "Using database directory: " << dbdir << endl;
102 cout << "Using test directory: " << testdir << endl;
103 cout << "Using categories: " << endl;
104 for (int i = 0; i < CATEGORY_SIZE; i++)
105 {
106 cout << " - " << categories[i] << endl;
107 }
108 cout << "***************************" << endl;
109 cout << "* Winter Olympic Imagery *" << endl;
110 cout << "***************************" << endl;
111 cout << endl;
112 cout << "1. calculate descriptors" << endl;
113 cout << "2. categorize descriptors (aka averages histograms)" << endl;
114 cout << "3. determine winter sport" << endl;
115 cout << "4. batch test winter sport" << endl;
116 cout << "v. Turn verbose mode ";
117 if (verbose)
118 cout << "off" << endl;
119 else
120 cout << "on" << endl;
121 cout << endl;
122 cout << "Please select option, or type 'q' to quit: ";
123 char c = _getch();
124 cout << c << endl;
125 fflush(stdin);
126 // start the chosen option
127 switch (c)
128 {
129 case 'q':
130 return 0;
131 case '1':
132 if (!CalculateDescriptors(dbdir)) {
133 _return("could not calculate descriptors",1);
134 }
135 break;
136 case '2':
137 if (!CategorizeDescriptors(dbdir)){
138 _return("could not categorize descriptors",1);
139 }
140 break;
141 case '3':
142 if (!LoadAverages(dbdir)){
143 _return("Unable to load averages",1);
144 }
145
146 if (!DetermineWinterSportSelect()){
147 _return("could not determine winter sport",1);
148 }
149 break;
150 case '4':
151 if (!LoadAverages(dbdir)){
152 _return("Unable to load averages",1);
153 }
154
155 if (!DetermineWinterSportBatch(testdir)) {
156 _return("could not run winter sport batch",1);
157 }
158 break;
159 case 'v':
160 verbose = (!verbose);
161 default:
162 continue;
163 }
164 }
165 return 0;
166}
167
168// histogram should be a preallocated array of size BIN_COUNT*BIN_COUNT*BIN_COUNT elements and will
169// be filled with the color histogram of the image where path points at
170bool CalculateDescriptor(const char *path, float *histogram)
171{
172 // load the image
173 CxImage image(path, CXIMAGE_FORMAT_JPG);
174 if (!image.IsValid())
175 return false;
176 // clear histogram
177 memset(histogram, 0, BIN_COUNT*BIN_COUNT*BIN_COUNT * sizeof(float));
178 // walk through the pixels to fill the histogram
179 int width = (int)image.GetWidth();
180 int height = (int)image.GetHeight();
181 int bin_r, bin_g, bin_b;
182 for (int y = 0; y < height; y++)
183 {
184 for (int x = 0; x < width; x++)
185 {
186 // Note: CxImage library starts counting at lower-left corner of the image,
187 // which is seen as the top of the image. however, usually images
188 // start counting from the top-left corner of the image. thus if you
189 // want to get pixel(2, 2) from the top-left you would have to ask
190 // for pixel (2, height - 2 - 1) from CxImage. although in this
191 // situation we don't care which pixel is where, we only care about
192 // its color.
193 RGBQUAD rgb = image.BlindGetPixelColor(x, y, false);
194 // determine the bin this color falls in
195 bin_r = rgb.rgbRed / (256 / BIN_COUNT);
196 bin_g = rgb.rgbGreen / (256 / BIN_COUNT);
197 bin_b = rgb.rgbBlue / (256 / BIN_COUNT);
198 histogram[bin_r*BIN_COUNT*BIN_COUNT + bin_g*BIN_COUNT + bin_b]++;
199 }
200 }
201 // normalize the histogram so that all together the values will add up
202 // to one. since there are width * height pixels, we divide each value
203 // by this amount
204 for (int i = 0; i < BIN_COUNT*BIN_COUNT*BIN_COUNT; i++)
205 histogram[i] /= width * height;
206 return true;
207}
208
209bool CalculateDescriptors(const char *basedir)
210{
211 // the histogram that we reuse for each image
212 float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
213 // walk through all images
214 // Note: each of the three categories has 50 images
215 char path[MAX_PATH];
216 char catdir[MAX_PATH];
217 const char *catname;
218 FILE *file = NULL;
219 for (int c = 0; c < CATEGORY_SIZE; c++)
220 {
221 catname = categories[c];
222 sprintf(catdir, "%s\\%s\\", basedir, catname);
223 cout << "[" << catname << "] Using directory " << catdir << endl;
224
225 // process the images in the directory
226 for (int i = 1; i <= 50; i++)
227 {
228 SAFE_SPRINTF(path, sizeof(path), "%s%i.jpg", catdir, i);
229 if (!file_exists(path)) {
230 continue;
231 }
232 cout << "[" << catname << "] processing image " << i << endl;
233 // calculate the histogram descriptor
234 if (!CalculateDescriptor(path, histogram))
235 goto failure;
236 // save the descriptor to disk
237 SAFE_SPRINTF(path, sizeof(path), "%s%i.dat", catdir, i);
238 if ((file = fopen(path, "wb")) == NULL)
239 goto failure;
240 if (fwrite(histogram, sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
241 goto failure;
242 SAFE_CLOSEFILE(file);
243 }
244 }
245 // release resources
246 SAFE_DELETE_ARRAY(histogram);
247 return true;
248
249failure:
250 SAFE_CLOSEFILE(file);
251 SAFE_DELETE_ARRAY(histogram);
252 return false;
253}
254
255bool CategorizeDescriptors(const char *basedir)
256{
257 // analyze the descriptors per category to determine the
258 // characteristics of that category
259 float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
260 float *average = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
261 // walk through all descriptors
262 char path[MAX_PATH];
263 char catdir[MAX_PATH];
264 const char *catname;
265 FILE *file = NULL;
266 int c_size = 0;
267 for (int c = 0; c < CATEGORY_SIZE; c++)
268 {
269 c_size = 0;
270 catname = categories[c];
271 sprintf(catdir,"%s\\%s\\", basedir, catname);
272
273 // average all descriptors
274 memset(average, 0, BIN_COUNT*BIN_COUNT*BIN_COUNT * sizeof(float));
275 for (int i = 1; i <= 50; i++)
276 {
277 SAFE_SPRINTF(path, sizeof(path), "%s%i.dat", catdir, i);
278 if (!file_exists(path)) {
279 continue;
280 }
281 cout << "[" << catname << "] processing image " << i << endl;
282 // load the histogram descriptor
283 if ((file = fopen(path, "rb")) == NULL)
284 goto failure;
285 if (fread(histogram, sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
286 goto failure;
287 SAFE_CLOSEFILE(file);
288 // add the value of each bin to the average
289 for (int b = 0; b < BIN_COUNT*BIN_COUNT*BIN_COUNT; b++)
290 average[b] += histogram[b];
291
292 c_size++;
293 }
294
295 for (int b = 0; b < BIN_COUNT*BIN_COUNT*BIN_COUNT; b++)
296 average[b] /= c_size;
297 // save the average to disk
298 SAFE_SPRINTF(path, sizeof(path), "%s%s.dat", catdir, "average");
299 if ((file = fopen(path, "wb")) == NULL)
300 goto failure;
301 if (fwrite(average, sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
302 goto failure;
303 SAFE_CLOSEFILE(file);
304 }
305 // release resources
306 SAFE_DELETE_ARRAY(histogram);
307 SAFE_DELETE_ARRAY(average);
308 return true;
309
310failure:
311 SAFE_CLOSEFILE(file);
312 SAFE_DELETE_ARRAY(histogram);
313 SAFE_DELETE_ARRAY(average);
314 return false;
315}
316
317bool LoadAverages(const char *basedir) {
318 /* determine the distance to each category */
319 const char *catname;
320 char catdir[MAX_PATH];
321 char path[MAX_PATH];
322 FILE *file = NULL;
323
324 for (int c = 0; c < CATEGORY_SIZE; c++)
325 {
326 catname = categories[c];
327 sprintf(catdir, "%s\\%s\\", basedir, catname);
328
329 // load the average from disk
330 SAFE_SPRINTF(path, sizeof(path), "%s%s.dat", catdir, "average");
331 if ((file = fopen(path, "rb")) == NULL) {
332 cout << "Cannot open " << path << endl;
333 return false;
334 }
335 if (fread(average[c], sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
336 return false;
337 SAFE_CLOSEFILE(file);
338 }
339 return true;
340}
341
342int DetermineCategory(const char *path, const int guess=-1) {
343 float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
344 float cat2dist[CATEGORY_SIZE];
345
346 /* First category default best canidate */
347 int retval = 0;
348
349 /* calculate the histogram of the image */
350 if (!CalculateDescriptor(path, histogram))
351 return -1;
352
353 /* determine the distance to each category */
354 for (int c = 0; c < CATEGORY_SIZE; c++)
355 {
356 // determine distance
357 cat2dist[c] = 0.0f;
358 for (int b = 0; b < BIN_COUNT*BIN_COUNT*BIN_COUNT; b++)
359 cat2dist[c] += fabs(histogram[b] - average[c][b]);
360 }
361
362 /* determine the winning category */
363 for (int i = 1; i < CATEGORY_SIZE; i++)
364 if (cat2dist[i] < cat2dist[retval])
365 retval = i;
366
367 if (verbose) {
368 /* Dirty hack to show some more details in case of failure */
369 if (guess != -1 && guess != retval) {
370 for (int i = 0; i < CATEGORY_SIZE; i++) {
371 printf("%s Distance to %-20s: %f %s\n", (retval == i) ? "*" : " ",
372 categories[i], cat2dist[i],(retval == i) ? "*" : "");
373 }
374 }
375 }
376
377 /* return result */
378 return retval;
379}
380
381/* ask for an input image and determine the most likely
382* category it belongs to
383*/
384bool DetermineWinterSportSelect()
385{
386 float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
387 char path[MAX_PATH] = {0};
388 char catdir[MAX_PATH] = {0};
389 const char *catname = NULL;
390 FILE *file = NULL;
391 int c = NULL;
392 float cat2dist[CATEGORY_SIZE] = {0};
393
394 OPENFILENAME ofn = {0};
395 //ofn.lpstrFilter = "Image files\0*.jpg;*.png;*.bmp\0\0";
396 ofn.lpstrFilter = "Image files\0*.jpg\0\0";
397 ofn.lpstrFile = path;
398 ofn.nMaxFile = MAX_PATH;
399 ofn.lpstrTitle = "Choose image file";
400 ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
401 ofn.lStructSize = sizeof(OPENFILENAME);
402
403 if (GetOpenFileName(&ofn) == FALSE)
404 goto failure;
405
406 if ((c = DetermineCategory(path,-2)) == -1)
407 return false;
408
409 cout << "The category this image belongs is category: " << categories[c] << " [" << c << "]" << endl;
410 cout << "Press any key to continue... ";
411 _getch();
412 fflush(stdin);
413 // release resources
414 SAFE_DELETE_ARRAY(histogram);
415 return true;
416
417failure:
418 SAFE_CLOSEFILE(file);
419 SAFE_DELETE_ARRAY(histogram);
420 return false;
421}
422
423bool DetermineWinterSportBatch(const char *basedir)
424{
425 const char *catname;
426 char catdir[MAX_PATH];
427 char path[MAX_PATH];
428
429 int all_total = 0;
430 int all_succes = 0;
431 int c_total[CATEGORY_SIZE] = {0};
432 int c_succes[CATEGORY_SIZE] = {0};
433
434 /* determine the distance to each category */
435 for (int c = 0; c < CATEGORY_SIZE; c++)
436 {
437 catname = categories[c];
438 sprintf(catdir, "%s\\%s\\", basedir, catname);
439
440 /* process the images in the directory */
441 for (int i = 1; i <= 50; i++)
442 {
443 SAFE_SPRINTF(path, sizeof(path), "%s%i.jpg", catdir, i);
444 if (!file_exists(path)) {
445 continue;
446 }
447
448 c_total[c]++;
449 /* Check if file matches category */
450 if (DetermineCategory(path,c) == c) {
451 cout << "[" << catname << "] testing image " << i << " : OK" << endl;
452 c_succes[c]++;
453 } else {
454 cout << "[" << catname << "] testing image " << i << " : FAIL" << endl; }
455 }
456 cout << "[" << catname << "] results " << c_succes[c] << "/" << c_total[c] << endl;
457 }
458
459 /* Display grand total */
460 cout << "=== Totals ===" << endl;
461 for (int c = 0; c < CATEGORY_SIZE; c++)
462 {
463 catname = categories[c];
464 printf ("[%-20s] %i/%i\n",catname,c_succes[c],c_total[c]);
465 all_total += c_total[c];
466 all_succes += c_succes[c];
467 }
468 printf ("[%-20s] %i/%i\n","total",all_succes,all_succes);
469 cout << "Press any key to continue..."; cin.get();
470
471
472 return true;
473}
Note: See TracBrowser for help on using the repository browser.