diff --git a/src/.gitignore b/src/.gitignore
index be1fd40c7ab8827aa2bf4a75bbf0864e7c1a3c63..b70230f22109bdb365a8d20816278019fc489f84 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,2 +1,2 @@
 *.dat
-results.txt
+results*.txt
diff --git a/src/Character.py b/src/Character.py
index ec49b65b930d550180b3050b5c46ddf802b4839d..110ef119157eaf1aee90b5c2ac7096351196bddb 100644
--- a/src/Character.py
+++ b/src/Character.py
@@ -7,11 +7,12 @@ class Character:
         self.image   = image
         self.filename = filename
 
-    def get_single_cell_feature_vector(self):
+    def get_single_cell_feature_vector(self, neighbours=5):
         if hasattr(self, 'feature'):
             return
 
-        self.feature = LBP(self.image).single_cell_features_vector()
+        pattern = LBP(self.image, neighbours=neighbours)
+        self.feature = pattern.single_cell_features_vector()
 
     def get_feature_vector(self, cell_size=None):
         pattern = LBP(self.image) if cell_size == None \
diff --git a/src/Classifier.py b/src/Classifier.py
index a9a58a700925199400142ad7e354dda9695a17f2..02c155c2ddee7fd49ba115ba465d1d0be02cba6e 100644
--- a/src/Classifier.py
+++ b/src/Classifier.py
@@ -3,8 +3,8 @@ from svmutil import svm_train, svm_problem, svm_parameter, svm_predict, \
 
 
 class Classifier:
-    def __init__(self, c=None, gamma=None, filename=None, cell_size=12):
-        self.cell_size = cell_size
+    def __init__(self, c=None, gamma=None, filename=None, neighbours=3):
+        self.neighbours = neighbours
 
         if filename:
             # If a filename is given, load a model from the given filename
@@ -34,7 +34,7 @@ class Classifier:
                   % (char.value, i + 1, l, int(100 * (i + 1) / l))
             classes.append(float(ord(char.value)))
             #features.append(char.get_feature_vector())
-            char.get_single_cell_feature_vector()
+            char.get_single_cell_feature_vector(self.neighbours)
             features.append(char.feature)
 
         problem = svm_problem(classes, features)
@@ -56,7 +56,7 @@ class Classifier:
         """Classify a character object, return its value."""
         true_value = 0 if true_value == None else ord(true_value)
         #x = character.get_feature_vector(self.cell_size)
-        character.get_single_cell_feature_vector()
+        character.get_single_cell_feature_vector(self.neighbours)
         p = svm_predict([true_value], [character.feature], self.model)
         prediction_class = int(p[0][0])
 
diff --git a/src/LocalBinaryPatternizer.py b/src/LocalBinaryPatternizer.py
index 468f0cb653bd11ade71949737c043ba7ced0aa44..b1f42101da95f458453e3c4cf4a1840cad3c7d54 100644
--- a/src/LocalBinaryPatternizer.py
+++ b/src/LocalBinaryPatternizer.py
@@ -3,9 +3,13 @@ from math import ceil
 
 class LocalBinaryPatternizer:
 
-    def __init__(self, image, cell_size=16):
+    def __init__(self, image, cell_size=16, neighbours=3):
         self.cell_size = cell_size
         self.image = image
+        self.pattern_callback, self.bins = {
+                    3: (self.pattern_3x3, 256),
+                    5: (self.pattern_5x5, 4096)
+                }[neighbours]
 
     def setup_histograms(self):
         cells_in_width = int(ceil(self.image.width / float(self.cell_size)))
@@ -16,9 +20,9 @@ class LocalBinaryPatternizer:
             self.histograms.append([])
 
             for j in xrange(cells_in_width):
-                self.histograms[i].append(Histogram(256, 0, 256))
+                self.histograms[i].append(Histogram(self.bins, 0, self.bins))
 
-    def local_binary_pattern(self, y, x, value):
+    def pattern_3x3(self, y, x, value):
         return (self.is_pixel_darker(y - 1, x - 1, value) << 7) \
              | (self.is_pixel_darker(y - 1, x    , value) << 6) \
              | (self.is_pixel_darker(y - 1, x + 1, value) << 5) \
@@ -26,7 +30,21 @@ class LocalBinaryPatternizer:
              | (self.is_pixel_darker(y + 1, x + 1, value) << 3) \
              | (self.is_pixel_darker(y + 1, x    , value) << 2) \
              | (self.is_pixel_darker(y + 1, x - 1, value) << 1) \
-             | (self.is_pixel_darker(y    , x - 1, value) << 0)
+             | (self.is_pixel_darker(y    , x - 1, value))
+
+    def pattern_5x5(self, y, x, value):
+        return (self.is_pixel_darker(y - 1, x - 2, value) << 11) \
+             | (self.is_pixel_darker(y    , x - 2, value) << 10) \
+             | (self.is_pixel_darker(y + 1, x - 2, value) << 9) \
+             | (self.is_pixel_darker(y + 2, x - 1, value) << 8) \
+             | (self.is_pixel_darker(y + 2, x    , value) << 7) \
+             | (self.is_pixel_darker(y + 2, x + 1, value) << 6) \
+             | (self.is_pixel_darker(y + 1, x + 2, value) << 5) \
+             | (self.is_pixel_darker(y    , x + 2, value) << 4) \
+             | (self.is_pixel_darker(y - 1, x + 2, value) << 3) \
+             | (self.is_pixel_darker(y - 2, x + 1, value) << 2) \
+             | (self.is_pixel_darker(y - 2, x    , value) << 1) \
+             | (self.is_pixel_darker(y - 2, x - 1, value))
 
     def create_features_vector(self):
         '''Walk around the pixels in clokwise order, shifting 1 bit less at
@@ -36,7 +54,7 @@ class LocalBinaryPatternizer:
 
         for y, x, value in self.image:
             cy, cx = self.get_cell_index(y, x)
-            self.histograms[cy][cx].add(self.local_binary_pattern(y, x, value))
+            self.histograms[cy][cx].add(self.pattern_callback(y, x, value))
 
         return self.get_features_as_array()
 
@@ -55,15 +73,14 @@ class LocalBinaryPatternizer:
                 f.extend(hist.bins)
 
         return f
-        #return [h.bins for h in [h for sub in self.histograms for h in sub]][0]
 
     def get_single_histogram(self):
         """Create a single histogram of the local binary patterns in the
         image."""
-        h = Histogram(256, 0, 256)
+        h = Histogram(self.bins, 0, self.bins)
 
         for y, x, value in self.image:
-            h.add(self.local_binary_pattern(y, x, value))
+            h.add(self.pattern_callback(y, x, value))
 
         h.normalize()
 
diff --git a/src/NormalizedCharacterImage.py b/src/NormalizedCharacterImage.py
index e682c1ca39934614ead67f4d45923c87d55767df..52b34b99418031fd474ee94ed98a2e2310a94ed0 100644
--- a/src/NormalizedCharacterImage.py
+++ b/src/NormalizedCharacterImage.py
@@ -5,19 +5,22 @@ from GaussianFilter import GaussianFilter
 
 class NormalizedCharacterImage(GrayscaleImage):
 
-    def __init__(self, image=None, data=None, size=(60, 40), blur=1.1, \
-            crop_threshold=0.9):
+    def __init__(self, image=None, data=None, height=None, blur=1.1):
         if image != None:
             GrayscaleImage.__init__(self, data=deepcopy(image.data))
         elif data != None:
             GrayscaleImage.__init__(self, data=deepcopy(data))
+
         self.blur = blur
-        self.crop_threshold = crop_threshold
-        self.size = size
         self.gaussian_filter()
+
         self.increase_contrast()
+
+        #self.crop_threshold = crop_threshold
         #self.crop_to_letter()
-        #self.resize()
+
+        self.height = height
+        self.resize()
 
     def increase_contrast(self):
         self.data -= self.data.min()
@@ -31,4 +34,9 @@ class NormalizedCharacterImage(GrayscaleImage):
         cropper.crop_to_letter(self)
 
     def resize(self):
-        GrayscaleImage.resize(self, self.size)
+        """Resize the image to a fixed height."""
+        if self.height == None:
+            return
+
+        h, w = self.data.shape
+        GrayscaleImage.resize(self, (self.height, self.height * w / h))
diff --git a/src/find_svm_params.py b/src/find_svm_params.py
index 6eb1d9e7e4380758b07458649be4fc822ce7fec0..f2defe5fc48779e674b33e7068bbed9192f61dbe 100755
--- a/src/find_svm_params.py
+++ b/src/find_svm_params.py
@@ -1,26 +1,92 @@
 #!/usr/bin/python
-from cPickle import load
+from os import listdir
+from os.path import exists
+from cPickle import load, dump
+from sys import argv, exit
+
+from GrayscaleImage import GrayscaleImage
+from NormalizedCharacterImage import NormalizedCharacterImage
+from Character import Character
 from Classifier import Classifier
 
+if len(argv) < 3:
+    print 'Usage: python %s NEIGHBOURS BLUR_SCALE' % argv[0]
+    exit(1)
+
+neighbours = int(argv[1])
+blur_scale = float(argv[2])
+suffix = '_%s_%s' % (blur_scale, neighbours)
+
+chars_file = 'characters%s.dat' % suffix
+learning_set_file = 'learning_set%s.dat' % suffix
+test_set_file = 'test_set%s.dat' % suffix
+classifier_file = 'classifier%s.dat' % suffix
+results_file = 'results%s.txt' % suffix
+
+
+# Load characters
+if exists(chars_file):
+    print 'Loading characters...'
+    chars = load(open(chars_file, 'r'))
+else:
+    print 'Going to generate character objects...'
+    chars = []
+
+    for char in sorted(listdir('../images/LearningSet')):
+        for image in sorted(listdir('../images/LearningSet/' + char)):
+            f = '../images/LearningSet/' + char + '/' + image
+            image = GrayscaleImage(f)
+            norm = NormalizedCharacterImage(image, blur=blur_scale, height=42)
+            #imshow(norm.data, cmap='gray'); show()
+            character = Character(char, [], norm)
+            character.get_single_cell_feature_vector(neighbours)
+            chars.append(character)
+            print char
+
+    print 'Saving characters...'
+    dump(chars, open(chars_file, 'w+'))
+
+
+# Load learning set and test set
+if exists(learning_set_file):
+    print 'Loading learning set...'
+    learning_set = load(open(learning_set_file, 'r'))
+    print 'Learning set:', [c.value for c in learning_set]
+    print 'Loading test set...'
+    test_set = load(open(test_set_file, 'r'))
+    print 'Test set:', [c.value for c in test_set]
+else:
+    print 'Going to generate learning set and test set...'
+    learning_set = []
+    test_set = []
+    learned = []
+
+    for char in chars:
+        if learned.count(char.value) == 70:
+            test_set.append(char)
+        else:
+            learning_set.append(char)
+            learned.append(char.value)
+
+    print 'Learning set:', [c.value for c in learning_set]
+    print '\nTest set:', [c.value for c in test_set]
+    print '\nSaving learning set...'
+    dump(learning_set, file(learning_set_file, 'w+'))
+    print 'Saving test set...'
+    dump(test_set, file(test_set_file, 'w+'))
+
+
+# Perform a grid-search to find the optimal values for C and gamma
 C = [float(2 ** p) for p in xrange(-5, 16, 2)]
 Y = [float(2 ** p) for p in xrange(-15, 4, 2)]
-best_classifier = None
-
-print 'Loading learning set...'
-learning_set = load(file('learning_set.dat', 'r'))
-print 'Learning set:', [c.value for c in learning_set]
-print 'Loading test set...'
-test_set = load(file('test_set.dat', 'r'))
-print 'Test set:', [c.value for c in test_set]
 
-# Perform a grid-search on different combinations of soft margin and gamma
 results = []
 best = (0,)
 i = 0
 
 for c in C:
     for y in Y:
-        classifier = Classifier(c=c, gamma=y)
+        classifier = Classifier(c=c, gamma=y, neighbours=neighbours)
         classifier.train(learning_set)
         result = classifier.test(test_set)
 
@@ -33,22 +99,30 @@ for c in C:
               % (i, len(C) * len(Y), c, y, int(round(result * 100)))
 
 i = 0
+s = '     c\y'
 
-print '\n     c\y',
 for y in Y:
-    print '| %f' % y,
+    s += '| %f' % y
 
-print
+s += '\n'
 
 for c in C:
-    print ' %7s' % c,
+    s += ' %7s' % c
 
     for y in Y:
-        print '| %8d' % int(round(results[i] * 100)),
+        s +=  '| %8d' % int(round(results[i] * 100))
         i += 1
 
-    print
+    s += '\n'
+
+s += '\nBest result: %.3f%% for C = %f and gamma = %f' % best[:3]
+
+print 'Saving results...'
+f = open(results_file, 'w+')
+f.write(s)
+f.close()
 
-print '\nBest result: %.3f%% for C = %f and gamma = %f' % best[:3]
+print 'Saving best classifier...'
+best[3].save(classifier_file)
 
-best[3].save('classifier.dat')
+print '\n' + s