%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% Prof. Sclaroff's CS585 Image avd Video Processing %%
%% Project ONE %%
%% C H A R A C T E R R E C O G N I T I O N %%
%% %%
%% by Stanislav Rost %%
%% ID: 31764117 %%
%% %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function result = vowel(filename)
% VOWEL.M
%
% function result = vowel(filename)
%
% Arguments: name of the file containing scanned letters
%
% Vowel.m performs vowel recognition using moments, part ratios and
% compactness.
%
% As a result, the function shows an image with letters O marked
% blue, E magenta, A red, U green. All other letters are white/gray.
%
% Assumptions:
% - if the paragraph is tilted, then it is rectangular
% - no more than two letters are joined at any instance of joined letters
imageData = im2bw(imread(filename,'tif'),0.5);
imageData = ~imageData;
% Find out average height of letters
avgLabel = bwlabel(imageData, 4);
avgLetters = imfeature(avgLabel, 'BoundingBox');
numAvg = max(avgLabel(:));
avgHeight = 0;
for i = 1:numAvg,
avgHeight = avgHeight + avgLetters(i).BoundingBox(4);
end
avgHeight = round(avgHeight / numAvg);
clear avgLetters
% #################### ROTATOR ############################
% Rotate the image so that letters are properly oriented
% This is how it's done: we use the find out imfeature's Orientation
% for the entire paragraph (so the assumption here is that the paragraph
% is wider than it is tall), and then rotate by that value...
% only if the image is tilted, which can be checked by breaking the image in 2,
% and then seeing if the top boundaries on their bounding boxes are about the
% same height
origFeature = imfeature(double(imageData), 'BoundingBox', 'Orientation');
angle = - origFeature.Orientation;
midpoint = floor(origFeature.BoundingBox(3)/2);
leftHalf = imageData(:,1:midpoint);
rightHalf = imageData(:,(midpoint+1):end);
leftFeature = imfeature(double(leftHalf), 'BoundingBox');
rightFeature = imfeature(double(rightHalf), 'BoundingBox');
% If the distance between upper boundaries of bounding boxes
% is bigger than half the average letter size, rotate image.
if (abs(leftFeature.BoundingBox(2) - rightFeature.BoundingBox(2)) > avgHeight/2 )
disp('Rotation accepted');
imageData = imrotate(imageData, angle, 'nearest');
end
% ################### LABEL 1 ##############################
imageData = bwlabel(imageData, 4);
% #################### SPLITTER ############################
% Split the letters that need to be split
% First, calculate the averge width of letters
splitLetters = imfeature(imageData, 'BoundingBox', 'Image');
numSplit = max(imageData(:));
avgWidth = 0;
for i = 1:numSplit,
avgWidth = avgWidth + splitLetters(i).BoundingBox(3);
end
avgWidth = round(avgWidth / numSplit);
% Now, go through all the letters and if their width
% is bigger than 1.5 average then split them.
for i = 1:numSplit,
if splitLetters(i).BoundingBox(3) > 1.5 * avgWidth
[ rows columns ] = size(splitLetters(i).Image);
% Draw vertical line
splitLetters(i).Image(:, avgWidth ) = 0;
splitLetters(i).Image(:, splitLetters(i).BoundingBox(3) - avgWidth) = 0;
if (columns>avgWidth*2.5)
splitLetters(i).Image(:, columns - avgWidth +2 ) = 0;
end
for k = 1:2,
splitLetters(i).Image =erode(splitLetters(i).Image, ...
[ 0,1,0;0,1,0 ]);
splitLetters(i).Image = bwmorph(splitLetters(i).Image, 'spur');
end
% insert the split image back into the original image
xoffset = round(splitLetters(i).BoundingBox(1));
yoffset = round(splitLetters(i).BoundingBox(2));
% Put the image back into training data
for c = 1:splitLetters(i).BoundingBox(3),
for d = 1:splitLetters(i).BoundingBox(4),
imageData(yoffset+d-1, xoffset+c-1) = ...
splitLetters(i).Image(d,c);
end
end
end
end
imageData = im2bw(imageData, 0.5);
imageData = bwlabel(imageData, 4);
% #################### RECOGNITION LOOP ################
% Relabel everything because it might be rotated
imageLetters = imfeature(imageData,'Image', 'Area', 'EulerNumber', 'BoundingBox');
imageNumLetters = max(imageData(:));
% Load recognizable letter data from file(s)
letterName(1) = 'o';
letterName(2) = 'e';
letterName(3) = 'a';
letterName(4) = 'u';
load 'o.mat' meanO covO
letterObj(1) = struct('Name', 'o', ...
'Mean', meanO, ...
'Covariance', covO);
load 'e.mat' meanE covE
letterObj(2) = struct('Name', 'e', ...
'Mean', meanE, ...
'Covariance', covE);
load 'a.mat' meanA covA
letterObj(3) = struct('Name', 'a', ...
'Mean', meanA, ...
'Covariance', covA);
load 'u.mat' meanU covU
letterObj(4) = struct('Name', 'u', ...
'Mean', meanU, ...
'Covariance', covU);
% Save a copy of a colormap
cmap = colormap;
% A spinning tick mark indicates that processing is underway
tickArray = [ '-' '/' '|' '\' '-' '/' '|' '\' ];
% Main recognition loop
% newColors will hold the number of colors that was added as a result of splitting
newColors = 0;
% Main recognition loop - go through each letter (or joined sequence of letters)
for k = 1:imageNumLetters,
% Output the tick mark
temp = mod(k,8);
if temp == 0
temp = 8;
end
disp(tickArray(temp));
% It's a single letter
% Compare each letter to be recognized to each letter
matchLetter = letter_compare(letterObj, imageLetters(k));
% Highlight the letter
switch matchLetter
case 1, palentry = [ 0.0 0.0 1.0 ];
case 2, palentry = [ 1.0 0.0 1.0 ];
case 3, palentry = [ 1.0 0.0 0.0 ];
case 4, palentry = [ 0.0 1.0 0.0 ];
otherwise, palentry = [ 0 0 0 ];
end
cmap(k,:) = palentry;
end
% End main recognition loop
% Shift the data values up to make room for black background
imageData = imageData +1;
% Add the black color into the palette
tempMap = [ 1,1,1; cmap ];
% Show results
figure;
image(imageData);
colormap(tempMap);