Index: /liacs/MIR2010/SourceCode/main.cpp
===================================================================
--- /liacs/MIR2010/SourceCode/main.cpp	(revision 97)
+++ /liacs/MIR2010/SourceCode/main.cpp	(revision 98)
@@ -6,7 +6,10 @@
 #include <conio.h>
 #include <iostream>
+#include <iomanip>
+#include <queue>
 
 #include "cximage/ximage.h"
 #include "xbrowseforfolder.h"
+
 // function prototypes
 bool CalculateDescriptors(const char *basedir);
@@ -15,15 +18,11 @@
 bool DetermineWinterSportBatch(const char *basedir);
 bool LoadAverages(const char *basedir);
+bool ShowAverages(const char *basedir);
 
 #define TRAINING_DIR_DEBUG "C:\\Documents and Settings\\rvdzwet\\Desktop\\liacs\\MIR2010\\trainingSet"
 #define TESTSET_DIR_DEBUG "C:\\Documents and Settings\\rvdzwet\\Desktop\\liacs\\MIR2010\\testSet"
 #define DEBUG 0
-// number of bins to use for each color
-// Note: number of bins must be a whole fraction of 256. if we would
-//       use 256 bins for each color then a single histogram would be
-//       64MB, so we should choose a more sensible number
-#define BIN_COUNT	32
-
-// names of categories
+
+// Names of categories (folders)
 #define CATEGORY_SIZE 8
 #define CATEGORY_1	"cat1.crowd"
@@ -36,16 +35,26 @@
 #define CATEGORY_8  "opt5.curling"
 
-const char *categories[CATEGORY_SIZE] = { CATEGORY_1, CATEGORY_2, CATEGORY_3, 
-	CATEGORY_4, CATEGORY_5, CATEGORY_6,  CATEGORY_7, CATEGORY_8};  
+// Nicely concat into a string array
+const char *categories[CATEGORY_SIZE] = { 
+	CATEGORY_1, CATEGORY_2, CATEGORY_3, 
+	CATEGORY_4, CATEGORY_5, CATEGORY_6,  
+	CATEGORY_7, CATEGORY_8};
+
+// number of bins to use for each color
+// Note: number of bins must be a whole fraction of 256. if we would
+//       use 256 bins for each color then a single histogram would be
+//       64MB, so we should choose a more sensible number
+#define BIN_COUNT	32
 float average[CATEGORY_SIZE][BIN_COUNT*BIN_COUNT*BIN_COUNT];
 
 
 // Find common coloured shapes as characteristics
-// Walk over image with SPREAD steps, if pixel -within TOLERANCE- matches previous pixel 
+// Walk over image with opt_spread steps, if pixel -within opt_tolerance- matches previous pixel 
 // make the size of the specific block bigger.
-#define TOLERANCE 20
-#define SPREAD 20
-#define MAX_SIZE 500
-float average_block[CATEGORY_SIZE][MAX_SIZE/SPREAD];
+// Block '0' is used an non-matcher. Not taken into account for the time beeing.
+int opt_tolerance = 10;
+int opt_spread = 20;
+#define MAX_BLOCKSIZE 500
+float average_block[CATEGORY_SIZE][MAX_BLOCKSIZE];
 
 
@@ -54,8 +63,10 @@
 #define _return(err_msg, retval) printf("DEBUG: %s\n",err_msg); cin.get(); return(retval);
 #define cout_status(msg,flag) cout << msg; (flag) ? cout << " off" : cout << " on"; cout << endl;
-
-
+#define cout_point(x,y) "[" << x << "," << y << "]"
+
+// Default options
+bool opt_debug = false;
 bool opt_verbose = false;
-bool opt_histogram = true;
+bool opt_histogram = false;
 bool opt_block = true;
 
@@ -92,5 +103,5 @@
 	if (strlen(dbdir) == 0)
 		return 0;
-	if (XBrowseForFolder(NULL, "Please select image testset folder", NULL, testdir, sizeof(testdir)) == FALSE)
+	if (XBrowseForFolder(NULL, "Please select image testset batch folder, could be any folder", NULL, testdir, sizeof(testdir)) == FALSE)
 		return 0;
 	if (strlen(testdir) == 0)
@@ -126,12 +137,18 @@
 		cout << endl;
 		cout << "1. calculate descriptors" << endl;
-		cout << "2. categorize descriptors (aka averages histograms)" << endl;
-		cout << "3. determine winter sport" << endl;
+		cout << "2. categorize descriptors (aka averages)" << endl;
+		cout << "3. determine winter sport on single image" << endl;
 		cout << "4. batch test winter sport" << endl;
+		cout << "9. Show averages" << endl;
+		cout << endl;
+		cout_status("d. Turn debug mode", opt_debug);
 		cout_status("v. Turn verbose mode", opt_verbose);
 		cout_status("h. Turn histogram classifier", opt_histogram);
-		cout_status("b. Turn block classifier", opt_block);
-
+		cout_status("b. Turn block classifier", opt_block);	
 		cout << endl;
+		cout << "==== Parameters Block Classifier ====" << endl;
+		cout << "s. Set spread [currently: " << opt_spread << "]" << endl;
+		cout << "t. Set Tolerance [currently: " << opt_tolerance << "]" << endl;
+
 		cout << "Please select option, or type 'q' to quit: ";
 		char c = _getch();
@@ -171,4 +188,15 @@
 			}
 			break;
+		case '9':
+			if (!LoadAverages(dbdir)){
+				_return("Unable to load averages",1);
+			}
+			if (!ShowAverages(testdir)) {
+				_return("Unable to show averages",1);
+			}
+			break;
+		case 'd':
+			opt_debug = (!opt_debug);
+			break;
 		case 'v':
 			opt_verbose = (!opt_verbose);
@@ -180,4 +208,17 @@
 			opt_histogram = (!opt_histogram);
 			break;
+		case 's':
+			cout << "Spread value: ";
+			cin >> opt_spread;
+			cout << "WARNING: Make sure to re-classify before (Batch)Testing" << endl;
+			cout << "Any key to continue..." << endl; cin.get();
+			break;
+		case 't':
+			cout << "Tolerance value: ";
+			cin >> opt_tolerance;
+			cout << "WARNING: Make sure to re-classify before (Batch)Testing" << endl;
+			cout << "Any key to continue..." << endl; cin.get();
+			break;
+
 		default:
 			continue;
@@ -228,4 +269,10 @@
 }
 
+struct point {
+	int x;
+	int y;
+};
+
+
 // histogram should be a preallocated array of size BIN_COUNT*BIN_COUNT*BIN_COUNT elements and will
 // be filled with the color histogram of the image where path points at
@@ -236,31 +283,117 @@
 	if (!image.IsValid())
 		return false;
+
 	// clear histogram
-	memset(block, 0, MAX_SIZE/SPREAD * sizeof(float));
+	memset(block, 0, MAX_BLOCKSIZE * sizeof(float));
+	
 	// walk through the pixels to fill the histogram
 	const int width = (int)image.GetWidth();
 	const int height = (int)image.GetHeight();
 
-	int rgb_value = 0;
-	int rgb_prev = 0;
-
+	int * status = new int [width * height];
+	//memset(status,0, width * height * sizeof(int));
+
+	queue<point> queue;
+	int rgb_current, rgb_right, rgb_up = 0;
 	int block_size = 0;
-
-	for (int y = 0; y < height; y += SPREAD)
-	{
-		for (int x = 0; x < width; x += SPREAD)
+	point p, p_right, p_up;
+
+	RGBQUAD rgb;
+
+	// Pixel field status
+	// 0 = No need for processing
+	// 1 = Need processing
+	// 2 = Done processing
+	for (int x = 0; x < width; x += opt_spread)
+		for (int y = 0; y < height; y += opt_spread)
+			status[x * height + y] = 1;
+
+	for (int x = 0; x < width; x += opt_spread)
+	{
+		for (int y = 0; y < height; y += opt_spread)
 		{
-			// Note: CxImage library starts counting at lower-left corner of the image,
-			//       which is seen as the top of the image.
-			RGBQUAD rgb = image.BlindGetPixelColor(x, y, false);
-			rgb_value = (rgb.rgbRed + rgb.rgbBlue + rgb.rgbGreen);
-
-			if (abs(rgb_value - rgb_prev) > TOLERANCE) {
-				block[block_size]++;
-				block_size = 1;
-			}
-			rgb_prev = rgb_value;
-		}
-	}
+			// Only process if required to and not yet done
+			if (status[x * height + y] != 1) {
+				if (opt_debug)
+					cout << "Already processed point " << cout_point(x,y) << endl;
+				continue;
+			}
+
+			if(opt_debug)
+				cout << "Testing new point " << cout_point(x,y) << endl;
+
+			// Stack based expantion and evaluation
+			block_size = 0;
+			p.x = x;
+			p.y = y;
+			queue.push(p);
+
+			while (!queue.empty()) 
+			{
+				// Note: CxImage library starts counting at lower-left corner of the image
+				// which is seen as the top of the image.
+				p = queue.front();
+				queue.pop();
+
+				// Make sure not to process points twice
+				if (status[p.x * height + p.y] != 1)
+					continue;
+
+				if (opt_debug)
+					cout << "Processing point " << cout_point(p.x,p.y) << endl;
+
+				rgb = image.BlindGetPixelColor(p.x, p.y, false);
+				rgb_current = (rgb.rgbRed + rgb.rgbBlue + rgb.rgbGreen);
+
+				p_right.x = p.x + opt_spread;
+				p_right.y = p.y;
+
+				// Check if right one fits the bill
+				if (p_right.x < width)
+				{
+					rgb = image.BlindGetPixelColor(p_right.x, p_right.y, false);
+					rgb_right = (rgb.rgbRed + rgb.rgbBlue + rgb.rgbGreen);
+
+					if (abs(rgb_right - rgb_current) < opt_tolerance) 
+					{
+						if (opt_debug)
+							cout << "Right point " << cout_point(p_right.x,p_right.y) << " gets included in block" << endl;
+
+						block_size++;
+						queue.push(p_right);
+					}
+				}
+
+				// Check if up one fits the bill
+				p_up.x = p.x;
+				p_up.y = p.y + opt_spread;
+				if (p_up.y < height) 
+				{
+					rgb = image.BlindGetPixelColor(p_up.x, p_up.y, false);
+					rgb_up = (rgb.rgbRed + rgb.rgbBlue + rgb.rgbGreen);
+
+					if (abs(rgb_up - rgb_current) < opt_tolerance) 
+					{
+						if (opt_debug)
+							cout << "Upper point " << cout_point(p_up.x, p_up.y) << " gets included in block" << endl;
+
+						block_size++;
+						queue.push(p_up);
+					}
+				}
+				status[p.x * height + p.y] = 2;
+			}
+			if (opt_debug)
+				cout << cout_point(x,y) << " blocksize " << block_size << endl;
+			if (block_size > MAX_BLOCKSIZE) {
+				cout << "WARN: block_size larger than fixed limit of " << MAX_BLOCKSIZE << endl;
+				block_size = MAX_BLOCKSIZE;
+			}
+
+			block[block_size]++;
+		}
+	}
+
+	delete status;
 	return true;
 }
@@ -270,5 +403,5 @@
 	// the histogram that we reuse for each image
 	float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
-	float *block = new float[MAX_SIZE/SPREAD];
+	float *block = new float[MAX_BLOCKSIZE];
 	// walk through all images
 	// Note: each of the three categories has 50 images
@@ -304,5 +437,5 @@
 			if (fwrite(histogram, sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
 				goto failure;
-			if (fwrite(block, sizeof(float), MAX_SIZE/SPREAD, file) != MAX_SIZE/SPREAD)
+			if (fwrite(block, sizeof(float), MAX_BLOCKSIZE, file) != MAX_BLOCKSIZE)
 				goto failure;
 			SAFE_CLOSEFILE(file);
@@ -328,6 +461,6 @@
 	// characteristics of that category
 	float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
-	float *block = new float[MAX_SIZE/SPREAD];
-	float *average_block = new float[MAX_SIZE/SPREAD];
+	float *block = new float[MAX_BLOCKSIZE];
+	float *average_block = new float[MAX_BLOCKSIZE];
 	float *average = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
 	// walk through all descriptors
@@ -362,5 +495,5 @@
 				goto failure;
 			}
-			if (fread(block, sizeof(float), MAX_SIZE/SPREAD, file) != MAX_SIZE/SPREAD) {
+			if (fread(block, sizeof(float), MAX_BLOCKSIZE, file) != MAX_BLOCKSIZE) {
 				p_err("Cannot read block");
 				goto failure;
@@ -372,5 +505,5 @@
 				average[b] += histogram[b];
 
-			for (int b = 0; b < MAX_SIZE/SPREAD; b++)
+			for (int b = 0; b < MAX_BLOCKSIZE; b++)
 				average_block[b] += block[b];
 
@@ -380,6 +513,6 @@
 		for (int b = 0; b < BIN_COUNT*BIN_COUNT*BIN_COUNT; b++)
 			average[b] /= c_size;
-		
-		for (int b = 0; b < MAX_SIZE/SPREAD; b++)
+
+		for (int b = 0; b < MAX_BLOCKSIZE; b++)
 			average_block[b] /= c_size;
 
@@ -390,5 +523,5 @@
 		if (fwrite(average, sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
 			goto failure;
-		if (fwrite(average_block, sizeof(float), MAX_SIZE/SPREAD, file) != MAX_SIZE/SPREAD)
+		if (fwrite(average_block, sizeof(float), MAX_BLOCKSIZE, file) != MAX_BLOCKSIZE)
 			goto failure;
 
@@ -428,5 +561,5 @@
 		if (fread(average[c], sizeof(float), BIN_COUNT*BIN_COUNT*BIN_COUNT, file) != BIN_COUNT*BIN_COUNT*BIN_COUNT)
 			return false;
-		if (fread(average_block[c], sizeof(float), MAX_SIZE/SPREAD, file) != MAX_SIZE/SPREAD)
+		if (fread(average_block[c], sizeof(float), MAX_BLOCKSIZE, file) != MAX_BLOCKSIZE)
 			return false;
 
@@ -436,7 +569,35 @@
 }
 
+
+bool ShowAverages(const char *basedir) {
+
+	if (opt_histogram) {
+		cout << "Histogram averages" << endl;
+		for (int c = 0; c < CATEGORY_SIZE; c++) {
+			cout << "Category [" << categories[c] << "] " << endl;
+			for (int i = 1; i <= BIN_COUNT*BIN_COUNT*BIN_COUNT; i++) {
+				cout << fixed << setprecision(2) << average[c][i-1] << " ";
+				if ((i % 10) == 0) cout << endl;
+			}
+			cout << endl;
+		}
+	}
+	if (opt_block) {
+		cout << "Block averages" << endl;
+		for (int c = 0; c < CATEGORY_SIZE; c++) {
+			cout << "Category [" << categories[c] << "] " << endl;
+			for (int i = 1; i < MAX_BLOCKSIZE; i++) {
+				cout << fixed << setprecision(2) << average_block[c][i-1] << " ";
+				if ((i % 10) == 0) cout << endl;
+			}
+			cout << endl;
+
+		}
+	}
+	return true;
+}
 int DetermineCategory(const char *path, const int guess=-1, const bool verbose=false) {
 	float *histogram = new float[BIN_COUNT*BIN_COUNT*BIN_COUNT];
-	float *block = new float[MAX_SIZE/SPREAD];
+	float *block = new float[MAX_BLOCKSIZE];
 	float cat2dist[CATEGORY_SIZE];
 	float cat2block[CATEGORY_SIZE];
@@ -462,5 +623,6 @@
 			cat2dist[c] += fabs(histogram[b] - average[c][b]);
 
-		for (int b = 0; b < MAX_SIZE/SPREAD; b++)
+		/* No including of the non-matching points currently */
+		for (int b = 1; b < MAX_BLOCKSIZE; b++)
 			cat2block[c] += fabs(block[b] - average_block[c][b]);
 	}
@@ -498,5 +660,5 @@
 		return cat_block;
 	} else {
-	    return -1;
+		return -1;
 	}
 }
@@ -560,5 +722,5 @@
 		catname = categories[c];
 		sprintf(catdir, "%s\\%s\\", basedir, catname);
-		
+
 		/* process the images in the directory */
 		for (int i = 1; i <= 50; i++)
@@ -581,5 +743,5 @@
 		cout << "[" << catname << "] results " << c_succes[c] << "/" << c_total[c] << endl;
 	}
-	
+
 	/* Display grand total */
 	cout << "=== Totals ===" << endl;
@@ -598,5 +760,5 @@
 		all_succes += c_succes[c];
 	}
-	printf ("[%-20s] %i/%i\n","total",all_succes,all_succes);
+	printf ("[%-20s] %i/%i\n","total",all_succes,all_total);
 	cout << "Press any key to continue..."; cin.get();
 
