소스 검색

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

Richard Torenvliet 14 년 전
부모
커밋
f957089b14
11개의 변경된 파일193개의 추가작업 그리고 118개의 파일을 삭제
  1. 1 0
      .gitignore
  2. 8 3
      src/Classifier.py
  3. 6 1
      src/GaussianFilter.py
  4. 1 1
      src/LocalBinaryPatternizer.py
  5. 1 1
      src/find_svm_params.py
  6. 81 0
      src/run_classifier.py
  7. 0 38
      src/test_classifier.py
  8. 1 2
      src/test_compare.py
  9. 0 1
      src/test_performance.py
  10. 91 71
      src/xml_helper_functions.py
  11. 3 0
      todo.txt

+ 1 - 0
.gitignore

@@ -15,3 +15,4 @@ images/BBB
 images/Images
 images/Images
 images/Infos
 images/Infos
 images/licenseplates
 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:
 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
         self.neighbours = neighbours
 
 
         if filename:
         if filename:
@@ -18,6 +19,8 @@ class Classifier:
             self.param.gamma = gamma  # Parameter for radial kernel
             self.param.gamma = gamma  # Parameter for radial kernel
             self.model = None
             self.model = None
 
 
+        self.verbose = verbose
+
     def save(self, filename):
     def save(self, filename):
         """Save the SVM model in the given filename."""
         """Save the SVM model in the given filename."""
         svm_save_model(filename, self.model)
         svm_save_model(filename, self.model)
@@ -30,8 +33,9 @@ class Classifier:
         l = len(learning_set)
         l = len(learning_set)
 
 
         for i, char in enumerate(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)))
             classes.append(float(ord(char.value)))
             #features.append(char.get_feature_vector())
             #features.append(char.get_feature_vector())
             char.get_single_cell_feature_vector(self.neighbours)
             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)
         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(self.neighbours)
         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)
         p = svm_predict([true_value], [character.feature], self.model)
         prediction_class = int(p[0][0])
         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
 from scipy.ndimage import gaussian_filter
 
 
 class GaussianFilter:
 class GaussianFilter:
+    """This class can apply a Gaussian blur on an image."""
 
 
     def __init__(self, scale):
     def __init__(self, scale):
+        """Create a GaussianFilter object with a given scale."""
         self.scale = scale
         self.scale = scale
 
 
     def get_filtered_copy(self, image):
     def get_filtered_copy(self, image):
@@ -12,12 +14,15 @@ class GaussianFilter:
         return GrayscaleImage(None, image)
         return GrayscaleImage(None, image)
 
 
     def filter(self, image):
     def filter(self, image):
+        """Apply a Gaussian blur on the image data."""
         image.data = gaussian_filter(image.data, self.scale)
         image.data = gaussian_filter(image.data, self.scale)
 
 
     def get_scale(self):
     def get_scale(self):
-      return self.scale
+        """Return the scale of the Gaussian kernel."""
+        return self.scale
 
 
     def set_scale(self, scale):
     def set_scale(self, scale):
+        """Set the scale of the Gaussian kernel."""
         self.scale = float(scale)
         self.scale = float(scale)
 
 
     scale = property(get_scale, set_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))
              | (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 clockwise order, shifting 1 bit less at
         each neighbour starting at 7 in the top-left corner. This gives a 8-bit
         each neighbour starting at 7 in the top-left corner. This gives a 8-bit
         feature number of a pixel'''
         feature number of a pixel'''
         self.setup_histograms()
         self.setup_histograms()

+ 1 - 1
src/find_svm_params.py

@@ -86,7 +86,7 @@ i = 0
 
 
 for c in C:
 for c in C:
     for y in Y:
     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)
         classifier.train(learning_set)
         result = classifier.test(test_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
 #!/usr/bin/python
 from matplotlib.pyplot import imshow, subplot, show
 from matplotlib.pyplot import imshow, subplot, show
 from LocalBinaryPatternizer import LocalBinaryPatternizer
 from LocalBinaryPatternizer import LocalBinaryPatternizer
-from GrayscaleImage import GrayscaleImage
 from cPickle import load
 from cPickle import load
-from numpy import zeros, resize
+from numpy import zeros
 
 
 chars = load(file('characters.dat', 'r'))[::2]
 chars = load(file('characters.dat', 'r'))[::2]
 left = None
 left = None

+ 0 - 1
src/test_performance.py

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

+ 91 - 71
src/xml_helper_functions.py

@@ -1,17 +1,21 @@
 from os import mkdir
 from os import mkdir
 from os.path import exists
 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 xml.dom.minidom import parse
+from Point import Point
 from Character import Character
 from Character import Character
 from GrayscaleImage import GrayscaleImage
 from GrayscaleImage import GrayscaleImage
 from NormalizedCharacterImage import NormalizedCharacterImage
 from NormalizedCharacterImage import NormalizedCharacterImage
 from LicensePlate import LicensePlate
 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)
     N = max(y0, y1, y2, y3) - min(y0, y1, y2, y3)
 
 
     matrix = array([
     matrix = array([
@@ -25,7 +29,7 @@ def retrieve_data(plate, corners):
       [ 0,  0, 0, x3, y3, 1, -N * x3, -N * y3, -N]
       [ 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)
     data = array([zeros(M, float)] * N)
 
 
     for i in range(M):
     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_h = (or_coor[1][0] / or_coor[2][0],
                          or_coor[0][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
     return data
 
 
@@ -46,92 +50,108 @@ def get_transformation_matrix(matrix):
     U, D, V = svd(matrix)
     U, D, V = svd(matrix)
     p = V[8][:]
     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):
 def pV(image, x, y):
     #Get the value of a point (interpolated x, y) in the given image
     #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):
 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):
 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