Commit d60f777c authored by Taddeüs Kroes's avatar Taddeüs Kroes

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

parents e88b8344 b49213eb
from scipy.ndimage import convolve1d
from pylab import ceil, zeros, pi, e, exp, sqrt, array
def f(x, s):
"""Return the value of a 1D Gaussian function for a given x and scale."""
return exp(-(x ** 2 / (2 * s ** 2))) / (sqrt(2 * pi) * s)
def gauss1(s, order=0):
"""Sample a one-dimensional Gaussian function of scale s."""
s = float(s)
r = int(ceil(3 * s))
size = 2 * r + 1
W = zeros(size)
# Sample the Gaussian function
W = array([f(x - r, s) for x in xrange(size)])
if not order:
# Make sure that the sum of all kernel values is equal to one
W /= W.sum()
return W
def filterNoise(image, s):
'''Apply a gaussian blur to an image, to suppress noise.'''
filt = gauss1(s)
image = convolve1d(image.data, filt, axis=0, mode='nearest')
return convolve1d(image, filt, axis=1, mode='nearest')
from GrayscaleImage import GrayscaleImage
from scipy.ndimage import convolve1d
from pylab import ceil, zeros, pi, e, exp, sqrt, array
class GaussianFilter:
def __init__(self, scale):
self.scale = scale
def gaussian(self, x):
'''Return the value of a 1D Gaussian function for a given x and scale'''
return exp(-(x ** 2 / (2 * self.scale ** 2))) / (sqrt(2 * pi) * self.scale)
def get_1d_gaussian_kernel(self):
'''Sample a one-dimensional Gaussian function of scale s'''
radius = int(ceil(3 * self.scale))
size = 2 * radius + 1
result = zeros(size)
# Sample the Gaussian function
result = array([self.gaussian(x - radius) for x in xrange(size)])
# The sum of all kernel values is equal to one
result /= result.sum()
return result
def get_filtered_copy(self, image):
'''Apply a gaussian blur to an image, to suppress noise.'''
kernel = self.get_1d_gaussian_kernel()
image = convolve1d(image.data, kernel, axis=0, mode='nearest')
return GrayscaleImage(None, convolve1d(image, kernel, axis=1, mode='nearest'))
def filter(self, image):
kernel = self.get_1d_gaussian_kernel()
image.data = convolve1d(image.data, kernel, axis=0, mode='nearest')
image.data = convolve1d(image.data, kernel, axis=1, mode='nearest')
def get_scale(self):
return self.scale
def set_scale(self, scale):
self.scale = float(scale)
scale = property(get_scale, set_scale)
from FilterNoise import filterNoise from FilterNoise import GaussianFilter
from GrayscaleImage import GrayscaleImage from GrayscaleImage import GrayscaleImage
# Get the image
image = GrayscaleImage('../images/plate.png') image = GrayscaleImage('../images/plate.png')
output_image = filterNoise(image, 1.4) filter = GaussianFilter(1.4)
output_image = filter.get_filtered_copy(image)
# Show the licenseplate
output_image = GrayscaleImage(None, output_image)
output_image.show() output_image.show()
from pylab import array, zeros, inv, dot, svd, shape
from Interpolation import pV
def getPlateAt(image, points, M, N):
'''Returns an image of size MxN of the licenseplate (or any rectangular
object) defined by 4 corner points.'''
# Construct the matrix M
x1_a, y1_a = 0, 0
x2_a, y2_a = M, 0
x3_a, y3_a = M, N
x4_a, y4_a = 0, N
p_i = []
for point in points:
p_i.append([point.x, point.y])
mat_M = array([[p_i[0][0], p_i[0][1], 1, 0, 0, 0, \
-x1_a * p_i[0][0], -x1_a * p_i[0][1], -x1_a], \
[0, 0, 0, p_i[0][0], p_i[0][1], 1, \
-y1_a * p_i[0][0], -y1_a * p_i[0][1], -y1_a], \
[p_i[1][0], p_i[1][1], 1, 0, 0, 0, \
-x2_a * p_i[1][0], -x2_a * p_i[1][1], -x2_a], \
[0, 0, 0, p_i[1][0], p_i[1][1], 1, \
-y2_a * p_i[1][0], -y2_a * p_i[1][1], -y2_a], \
[p_i[2][0], p_i[2][1], 1, 0, 0, 0, \
-x3_a * p_i[2][0], -x3_a * p_i[2][1], -x3_a], \
[0, 0, 0, p_i[2][0], p_i[2][1], 1, \
-y3_a * p_i[2][0], -y3_a * p_i[2][1], -y3_a], \
[p_i[3][0], p_i[3][1], 1, 0, 0, 0, \
-x4_a * p_i[3][0], -x4_a * p_i[3][1], -x4_a], \
[0, 0, 0, p_i[3][0], p_i[3][1], 1, \
-y4_a * p_i[3][0], -y4_a * p_i[3][1], -y4_a]])
# 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,
# p = Vq means p is the last column in V.
U, D, V = svd(mat_M)
p = V[8][:]
a, b, c, d, e, f, g, h, i = p[0], \
p[1], \
p[2], \
p[3], \
p[4], \
p[5], \
p[6], \
p[7], \
p[8]
# P is the resulting matrix that describes the transformation
P = array([[a, b, c], \
[d, e, f], \
[g, h, i]])
# Create the new image
b = array([zeros(M, float)] * N)
for i in range(0, M):
for j in range(0, N):
or_coor = dot(inv(P),([[i],[j],[1]]))
or_coor_h = or_coor[1][0] / or_coor[2][0], \
or_coor[0][0] / or_coor[2][0]
b[j][i] = pV(image, or_coor_h[0], or_coor_h[1])
return b
from GetPlate import getPlateAt
from GrayscaleImage import GrayscaleImage
from Point import Point
# Define the corners of the licenseplate
points = [Point(None, (310, 383)), \
Point(None, (382, 381)), \
Point(None, (382, 396)), \
Point(None, (310, 398))]
# Get the image
image = GrayscaleImage('../images/test_plate.png')
# Let the code get the licenseplate
output_image = getPlateAt(image, points, 100, 20)
# Show the licenseplate
output_image = GrayscaleImage(None, output_image)
output_image.show()
from pylab import imshow, imread, show from pylab import imshow, imread, show
from scipy.misc import imresize from scipy.misc import imresize
from matplotlib.pyplot import hist
from scipy.misc import imresize, imsave
class GrayscaleImage: class GrayscaleImage:
...@@ -29,7 +31,8 @@ class GrayscaleImage: ...@@ -29,7 +31,8 @@ class GrayscaleImage:
return self.data[position] return self.data[position]
def convert_to_grayscale(self): def convert_to_grayscale(self):
self.data = self.data.sum(axis=2) / 3 if len(self.data.shape) > 2:
self.data = self.data[:,:,:3].sum(axis=2) / 3
def crop(self, rectangle): def crop(self, rectangle):
self.data = self.data[rectangle.y : rectangle.y + rectangle.height, self.data = self.data[rectangle.y : rectangle.y + rectangle.height,
...@@ -39,21 +42,33 @@ class GrayscaleImage: ...@@ -39,21 +42,33 @@ class GrayscaleImage:
imshow(self.data, cmap="gray") imshow(self.data, cmap="gray")
show() show()
# size is of type float def make_histogram(self):
return hist(self.data)
# size is of type tuple of integers (DEFAULT = (50, 50))
def resize(self, size): def resize(self, size):
print size
def resize(self, size): # size is of type float
self.data = imresize(self.data, size) self.data = imresize(self.data, size)
def get_shape(self): def get_shape(self):
return self.data.shape return self.data.shape
shape = property(get_shape) shape = property(get_shape)
def get_width(self): def get_width(self):
return self.get_shape()[1] return self.get_shape()[1]
width = property(get_width) width = property(get_width)
def get_height(self): def get_height(self):
return self.get_shape()[0] return self.get_shape()[0]
height = property(get_height) height = property(get_height)
def in_bounds(self, y, x): def in_bounds(self, y, x):
return x >= 0 and x < self.width and y >= 0 and y < self.height return x >= 0 and x < self.width and y >= 0 and y < self.height
def save(self, path):
imsave(path, self.data)
#!/usr/bin/python
import Image
from numpy import array, zeros, byte
from matplotlib.pyplot import imshow, subplot, show, axis
# Divide the examined window to cells (e.g. 16x16 pixels for each cell).
# For each pixel in a cell, compare the pixel to each of its 8 neighbors
# (on its left-top, left-middle, left-bottom, right-top, etc.). Follow the
# pixels along a circle, i.e. clockwise or counter-clockwise.
# Where the center pixel's value is greater than the neighbor, write "1".
# Otherwise, write "0". This gives an 8-digit binary number (which is usually
# converted to decimal for convenience).
# Compute the histogram, over the cell, of the frequency of each "number"
# occurring (i.e., each combination of which pixels are smaller and which are
# greater than the center).
# Optionally normalize the histogram. Concatenate normalized histograms of all
# cells. This gives the feature vector for the window.
CELL_SIZE = 16
def domain_iterator(shape):
"""Iterate over the pixels of an image."""
for y in xrange(shape[0]):
for x in xrange(shape[1]):
yield y, x
image = array(Image.open('../images/test.png').convert('L'))
def in_image(y, x, F):
"""Check if given pixel coordinates are within the bounds of image F."""
return 0 <= y < F.shape[0] and 0 <= x < F.shape[1]
def features(image):
"""Compare each pixel to each of its eight neigheach pixel to each of its
eight neighbours."""
features = zeros(image.shape, dtype=byte)
def cmp_pixels(y, x, p):
"""Check if two pixels (y, x) and p are in the image, and if the value
at (y, x) is larger than the value at p."""
return in_image(y, x, image) and image[y, x] > p
for y, x in domain_iterator(features.shape):
p = image[y, x]
# Walk around the pixel in counter-clokwise 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
features[y, x] = byte(cmp_pixels(y - 1, x - 1, p)) << 7 \
| byte(cmp_pixels(y - 1, x, p)) << 6 \
| byte(cmp_pixels(y - 1, x + 1, p)) << 5 \
| byte(cmp_pixels(y, x + 1, p)) << 4 \
| byte(cmp_pixels(y + 1, x + 1, p)) << 3 \
| byte(cmp_pixels(y + 1, x, p)) << 2 \
| byte(cmp_pixels(y + 1, x - 1, p)) << 1 \
| byte(cmp_pixels(y, x - 1, p))
return features
def feature_vectors(image):
"""Create cell histograms of an image"""
F = features(image)
V = feature_vectors(image)
subplot(121)
imshow(image, cmap='gray')
subplot(122)
imshow(V, cmap='gray')
axis('off')
show()
from copy import deepcopy from copy import deepcopy
from Rectangle import Rectangle from Rectangle import Rectangle
from GrayscaleImage import GrayscaleImage
class LetterCropper: class LetterCropper:
def __init__(self, image, threshold = 0.9): def __init__(self, threshold = 0.9):
self.source_image = image
self.threshold = threshold self.threshold = threshold
def get_cropped_letter(self): def crop_to_letter(self, image):
self.image = image
self.determine_letter_bounds() self.determine_letter_bounds()
self.result_image = deepcopy(self.source_image) self.image.crop(self.letter_bounds)
self.result_image.crop(self.letter_bounds)
return self.result_image
def determine_letter_bounds(self): def determine_letter_bounds(self):
min_x = self.source_image.width min_x = self.image.width
max_x = 0 max_x = 0
min_y = self.source_image.height min_y = self.image.height
max_y = 0 max_y = 0
for y, x, value in self.source_image: for y, x, value in self.image:
if value < self.threshold: if value < self.threshold:
if x < min_x: min_x = x if x < min_x: min_x = x
if y < min_y: min_y = y if y < min_y: min_y = y
......
...@@ -9,7 +9,6 @@ class LocalBinaryPatternizer: ...@@ -9,7 +9,6 @@ class LocalBinaryPatternizer:
self.image = image self.image = image
self.setup_histograms() self.setup_histograms()
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)))
cells_in_height = int(ceil(self.image.height / float(self.cell_size))) cells_in_height = int(ceil(self.image.height / float(self.cell_size)))
...@@ -19,7 +18,6 @@ class LocalBinaryPatternizer: ...@@ -19,7 +18,6 @@ class LocalBinaryPatternizer:
for j in xrange(cells_in_width): for j in xrange(cells_in_width):
self.features[i].append(Histogram(256,0,256)) self.features[i].append(Histogram(256,0,256))
def create_features_vector(self): def create_features_vector(self):
''' Walk around the pixels in clokwise order, shifting 1 bit less ''' Walk around the pixels in clokwise order, shifting 1 bit less
at each neighbour starting at 7 in the top-left corner. This gives a at each neighbour starting at 7 in the top-left corner. This gives a
...@@ -40,14 +38,11 @@ class LocalBinaryPatternizer: ...@@ -40,14 +38,11 @@ class LocalBinaryPatternizer:
return self.get_features_as_array() return self.get_features_as_array()
def is_pixel_darker(self, y, x, value): def is_pixel_darker(self, y, x, value):
return self.image.in_bounds(y, x) and self.image[y, x] > value return self.image.in_bounds(y, x) and self.image[y, x] > value
def get_cell_index(self, y, x): def get_cell_index(self, y, x):
return (y / self.cell_size, x / self.cell_size) return (y / self.cell_size, x / self.cell_size)
def get_features_as_array(self): def get_features_as_array(self):
return [item for sublist in self.features for item in sublist] return [item for sublist in self.features for item in sublist]
\ No newline at end of file
from copy import deepcopy
from GrayscaleImage import GrayscaleImage
from LetterCropper import LetterCropper
from GaussianFilter import GaussianFilter
class NormalizedCharacterImage(GrayscaleImage):
def __init__(self, image, size=(60, 40), blur=1.1, crop_threshold=0.9):
GrayscaleImage.__init__(self, data=deepcopy(image.data))
self.blur = blur
self.crop_threshold = crop_threshold
self.size = size
self.gausse_filter()
self.increase_contrast()
self.crop_to_letter()
self.resize()
def increase_contrast(self):
self.data -= self.data.min()
self.data /= self.data.max()
def gausse_filter(self):
filter = GaussianFilter(1.1)
filter.filter(self)
def crop_to_letter(self):
cropper = LetterCropper(0.9)
cropper.crop_to_letter(self)
def resize(self):
GrayscaleImage.resize(self, self.size)
\ No newline at end of file
from copy import deepcopy
class NormalizedImage:
DEFAULT_SIZE = 100.0
def __init__(self, image, size=DEFAULT_SIZE):
self.source_image = image
self.size = size
def add_padding(self):
pass
# normalize img
def get_normalized_letter(self):
self.result_image = deepcopy(self.source_image)
self.result_image.resize(self.size / self.source_image.height)
return self.result_image
from GrayscaleImage import GrayscaleImage from GrayscaleImage import GrayscaleImage
from LocalBinaryPatternizer import LocalBinaryPatternizer from NormalizedCharacterImage import NormalizedCharacterImage
from LetterCropper import LetterCropper from LetterCropper import LetterCropper
from matplotlib.pyplot import imshow, subplot, show, axis
from NormalizedImage import NormalizedImage
# Comment added by Richard Torenvliet image = GrayscaleImage("../images/test10.png")
# Steps in this test files are normalized_character_image = NormalizedCharacterImage(image)
# 1. crop image normalized_character_image.show()
# 2. resize to default hight (in future also to width) \ No newline at end of file
# 3. preform LBP
# 4. construct feature vector
# 5. plot
# Image is now an instance of class GrayscaleImage
# GrayscaleImage has functions like resize, crop etc.
image = GrayscaleImage("../images/test.png")
# Crops image; param threshold is optional: LetterCropper(image, threshold=0.9)
# image: GrayscaleImage, threshold: float
cropper = LetterCropper(image, 0.9)
cropped_letter = cropper.get_cropped_letter()
# Show difference in shape
print cropped_letter.shape
# Resizes image; param size is optional: NormalizedImage(image, size=DEFAULT)
# image: GrayscaleImage, size: float
norm = NormalizedImage(cropped_letter)
resized = norm.get_normalized_letter()
# Difference is noticable
print resized.shape
lbp = LocalBinaryPatternizer(resized)
feature_vector = lbp.create_features_vector()
feature_vector /= 255 # Prepare for displaying -> 0 - 255 -> 0 - 1
subplot(141)
imshow(image.data, cmap='gray')
subplot(142)
imshow(cropped_letter.data, cmap='gray')
subplot(143)
imshow(resized.data, cmap='gray')
subplot(144)
imshow(feature_vector, cmap='gray')
axis('off')
show()
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