Ver código fonte

Merge branch 'master' of github.com:taddeus/licenseplates

Richard Torenvliet 14 anos atrás
pai
commit
f957089b14

+ 1 - 0
.gitignore

@@ -15,3 +15,4 @@ images/BBB
 images/Images
 images/Infos
 images/licenseplates
+images/faulty

+ 8 - 3
src/Classifier.py

@@ -3,7 +3,8 @@ from svmutil import svm_train, svm_problem, svm_parameter, svm_predict, \
 
 
 class Classifier:
-    def __init__(self, c=None, gamma=None, filename=None, neighbours=3):
+    def __init__(self, c=None, gamma=None, filename=None, neighbours=3, \
+            verbose=0):
         self.neighbours = neighbours
 
         if filename:
@@ -18,6 +19,8 @@ class Classifier:
             self.param.gamma = gamma  # Parameter for radial kernel
             self.model = None
 
+        self.verbose = verbose
+
     def save(self, filename):
         """Save the SVM model in the given filename."""
         svm_save_model(filename, self.model)
@@ -30,8 +33,9 @@ class Classifier:
         l = len(learning_set)
 
         for i, char in enumerate(learning_set):
-            print 'Found "%s"  --  %d of %d (%d%% done)' \
-                  % (char.value, i + 1, l, int(100 * (i + 1) / l))
+            if self.verbose:
+                print 'Found "%s"  --  %d of %d (%d%% done)' \
+                    % (char.value, i + 1, l, round(100 * (i + 1) / l))
             classes.append(float(ord(char.value)))
             #features.append(char.get_feature_vector())
             char.get_single_cell_feature_vector(self.neighbours)
@@ -57,6 +61,7 @@ class Classifier:
         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(self.neighbours)
+        #p = svm_predict([true_value], [character.feature], self.model, '-b 1')
         p = svm_predict([true_value], [character.feature], self.model)
         prediction_class = int(p[0][0])
 

+ 6 - 1
src/GaussianFilter.py

@@ -2,8 +2,10 @@ from GrayscaleImage import GrayscaleImage
 from scipy.ndimage import gaussian_filter
 
 class GaussianFilter:
+    """This class can apply a Gaussian blur on an image."""
 
     def __init__(self, scale):
+        """Create a GaussianFilter object with a given scale."""
         self.scale = scale
 
     def get_filtered_copy(self, image):
@@ -12,12 +14,15 @@ class GaussianFilter:
         return GrayscaleImage(None, image)
 
     def filter(self, image):
+        """Apply a Gaussian blur on the image data."""
         image.data = gaussian_filter(image.data, self.scale)
 
     def get_scale(self):
-      return self.scale
+        """Return the scale of the Gaussian kernel."""
+        return self.scale
 
     def set_scale(self, scale):
+        """Set the scale of the Gaussian kernel."""
         self.scale = float(scale)
 
     scale = property(get_scale, set_scale)

+ 1 - 1
src/LocalBinaryPatternizer.py

@@ -57,7 +57,7 @@ class LocalBinaryPatternizer:
              | (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
+        '''Walk around the pixels in clockwise order, shifting 1 bit less at
         each neighbour starting at 7 in the top-left corner. This gives a 8-bit
         feature number of a pixel'''
         self.setup_histograms()

+ 1 - 1
src/find_svm_params.py

@@ -86,7 +86,7 @@ i = 0
 
 for c in C:
     for y in Y:
-        classifier = Classifier(c=c, gamma=y, neighbours=neighbours)
+        classifier = Classifier(c=c, gamma=y, neighbours=neighbours, verbose=1)
         classifier.train(learning_set)
         result = classifier.test(test_set)
 

+ 81 - 0
src/run_classifier.py

@@ -0,0 +1,81 @@
+#!/usr/bin/python
+from cPickle import load
+from sys import argv, exit
+from pylab import imsave, plot, subplot, imshow, show, axis, title
+from math import sqrt, ceil
+import os
+
+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)
+
+test_set_file = 'test_set%s.dat' % suffix
+classifier_file = 'classifier%s.dat' % suffix
+
+print 'Loading classifier...'
+classifier = Classifier(filename=classifier_file)
+classifier.neighbours = neighbours
+
+print 'Loading test set...'
+test_set = load(file(test_set_file, 'r'))
+l = len(test_set)
+matches = 0
+#classified = {}
+classified = []
+
+for i, char in enumerate(test_set):
+    prediction = classifier.classify(char, char.value)
+
+    if char.value != prediction:
+        classified.append((char, prediction))
+
+        #key = '%s_as_%s' % (char.value, prediction)
+
+        #if key not in classified:
+        #    classified[key] = [char]
+        #else:
+        #    classified[key].append(char)
+
+        print '"%s" was classified as "%s"' \
+                % (char.value, prediction)
+    else:
+        matches += 1
+
+    print '%d of %d (%d%% done)' % (i + 1, l, round(100 * (i + 1) / l))
+
+print '\n%d matches (%d%%), %d fails' % (matches, \
+        round(100 * matches / l), \
+        len(test_set) - matches)
+
+# Show a grid plot of all faulty classified characters
+print 'Plotting faulty classified characters...'
+rows = int(ceil(sqrt(l - matches)))
+columns = int(ceil((l - matches) / float(rows)))
+
+for i, pair in enumerate(classified):
+    char, prediction = pair
+    subplot(rows, columns, i + 1)
+    title('%s as %s' % (char.value, prediction))
+    imshow(char.image.data, cmap='gray')
+    axis('off')
+
+show()
+
+#print 'Saving faulty classified characters...'
+#folder = '../images/faulty/'
+#
+#if not os.path.exists(folder):
+#    os.mkdir(folder)
+#
+#for filename, chars in classified.iteritems():
+#    if len(chars) == 1:
+#        imsave('%s%s' % (folder, filename), char.image.data, cmap='gray')
+#    else:
+#        for i, char in enumerate(chars):
+#            imsave('%s%s_%d' % (folder, filename, i), char.image.data, cmap='gray')

+ 0 - 38
src/test_classifier.py

@@ -1,38 +0,0 @@
-#!/usr/bin/python
-from cPickle import dump, load
-
-from Classifier import Classifier
-
-if len(argv) < 5:
-    print 'Usage: python %s FILE_SUFFIX C GAMMA NEIGHBOURS' % argv[0]
-    exit(1)
-
-print 'Loading learning set'
-learning_set = load(file('learning_set%s.dat' % argv[1], 'r'))
-
-# Train the classifier with the learning set
-classifier = Classifier(c=float(argv[1]), \
-                        gamma=float(argv[2]), \
-                        neighbours=int(argv[3]))
-classifier.train(learning_set)
-
-print 'Loading test set'
-test_set = load(file('test_set%s.dat' % argv[1], 'r'))
-l = len(test_set)
-matches = 0
-
-for i, char in enumerate(test_set):
-    prediction = classifier.classify(char, char.value)
-
-    if char.value == prediction:
-        print ':-----> Successfully recognized "%s"' % char.value,
-        matches += 1
-    else:
-        print ':( Expected character "%s", got "%s"' \
-                % (char.value, prediction),
-
-    print '  --  %d of %d (%d%% done)' % (i + 1, l, int(100 * (i + 1) / l))
-
-print '\n%d matches (%d%%), %d fails' % (matches, \
-        int(100 * matches / len(test_set)), \
-        len(test_set) - matches)

+ 1 - 2
src/test_compare.py

@@ -1,9 +1,8 @@
 #!/usr/bin/python
 from matplotlib.pyplot import imshow, subplot, show
 from LocalBinaryPatternizer import LocalBinaryPatternizer
-from GrayscaleImage import GrayscaleImage
 from cPickle import load
-from numpy import zeros, resize
+from numpy import zeros
 
 chars = load(file('characters.dat', 'r'))[::2]
 left = None

+ 0 - 1
src/test_performance.py

@@ -1,6 +1,5 @@
 #!/usr/bin/python
 from os import listdir
-from cPickle import load
 from sys import argv, exit
 from time import time
 

+ 91 - 71
src/xml_helper_functions.py

@@ -1,17 +1,21 @@
 from os import mkdir
 from os.path import exists
-from pylab import imsave, array, zeros, inv, dot, norm, svd, floor
+from pylab import array, zeros, inv, dot, svd, floor
 from xml.dom.minidom import parse
+from Point import Point
 from Character import Character
 from GrayscaleImage import GrayscaleImage
 from NormalizedCharacterImage import NormalizedCharacterImage
 from LicensePlate import LicensePlate
 
-# Gets the character data from a picture with a license plate
-def retrieve_data(plate, corners):
-    x0,y0, x1,y1, x2,y2, x3,y3 = corners
+# sets the entire license plate of an image
+def retrieve_data(image, corners):
+    x0, y0 = corners[0].to_tuple()
+    x1, y1 = corners[1].to_tuple()
+    x2, y2 = corners[2].to_tuple()
+    x3, y3 = corners[3].to_tuple()
 
-    M = max(x0, x1, x2, x3) - min(x0, x1, x2, x3)
+    M = int(1.2 * (max(x0, x1, x2, x3) - min(x0, x1, x2, x3)))
     N = max(y0, y1, y2, y3) - min(y0, y1, y2, y3)
 
     matrix = array([
@@ -25,7 +29,7 @@ def retrieve_data(plate, corners):
       [ 0,  0, 0, x3, y3, 1, -N * x3, -N * y3, -N]
     ])
 
-    P = get_transformation_matrix(matrix)
+    P = inv(get_transformation_matrix(matrix))
     data = array([zeros(M, float)] * N)
 
     for i in range(M):
@@ -34,7 +38,7 @@ def retrieve_data(plate, corners):
             or_coor_h = (or_coor[1][0] / or_coor[2][0],
                          or_coor[0][0] / or_coor[2][0])
 
-            data[j][i] = pV(plate, or_coor_h[0], or_coor_h[1])
+            data[j][i] = pV(image, or_coor_h[0], or_coor_h[1])
 
     return data
 
@@ -46,92 +50,108 @@ def get_transformation_matrix(matrix):
     U, D, V = svd(matrix)
     p = V[8][:]
 
-    return inv(array([[p[0],p[1],p[2]], [p[3],p[4],p[5]], [p[6],p[7],p[8]]]))
+    return array([
+        [ p[0], p[1], p[2] ],
+        [ p[3], p[4], p[5] ],
+        [ p[6], p[7], p[8] ]
+    ])
 
 def pV(image, x, y):
     #Get the value of a point (interpolated x, y) in the given image
-    if not image.in_bounds(x, y):
-      return 0
+    if image.in_bounds(x, y):
+        x_low  = floor(x)
+        x_high = floor(x + 1)
+        y_low  = floor(y)
+        y_high = floor(y + 1)
+        x_y    = (x_high - x_low) * (y_high - y_low)
 
-    x_low, x_high = floor(x), floor(x+1)
-    y_low, y_high = floor(y), floor(y+1)
-    x_y    = (x_high - x_low) * (y_high - y_low)
+        a = x_high - x
+        b = y_high - y
+        c = x - x_low
+        d = y - y_low
 
-    a = x_high - x
-    b = y_high - y
-    c = x - x_low
-    d = y - y_low
+        return image[x_low,  y_low] / x_y * a * b \
+            + image[x_high,  y_low] / x_y * c * b \
+            + image[x_low , y_high] / x_y * a * d \
+            + image[x_high, y_high] / x_y * c * d
 
-    return image[x_low,  y_low] / x_y * a * b \
-        + image[x_high,  y_low] / x_y * c * b \
-        + image[x_low , y_high] / x_y * a * d \
-        + image[x_high, y_high] / x_y * c * d
+    return 0
 
 def xml_to_LicensePlate(filename, save_character=None):
-    plate   = GrayscaleImage('../images/Images/%s.jpg' % filename)
-    dom     = parse('../images/Infos/%s.info' % filename)
-    country = ''
-    result  = []
-    version = get_node(dom, "current-version")
-    infos   = by_tag(dom, "info")
-
-    for info in infos:
-        if not version == get_node(info, "version"):
-            continue
+    image = GrayscaleImage('../images/Images/%s.jpg' % filename)
+    dom   = parse('../images/Infos/%s.info' % filename)
+    result_characters = []
 
-        country = get_node(info, "identification-letters")
-        temp    = by_tag(info, "characters")
+    version = dom.getElementsByTagName("current-version")[0].firstChild.data
+    info    = dom.getElementsByTagName("info")
 
-        if not temp: # no characters where found in the file
-            break
+    for i in info:
+        if version == i.getElementsByTagName("version")[0].firstChild.data:
 
-        characters = temp[0].childNodes
+            country = i.getElementsByTagName("identification-letters")[0].firstChild.data
+            temp = i.getElementsByTagName("characters")
 
-        for i, char in enumerate(characters):
-            if not char.nodeName == "character":
-              continue
+            if len(temp):
+              characters = temp[0].childNodes
+            else:
+              characters = []
+              break
 
-            value   = get_node(char, "char")
-            corners = get_corners(char)
+            for i, character in enumerate(characters):
+                if character.nodeName == "character":
+                    value   = character.getElementsByTagName("char")[0].firstChild.data
+                    corners = get_corners(character)
 
-            if not len(corners) == 8:
-                break
+                    if not len(corners) == 4:
+                      break
 
-            data  = retrieve_data(plate, corners)
-            image = NormalizedCharacterImage(data=data)
-            result.append(Character(value, corners, image, filename))
-        
-            if save_character:
-                character_image = GrayscaleImage(data=data)
-                path       = "../images/LearningSet/%s" % value
-                image_path = "%s/%d_%s.jpg" % (path, i, filename.split('/')[-1])
+                    character_data  = retrieve_data(image, corners)
+                    character_image = NormalizedCharacterImage(data=character_data)
 
-                if not exists(path):
-                  mkdir(path)
+                    result_characters.append(Character(value, corners, character_image, filename))
 
-                if not exists(image_path):
-                  character_image.save(image_path)
+                    if save_character:
+                        single_character = GrayscaleImage(data=character_data)
 
-    return LicensePlate(country, result)
+                        path = "../images/LearningSet/%s" % value
+                        image_path = "%s/%d_%s.jpg" % (path, i, filename.split('/')[-1])
 
-def get_node(node, tag):
-    return by_tag(node, tag)[0].firstChild.data
+                        if not exists(path):
+                          mkdir(path)
 
-def by_tag(node, tag):
-    return node.getElementsByTagName(tag)
+                        if not exists(image_path):
+                          single_character.save(image_path)
 
-def get_attr(node, attr):
-  return int(node.getAttribute(attr))
+    return LicensePlate(country, result_characters)
 
 def get_corners(dom):
-    p = by_tag(dom, "point")
+  nodes = dom.getElementsByTagName("point")
+  corners = []
+
+  margin_y = 3
+  margin_x = 2
+
+  corners.append(
+    Point(get_coord(nodes[0], "x") - margin_x,
+          get_coord(nodes[0], "y") - margin_y)
+  )
+
+  corners.append(
+    Point(get_coord(nodes[1], "x") + margin_x,
+          get_coord(nodes[1], "y") - margin_y)
+  )
+
+  corners.append(
+    Point(get_coord(nodes[2], "x") + margin_x,
+          get_coord(nodes[2], "y") + margin_y)
+  )
+
+  corners.append(
+    Point(get_coord(nodes[3], "x") - margin_x,
+          get_coord(nodes[3], "y") + margin_y)
+  )
 
-    # Extra padding
-    y = 3
-    x = 2
+  return corners
 
-    # return 8 values (x0,y0, .., x3,y3)
-    return get_attr(p[0], "x") - x, get_attr(p[0], "y") - y,\
-           get_attr(p[1], "x") + x, get_attr(p[1], "y") - y,\
-           get_attr(p[2], "x") + x, get_attr(p[2], "y") + y,\
-           get_attr(p[3], "x") - x, get_attr(p[3], "y") + y
+def get_coord(node, attribute):
+  return int(node.getAttribute(attribute))

+ 3 - 0
todo.txt

@@ -0,0 +1,3 @@
+- Beste classifier runnen en kijken welke karakters fout gaan
+- code documenteren
+- verslag: conclusies aan parameters verbinden