LicensePlate.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. from pylab import array, zeros, inv, dot, svd, shape, floor
  2. from xml.dom.minidom import parse
  3. from Error import Error
  4. from Point import Point
  5. from Character import Character
  6. from GrayscaleImage import GrayscaleImage
  7. from NormalizedCharacterImage import NormalizedCharacterImage
  8. '''
  9. Creates a license plate object based on an XML file. The image should be
  10. placed in a folder 'images' the xml file in a folder 'xml'
  11. TODO: perhaps remove non required XML lookups
  12. '''
  13. class LicensePlate:
  14. def __init__(self, folder_nr, file_nr):
  15. filename = '%04d/00991_%04d%02d' % (folder_nr, folder_nr, file_nr)
  16. self.dom = parse('../images/Infos/%s.info' % filename)
  17. properties = self.get_properties()
  18. self.image = GrayscaleImage('../images/Images/%s.jpg' % filename)
  19. self.width = int(properties['width'])
  20. self.height = int(properties['height'])
  21. self.read_xml()
  22. # sets the entire license plate of an image
  23. def retrieve_data(self, corners):
  24. x0, y0 = corners[0].to_tuple()
  25. x1, y1 = corners[1].to_tuple()
  26. x2, y2 = corners[2].to_tuple()
  27. x3, y3 = corners[3].to_tuple()
  28. M = max(x0, x1, x2, x3) - min(x0, x1, x2, x3)
  29. N = max(y0, y1, y2, y3) - min(y0, y1, y2, y3)
  30. matrix = array([
  31. [x0, y0, 1, 0, 0, 0, 0, 0, 0],
  32. [ 0, 0, 0, x0, y0, 1, 0, 0, 0],
  33. [x1, y1, 1, 0, 0, 0, -M*x0, -M*y1, -M],
  34. [ 0, 0, 0, x1, y1, 1, 0, 0, 0],
  35. [x2, y2, 1, 0, 0, 0, -M*x2, -M*y2, -M],
  36. [ 0, 0, 0, x2, y2, 1, -N*x2, -N*y2, -N],
  37. [x3, y3, 1, 0, 0, 0, 0, 0, 0],
  38. [ 0, 0, 0, x3, y3, 1, -N*x3, -N*y3, -N]
  39. ])
  40. P = inv(self.get_transformation_matrix(matrix))
  41. data = array([zeros(M, float)] * N)
  42. for i in range(0, M):
  43. for j in range(0, N):
  44. or_coor = dot(P, ([[i],[j],[1]]))
  45. or_coor_h = or_coor[1][0] / or_coor[2][0], or_coor[0][0] / or_coor[2][0]
  46. data[j][i] = self.pV(or_coor_h[0], or_coor_h[1])
  47. return data
  48. def get_transformation_matrix(self, matrix):
  49. # Get the vector p and the values that are in there by taking the SVD.
  50. # Since D is diagonal with the eigenvalues sorted from large to small on
  51. # the diagonal, the optimal q in min ||Dq|| is q = [[0]..[1]]. Therefore,
  52. # p = Vq means p is the last column in V.
  53. U, D, V = svd(matrix)
  54. p = V[8][:]
  55. return array([
  56. [ p[0], p[1], p[2] ],
  57. [ p[3], p[4], p[5] ],
  58. [ p[6], p[7], p[8] ]
  59. ])
  60. def pV(self, x, y):
  61. image = self.image
  62. '''Get the value of a point x,y in the given image, where x and y are not
  63. necessary integers, so the value is interpolated from its neighbouring
  64. pixels.'''
  65. if image.in_bounds(x, y):
  66. x_low = floor(x)
  67. x_high = floor(x + 1)
  68. y_low = floor(y)
  69. y_high = floor(y + 1)
  70. x_y = (x_high - x_low) * (y_high - y_low)
  71. a = x_high - x
  72. b = y_high - y
  73. c = x - x_low
  74. d = y - y_low
  75. return image[x_low, y_low] / x_y * a * b \
  76. + image[x_high, y_low] / x_y * c * b \
  77. + image[x_low , y_high] / x_y * a * d \
  78. + image[x_high, y_high] / x_y * c * d
  79. return 0
  80. # Testing purposes
  81. def show(self):
  82. from pylab import imshow, show
  83. imshow(self.data, cmap="gray")
  84. show()
  85. def get_properties(self):
  86. children = self.get_children("properties")
  87. properties = {}
  88. for child in children:
  89. if child.nodeType == child.TEXT_NODE:
  90. properties[child.nodeName] = child.data
  91. elif child.nodeType == child.ELEMENT_NODE:
  92. properties[child.nodeName] = child.firstChild.data
  93. return properties
  94. # TODO : create function for location / characters as they do the same
  95. def read_xml(self):
  96. children = self.get_children("plate") # most recent version
  97. for child in children:
  98. if child.nodeName == "regnum":
  99. self.license_full = child.firstChild.data
  100. elif child.nodeName == "identification-letters":
  101. self.country = child.firstChild.data
  102. elif child.nodeName == "location":
  103. self.corners = self.get_corners(child)
  104. elif child.nodeName == "characters":
  105. nodes = child.childNodes
  106. self.characters = []
  107. for character in nodes:
  108. if character.nodeName == "character":
  109. value = self.get_node("char", character).firstChild.data
  110. corners = self.get_corners(character)
  111. data = self.retrieve_data(corners)
  112. image = NormalizedCharacterImage(data=data)
  113. self.characters.append(Character(value, corners, image))
  114. else:
  115. pass
  116. def get_node(self, node, dom=None):
  117. if not dom:
  118. dom = self.dom
  119. return dom.getElementsByTagName(node)[0]
  120. def get_children(self, node, dom=None):
  121. return self.get_node(node, dom).childNodes
  122. def get_corners(self, child):
  123. nodes = self.get_children("quadrangle", child)
  124. corners = []
  125. for corner in nodes:
  126. if corner.nodeName == "point":
  127. corners.append(Point(corner))
  128. return corners