|
|
@@ -1,42 +1,41 @@
|
|
|
from pylab import array, zeros, inv, dot, svd, shape, floor
|
|
|
-from xml.dom.minidom import parse
|
|
|
+from xml.dom.minidom import parse
|
|
|
from Error import Error
|
|
|
from Point import Point
|
|
|
from Character import Character
|
|
|
from GrayscaleImage import GrayscaleImage
|
|
|
+from NormalizedCharacterImage import NormalizedCharacterImage
|
|
|
|
|
|
'''
|
|
|
Creates a license plate object based on an XML file. The image should be
|
|
|
placed in a folder 'images' the xml file in a folder 'xml'
|
|
|
-
|
|
|
- TODO: perhaps remove non required XML lookups
|
|
|
+
|
|
|
+ TODO: perhaps remove non required XML lookups
|
|
|
'''
|
|
|
class LicensePlate:
|
|
|
|
|
|
- def __init__(self, xml_title):
|
|
|
- try:
|
|
|
- self.dom = parse('../XML/' + str(xml_title))
|
|
|
- except IOError:
|
|
|
- Error("Incorrect file name given.")
|
|
|
- else:
|
|
|
- properties = self.get_properties()
|
|
|
+ def __init__(self, folder_nr, file_nr):
|
|
|
+ filename = '%04d/00991_%04d%02d' % (folder_nr, folder_nr, file_nr)
|
|
|
+
|
|
|
+ self.dom = parse('../images/Infos/%s.info' % filename)
|
|
|
+ properties = self.get_properties()
|
|
|
+
|
|
|
+ self.image = GrayscaleImage('../images/Images/%s.jpg' % filename)
|
|
|
+ self.width = int(properties['width'])
|
|
|
+ self.height = int(properties['height'])
|
|
|
|
|
|
- self.image = GrayscaleImage('../images/' + str(properties['uii']) + '.' + str(properties['type']))
|
|
|
- self.width = int(properties['width'])
|
|
|
- self.height = int(properties['height'])
|
|
|
+ self.read_xml()
|
|
|
|
|
|
- self.read_xml()
|
|
|
-
|
|
|
# sets the entire license plate of an image
|
|
|
- def retrieve_data(self, corners):
|
|
|
+ def retrieve_data(self, 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)
|
|
|
N = max(y0, y1, y2, y3) - min(y0, y1, y2, y3)
|
|
|
-
|
|
|
+
|
|
|
matrix = array([
|
|
|
[x0, y0, 1, 0, 0, 0, 0, 0, 0],
|
|
|
[ 0, 0, 0, x0, y0, 1, 0, 0, 0],
|
|
|
@@ -44,10 +43,10 @@ class LicensePlate:
|
|
|
[ 0, 0, 0, x1, y1, 1, 0, 0, 0],
|
|
|
[x2, y2, 1, 0, 0, 0, -M*x2, -M*y2, -M],
|
|
|
[ 0, 0, 0, x2, y2, 1, -N*x2, -N*y2, -N],
|
|
|
- [x3, y3, 1, 0, 0, 0, 0, 0, 0],
|
|
|
+ [x3, y3, 1, 0, 0, 0, 0, 0, 0],
|
|
|
[ 0, 0, 0, x3, y3, 1, -N*x3, -N*y3, -N]
|
|
|
])
|
|
|
-
|
|
|
+
|
|
|
P = inv(self.get_transformation_matrix(matrix))
|
|
|
data = array([zeros(M, float)] * N)
|
|
|
|
|
|
@@ -56,23 +55,23 @@ class LicensePlate:
|
|
|
or_coor = dot(P, ([[i],[j],[1]]))
|
|
|
or_coor_h = or_coor[1][0] / or_coor[2][0], or_coor[0][0] / or_coor[2][0]
|
|
|
data[j][i] = self.pV(or_coor_h[0], or_coor_h[1])
|
|
|
-
|
|
|
+
|
|
|
return data
|
|
|
-
|
|
|
+
|
|
|
def get_transformation_matrix(self, matrix):
|
|
|
- # Get the vector p and the values that are in there by taking the SVD.
|
|
|
+ # Get the vector p and the values that are in there by taking the SVD.
|
|
|
# Since D is diagonal with the eigenvalues sorted from large to small on
|
|
|
- # the diagonal, the optimal q in min ||Dq|| is q = [[0]..[1]]. Therefore,
|
|
|
+ # the diagonal, the optimal q in min ||Dq|| is q = [[0]..[1]]. Therefore,
|
|
|
# p = Vq means p is the last column in V.
|
|
|
U, D, V = svd(matrix)
|
|
|
p = V[8][:]
|
|
|
-
|
|
|
+
|
|
|
return array([
|
|
|
- [ p[0], p[1], p[2] ],
|
|
|
- [ p[3], p[4], p[5] ],
|
|
|
+ [ p[0], p[1], p[2] ],
|
|
|
+ [ p[3], p[4], p[5] ],
|
|
|
[ p[6], p[7], p[8] ]
|
|
|
])
|
|
|
-
|
|
|
+
|
|
|
def pV(self, x, y):
|
|
|
image = self.image
|
|
|
|
|
|
@@ -85,25 +84,25 @@ class LicensePlate:
|
|
|
y_low = floor(y)
|
|
|
y_high = 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
|
|
|
-
|
|
|
+
|
|
|
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
|
|
|
-
|
|
|
+
|
|
|
# Testing purposes
|
|
|
def show(self):
|
|
|
from pylab import imshow, show
|
|
|
imshow(self.data, cmap="gray")
|
|
|
show()
|
|
|
-
|
|
|
+
|
|
|
def get_properties(self):
|
|
|
children = self.get_children("properties")
|
|
|
|
|
|
@@ -120,7 +119,7 @@ class LicensePlate:
|
|
|
# TODO : create function for location / characters as they do the same
|
|
|
def read_xml(self):
|
|
|
children = self.get_children("plate") # most recent version
|
|
|
-
|
|
|
+
|
|
|
for child in children:
|
|
|
if child.nodeName == "regnum":
|
|
|
self.license_full = child.firstChild.data
|
|
|
@@ -130,7 +129,7 @@ class LicensePlate:
|
|
|
self.corners = self.get_corners(child)
|
|
|
elif child.nodeName == "characters":
|
|
|
nodes = child.childNodes
|
|
|
-
|
|
|
+
|
|
|
self.characters = []
|
|
|
|
|
|
for character in nodes:
|
|
|
@@ -139,11 +138,11 @@ class LicensePlate:
|
|
|
corners = self.get_corners(character)
|
|
|
data = self.retrieve_data(corners)
|
|
|
image = NormalizedCharacterImage(data=data)
|
|
|
-
|
|
|
+
|
|
|
self.characters.append(Character(value, corners, image))
|
|
|
else:
|
|
|
pass
|
|
|
-
|
|
|
+
|
|
|
def get_node(self, node, dom=None):
|
|
|
if not dom:
|
|
|
dom = self.dom
|
|
|
@@ -152,14 +151,14 @@ class LicensePlate:
|
|
|
|
|
|
def get_children(self, node, dom=None):
|
|
|
return self.get_node(node, dom).childNodes
|
|
|
-
|
|
|
+
|
|
|
def get_corners(self, child):
|
|
|
nodes = self.get_children("quadrangle", child)
|
|
|
-
|
|
|
+
|
|
|
corners = []
|
|
|
|
|
|
for corner in nodes:
|
|
|
if corner.nodeName == "point":
|
|
|
corners.append(Point(corner))
|
|
|
-
|
|
|
+
|
|
|
return corners
|