Commit 7a67ea01 authored by Taddeus Kroes's avatar Taddeus Kroes

Implemented 5x5 neighbourhood patterns.

parent 2e1b2e8c
*.dat *.dat
results.txt results*.txt
...@@ -7,11 +7,12 @@ class Character: ...@@ -7,11 +7,12 @@ class Character:
self.image = image self.image = image
self.filename = filename self.filename = filename
def get_single_cell_feature_vector(self): def get_single_cell_feature_vector(self, neighbours=5):
if hasattr(self, 'feature'): if hasattr(self, 'feature'):
return 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): def get_feature_vector(self, cell_size=None):
pattern = LBP(self.image) if cell_size == None \ pattern = LBP(self.image) if cell_size == None \
......
...@@ -3,8 +3,8 @@ from svmutil import svm_train, svm_problem, svm_parameter, svm_predict, \ ...@@ -3,8 +3,8 @@ from svmutil import svm_train, svm_problem, svm_parameter, svm_predict, \
class Classifier: class Classifier:
def __init__(self, c=None, gamma=None, filename=None, cell_size=12): def __init__(self, c=None, gamma=None, filename=None, neighbours=3):
self.cell_size = cell_size self.neighbours = neighbours
if filename: if filename:
# If a filename is given, load a model from the given filename # If a filename is given, load a model from the given filename
...@@ -34,7 +34,7 @@ class Classifier: ...@@ -34,7 +34,7 @@ class Classifier:
% (char.value, i + 1, l, int(100 * (i + 1) / l)) % (char.value, i + 1, l, int(100 * (i + 1) / l))
classes.append(float(ord(char.value))) classes.append(float(ord(char.value)))
#features.append(char.get_feature_vector()) #features.append(char.get_feature_vector())
char.get_single_cell_feature_vector() char.get_single_cell_feature_vector(self.neighbours)
features.append(char.feature) features.append(char.feature)
problem = svm_problem(classes, features) problem = svm_problem(classes, features)
...@@ -56,7 +56,7 @@ class Classifier: ...@@ -56,7 +56,7 @@ class Classifier:
"""Classify a character object, return its value.""" """Classify a character object, return its value."""
true_value = 0 if true_value == None else ord(true_value) true_value = 0 if true_value == None else ord(true_value)
#x = character.get_feature_vector(self.cell_size) #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) p = svm_predict([true_value], [character.feature], self.model)
prediction_class = int(p[0][0]) prediction_class = int(p[0][0])
......
...@@ -3,9 +3,13 @@ from math import ceil ...@@ -3,9 +3,13 @@ from math import ceil
class LocalBinaryPatternizer: class LocalBinaryPatternizer:
def __init__(self, image, cell_size=16): def __init__(self, image, cell_size=16, neighbours=3):
self.cell_size = cell_size self.cell_size = cell_size
self.image = image self.image = image
self.pattern_callback, self.bins = {
3: (self.pattern_3x3, 256),
5: (self.pattern_5x5, 4096)
}[neighbours]
def setup_histograms(self): def setup_histograms(self):
cells_in_width = int(ceil(self.image.width / float(self.cell_size))) cells_in_width = int(ceil(self.image.width / float(self.cell_size)))
...@@ -16,9 +20,9 @@ class LocalBinaryPatternizer: ...@@ -16,9 +20,9 @@ class LocalBinaryPatternizer:
self.histograms.append([]) self.histograms.append([])
for j in xrange(cells_in_width): 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) \ 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 , value) << 6) \
| (self.is_pixel_darker(y - 1, x + 1, value) << 5) \ | (self.is_pixel_darker(y - 1, x + 1, value) << 5) \
...@@ -26,7 +30,21 @@ class LocalBinaryPatternizer: ...@@ -26,7 +30,21 @@ class LocalBinaryPatternizer:
| (self.is_pixel_darker(y + 1, x + 1, value) << 3) \ | (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 , value) << 2) \
| (self.is_pixel_darker(y + 1, x - 1, value) << 1) \ | (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): def create_features_vector(self):
'''Walk around the pixels in clokwise order, shifting 1 bit less at '''Walk around the pixels in clokwise order, shifting 1 bit less at
...@@ -36,7 +54,7 @@ class LocalBinaryPatternizer: ...@@ -36,7 +54,7 @@ class LocalBinaryPatternizer:
for y, x, value in self.image: for y, x, value in self.image:
cy, cx = self.get_cell_index(y, x) 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() return self.get_features_as_array()
...@@ -55,15 +73,14 @@ class LocalBinaryPatternizer: ...@@ -55,15 +73,14 @@ class LocalBinaryPatternizer:
f.extend(hist.bins) f.extend(hist.bins)
return f return f
#return [h.bins for h in [h for sub in self.histograms for h in sub]][0]
def get_single_histogram(self): def get_single_histogram(self):
"""Create a single histogram of the local binary patterns in the """Create a single histogram of the local binary patterns in the
image.""" image."""
h = Histogram(256, 0, 256) h = Histogram(self.bins, 0, self.bins)
for y, x, value in self.image: 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() h.normalize()
......
...@@ -5,19 +5,22 @@ from GaussianFilter import GaussianFilter ...@@ -5,19 +5,22 @@ from GaussianFilter import GaussianFilter
class NormalizedCharacterImage(GrayscaleImage): class NormalizedCharacterImage(GrayscaleImage):
def __init__(self, image=None, data=None, size=(60, 40), blur=1.1, \ def __init__(self, image=None, data=None, height=None, blur=1.1):
crop_threshold=0.9):
if image != None: if image != None:
GrayscaleImage.__init__(self, data=deepcopy(image.data)) GrayscaleImage.__init__(self, data=deepcopy(image.data))
elif data != None: elif data != None:
GrayscaleImage.__init__(self, data=deepcopy(data)) GrayscaleImage.__init__(self, data=deepcopy(data))
self.blur = blur self.blur = blur
self.crop_threshold = crop_threshold
self.size = size
self.gaussian_filter() self.gaussian_filter()
self.increase_contrast() self.increase_contrast()
#self.crop_threshold = crop_threshold
#self.crop_to_letter() #self.crop_to_letter()
#self.resize()
self.height = height
self.resize()
def increase_contrast(self): def increase_contrast(self):
self.data -= self.data.min() self.data -= self.data.min()
...@@ -31,4 +34,9 @@ class NormalizedCharacterImage(GrayscaleImage): ...@@ -31,4 +34,9 @@ class NormalizedCharacterImage(GrayscaleImage):
cropper.crop_to_letter(self) cropper.crop_to_letter(self)
def resize(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))
#!/usr/bin/python #!/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 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)] C = [float(2 ** p) for p in xrange(-5, 16, 2)]
Y = [float(2 ** p) for p in xrange(-15, 4, 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 = [] results = []
best = (0,) best = (0,)
i = 0 i = 0
for c in C: for c in C:
for y in Y: for y in Y:
classifier = Classifier(c=c, gamma=y) classifier = Classifier(c=c, gamma=y, neighbours=neighbours)
classifier.train(learning_set) classifier.train(learning_set)
result = classifier.test(test_set) result = classifier.test(test_set)
...@@ -33,22 +99,30 @@ for c in C: ...@@ -33,22 +99,30 @@ for c in C:
% (i, len(C) * len(Y), c, y, int(round(result * 100))) % (i, len(C) * len(Y), c, y, int(round(result * 100)))
i = 0 i = 0
s = ' c\y'
print '\n c\y',
for y in Y: for y in Y:
print '| %f' % y, s += '| %f' % y
print s += '\n'
for c in C: for c in C:
print ' %7s' % c, s += ' %7s' % c
for y in Y: for y in Y:
print '| %8d' % int(round(results[i] * 100)), s += '| %8d' % int(round(results[i] * 100))
i += 1 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment