03.07.2013 Views

Magnus Bjernstad Klas Josephson

Magnus Bjernstad Klas Josephson

Magnus Bjernstad Klas Josephson

SHOW MORE
SHOW LESS

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Igenkänning av handskrivna siffror med hjälp av<br />

basbilder<br />

Sammanfattning<br />

I den här artikeln visar vi ett exempel p˚a en Matlabimplementation<br />

av identifiering av handskrivna siffror<br />

genom att utnyttja basbilder skapade fr˚an ett<br />

inlärningsmaterial. Dessutom visas en algoritm för<br />

identifiering av hur rader löper i en sida med okända<br />

tecken.<br />

1 Nyckelord<br />

OCR, handskrivna siffror, basbilder, raddetektion<br />

2 Introduktion<br />

Vid m˚anga tillfällen är det intressant att kunna tolka<br />

handskrivna tecken och överföra dessa p˚a textform,<br />

till exempel vid inmatning av text och siffror i handdatorer.<br />

Det finns flera metoder för att konstruera<br />

ett program som utför denna uppgift. Vi har använt<br />

oss av basbilder och jämför sedan de okända objekten<br />

med dessa för att avgöra vilken siffra som tecknet ska<br />

identifieras som.<br />

3 Metod<br />

Segmentering av siffror<br />

Inledningsvis trösklas inbilden s˚a att en binär bild<br />

erh˚alls. Ströpixlar tas bort med hjälp av att morfologiskt<br />

öppna bilden med ett 2 × 2-pixlars strukturelement.<br />

För att koppla samman siffror som best˚ar<br />

av mer än ett sammanhängande omr˚ade dilateras<br />

därefter bilden med ett kvadratiskt strukturelement<br />

med ˚atta pixlars bredd.<br />

Nästa delproblem best˚ar i att fr˚an en bild med siffror<br />

identifiera sammanhängande omr˚aden som vardera<br />

inneh˚aller en siffra. Genom att söka linjärt pixel<br />

för pixel i bilden hittas en punkt där en siffra finns.<br />

Utg˚aende fr˚an denna punkten lokaliseras alla andra<br />

punkter i en 8-sammanhängande väg. För att inte<br />

behöva arbeta vidare med den morfologiskt modifierade<br />

bilden maskas motsvarande pixlar ut fr˚an originalbilden.<br />

<strong>Klas</strong> <strong>Josephson</strong> F-00<br />

E-mail: f00kj@efd.lth.se<br />

<strong>Magnus</strong> <strong>Bjernstad</strong> F-00<br />

E-mail: f00mbj@efd.lth.se<br />

Handledare: Jacob Sternby<br />

För att förenkla sökningen efter nästa siffra raderas<br />

den utsegmenterade siffran fr˚an originalbilden.<br />

P˚a s˚a sätt kan nästa siffra ocks˚a hittas genom linjär<br />

sökning.<br />

Homogenisering av siffror<br />

Att göra de olika utsegmenterade siffrorna s˚a lika som<br />

möjligt torde bidra till ett bättre resultat. Särskilt<br />

med avseende p˚a basbildsmetoden finns det vissa<br />

fördelar med att göra bilderna invarianta under lutning<br />

i originalbilden. Tv˚a ettor som skrivits med olika<br />

lutning rätas därför upp.<br />

Detta kan uppn˚as genom att l˚ata nedre vänstra<br />

hörnet av siffran motsvara origo. För att avgöra hur<br />

mycket siffran ska roteras identifieras masscentrum<br />

varp˚a bilden translateras s˚a att masscentrum hamnar<br />

i origo. Därefter bildas matrisen A med tv˚a kolonner<br />

och med lika m˚anga rader som antal pixlar som är<br />

icke-noll. Varje rad inneh˚aller koordinaterna till motsvarande<br />

pixel. Egenvektorerna till A T A ger d˚a tv˚a<br />

huvudriktningar för bilden med tillhörande egenvektorer.<br />

Bilden vrids s˚a mycket att egenvektorn som hör<br />

till det största egenvärdet blir vertikal.<br />

För att de segmenterade siffrorna ska kunna<br />

jämföras krävs att de är lika stora. Varje inbild skalas<br />

till 30×20 pixlar oberoende om inbilden inte har dessa<br />

proportioner. Resultat av olika skalningsmetoder<br />

ses i figur 1. Om denna skalning inte utförs innan bilden<br />

roteras s˚a riskeras att en felaktig huvudriktning<br />

för platta bilder identifieras.<br />

2 2<br />

2 2<br />

2<br />

2<br />

Figur 1: Skillnaden mellan skalning d˚a proportionerna<br />

bibeh˚alls (vänstra halvan) och d˚a proportionerna inte<br />

bibeh˚alls (högra halvan).


Generering av basbilder<br />

De m stycken bilderna varifr˚an basbilder ska genereras<br />

läses in i en matris<br />

⎛<br />

⎞<br />

x11 x21 . . . xm1<br />

⎜ x12 x22 . . . xm2 ⎟<br />

A ′ ⎜<br />

= ⎜<br />

⎝<br />

.<br />

.<br />

. ..<br />

.<br />

x1n x2n . . . xmn<br />

⎟<br />

⎠ ,<br />

där varje kolumn är en radstaplad bild med n pixlar.<br />

För att kunna generera relevanta basbilder m˚aste alla<br />

inbilder viktas lika, dvs. varje kolonn i A’ ska normeras<br />

enligt<br />

A·i = A′ ·i<br />

||A ′ ·i ||1<br />

.<br />

Kovariansmatrisen C = AA T beskriver variationen<br />

mellan de enskilda pixlarna i bilderna. Egenbilderna<br />

är egentligen egenvektorer till denna kovariansmatris.<br />

Denna är dock av storlek n × n vilket för stora inbilder<br />

är alltför tungt att beräkna numeriskt. Istället<br />

beräknas egenvektorerna till L = A T A som endast<br />

är av storlek m × m - i de flesta fall ett numeriskt<br />

betydligt enklare problem. L˚at V vara en matris med<br />

egenvektorerna till matrisen L i dess kolonner. D˚a kan<br />

m stycken egenvektorer ui till kovariansmatrisen C,<br />

hörande till de m största egenvärdena, beräknas som<br />

en linjärkombination av de radstaplade bilderna i A.<br />

Detta l˚ater sig lätt göras med en matrismultiplikation<br />

U = AV , där kolonnerna i U är egenvektorerna ui.<br />

Efter en normering f˚as de sökta basbilderna.<br />

De mest intressanta basbilderna är de med störst<br />

tillhörande egenvärde. Med hjälp av endast ett f˚atal<br />

av dessa kan bilderna i A approximativt ˚aterskapas<br />

med gott resultat. Huvudsyftet med utnyttjandet av<br />

basbilder är att oidentifierade siffror kan approximeras<br />

med dessa basbilder.<br />

Figur 2 visar fyra basbilder för tv˚aor. Där syns<br />

det att de sista bilderna är betydligt brusigare än de<br />

första.<br />

Figur 2: Basbild nummer ett, tv˚a, 99 och 100 för<br />

tv˚aor.<br />

Identifikation av okända siffror<br />

När väl basbilderna är konstruerade är det enkelt<br />

att avgöra vilken siffra en viss bild inneh˚aller.<br />

Skalärprodukterna mellan en okänd bild och alla basbilderna<br />

för en viss siffra beräknas. Normen av dessa<br />

koordinater blir d˚a ett m˚att p˚a hur lik bilden är denna<br />

siffra.<br />

Raddetektion<br />

För att kunna använda sifferigenkänningen krävs en<br />

presentation av identifikationen som tar hänsyn till<br />

siffrornas inbördes placering. Detta innebär i praktiken<br />

att en rimlig raddetektion m˚aste utföras. Figur<br />

3 visar principen för en s˚adan algoritm. Steg ett<br />

3 4 5<br />

Figur 3: Princip för raddetektion.<br />

är att genom linjär sökning hitta en pixel i den högst<br />

belägna siffran p˚a första raden. Detta motsvaras att<br />

den mittersta siffran i figuren. Utg˚aende fr˚an denna<br />

siffra söks sedan inledningsvis ˚at vänster i dess<br />

förlängning till dess att nästa pixel p˚aträffas. Proceduren<br />

upprepas fram till vänsterkanten p˚a bilden.<br />

Samma sak utförs˚at höger utg˚aende fr˚an startsiffran.<br />

När en siffra är behandlad raderas den fr˚an urbilden<br />

s˚a att den inte p˚aträffas igen.<br />

4 Experiment<br />

Implementation av ett program som identifikation av<br />

handskrivna siffror var den praktiska delen av v˚art<br />

projekt. Programmet är skrivet i Matlab förutom en<br />

beräkningstung funktion skriven i C. Programkoden<br />

är bifogad i appendix A.<br />

Användning av programmet<br />

Programmet kan använda sig av olika användare för<br />

att kunna identifiera siffror skrivna av en viss person<br />

med särskilda särdrag i sin handstil. För att<br />

kunna använda programmet m˚aste inledningsvis basbilder<br />

genereras fr˚an ett inlärningsmaterial. Dessa<br />

inlärningsbilder inneh˚aller vardera endast en typ av<br />

siffra och ska vara i PNG-format med namn enligt<br />

formen img0001.png, img0002.png, . . . . Bilder med<br />

en typ av siffror placeras under motsvarande underkatalog<br />

till respektive användare (se figur 4).<br />

För att skapa basbilder fr˚an inlärningsbilderna<br />

till en viss användare körs kommandot<br />

setupUser(user), där user är en sträng med<br />

användarnamnet. Basbilderna som skapas läggs<br />

i filerna baseImages/user/baseImages0.dat,<br />

baseImages/user/baseImages1.dat, . . . .


|--sourceImages/<br />

| |--user1/<br />

| | |--0/<br />

| | :<br />

| | |--9/<br />

| |--user2/<br />

| |--0/<br />

| :<br />

| |--9/<br />

|--segmentedImages/<br />

| |--user1/<br />

| |--user2/<br />

| :<br />

|--baseImages/<br />

|--user1/<br />

|--user2/<br />

:<br />

Figur 4: Katalogstruktur för programmet.<br />

Att identifiera okända siffror görs med kommandot<br />

identifyImage(image, user, ’plot’). image<br />

anger en sökväg till en bildfil med siffror. Siffrorna<br />

m˚aste vara mörka och bakgrunden ljus. Anges det frivilliga<br />

argumentet ’plot’ visas identifikationen grafiskt<br />

i realtid.<br />

Med programmet bifogas kompilerade versioner av<br />

getContiguous.c för Sun (getContiguous.mexol)<br />

och för Windows (getContiguous.dll). Om<br />

programmet ska köras p˚a en annan plattform<br />

behöver getContiguous.c kompileras med Matlabkommandot<br />

mex getContiguous.c.<br />

Testresultat<br />

I v˚ara testar har vi använt tre olika användare; <strong>Magnus</strong>,<br />

<strong>Klas</strong> och Both. Basbilderna till användaren Both<br />

är genererade fr˚an b˚ade <strong>Magnus</strong> och <strong>Klas</strong> siffror. Tabell<br />

1 och tabell 2 visar hur stor procentandel rätt<br />

som f˚as vid identifiering av cirka 200 siffror av varje<br />

slag.<br />

Resultaten visar att det är en framkomlig väg<br />

att identifiera siffrorna med hjälp av basbilder. Vid<br />

identifikation av användarens egna siffror erhölls en<br />

träffsäkerhet p˚a nästan 100%. Noteras bör att denna<br />

träffsäkerhet knappt sjunker när basbilder som skapats<br />

fr˚an b˚adas siffror används. Intressant vore att<br />

undersöka hur dessa resultat förändras med fler samtidiga<br />

användare.<br />

Att identifiera siffror med basbilder skapade fr˚an en<br />

annan persons siffror ger överlag mycket d˚aligt resultat.<br />

Detta är väntat eftersom det inte krävs särskilt<br />

stor förändring av en handstil för att basbildernas<br />

mest signifikanta delar ska förskjutas till omr˚aden<br />

med tidigare mycket l˚ag signifikans.<br />

Raddetektionen ger i allmänhet korrekta resultat.<br />

Även kraftigt lutande rader hanteras väl. I vissa extremfall<br />

kan ordningen p˚a raderna kastas om.<br />

<strong>Magnus</strong> <strong>Klas</strong> Both<br />

0 100 86 100<br />

1 100 31 100<br />

2 99 38 96<br />

3 100 38 96<br />

4 97 6 96<br />

5 100 89 100<br />

6 99 100 100<br />

7 95 21 98<br />

8 98 92 98<br />

9 97 41 99<br />

Totalt 99 54 98<br />

Tabell 1: Procentandel rätt vid test p˚a <strong>Magnus</strong> siffror<br />

med basbilder genererade med de tre användarna<br />

<strong>Magnus</strong>, <strong>Klas</strong> och Both, där Both använder b˚adas<br />

inlärningsbilder.<br />

<strong>Klas</strong> <strong>Magnus</strong> Both<br />

0 100 100 100<br />

1 100 1 100<br />

2 99 58 97<br />

3 98 81 98<br />

4 91 0 89<br />

5 99 86 100<br />

6 98 97 98<br />

7 100 0 96<br />

8 98 92 99<br />

9 98 38 94<br />

Totalt 98 55 97<br />

Tabell 2: Procentandel rätt vid test p˚a <strong>Klas</strong> siffror<br />

med basbilder genererade med de tre användarna<br />

<strong>Klas</strong>, <strong>Magnus</strong> och Both, där Both använder b˚adas<br />

inlärningsbilder.<br />

Möjliga förbättringar<br />

Önskvärt vore att ha en metod för att avgöra om ett<br />

tecken inte är en siffra. Det enda vi använder oss av<br />

för att urskilja felaktiga tecken är en storlekskontroll<br />

för att hindra alltför sm˚a omr˚aden att tolkas som siffror.<br />

En möjligt förbättring vad gäller klassificeringen är<br />

att utveckla en mer sofistikerad metod för att analysera<br />

de koordinater som f˚ar efter skalärprodukt mellan<br />

den okända bilden och basbilderna. I nuläget avgörs<br />

vilken siffra det är av normen av koordinaterna. En<br />

utveckling av detta är att även ta hänsyn till hur<br />

koordinaterna varierar mellan de olika möjliga siffrorna.<br />

Vidare skulle man kunna göra noggrannare<br />

undersökningar p˚a siffror som ofta ger resultat nära<br />

varandra, främst fyror och nior. Detta skulle kunna<br />

ske genom att man även tittar p˚a specifika särdrag för<br />

siffror. Ett s˚adant exempel är att studera hur m˚anga<br />

h˚al en siffra har. Där skulle fyror i normalfallet ge noll<br />

h˚al, nior ge ett h˚al och ˚attor ge tv˚a h˚al.<br />

Om man skulle använda sig av flera metoder är en


idé att vikta dessa vid klassificeringen s˚a att metoder<br />

som är bra p˚a att till exempel skilja mellan tv˚a<br />

specifika siffror endast har signifikans i detta fall.<br />

Sammanfattning<br />

Vi har i denna rapport gett ett exempel p˚a hur en<br />

implementation av identifiering av handskrivna siffror<br />

kan se ut. V˚ara resultat visar p˚a goda resultat<br />

när basbildsmetoden används. Bra resultat erh˚alls<br />

även när antalet personer som basbilderna bygger p˚a<br />

utökas till tv˚a. Hur detta utvecklas med ännu fler<br />

personer är n˚agot som skulle vara intressant att undersöka.<br />

Speciellt hur bra det d˚a skulle fungera p˚a en<br />

person som inte varit med och genererat basbilderna.


A m-filer<br />

setupUser<br />

function utbild = setupUser(user)<br />

% Segmentera alla bilder som ligger i katalogen<br />

% sourceImages/{user}/[0:9] samt skapa basbilder<br />

% fr˚an dessa.<br />

t = cputime;<br />

for number=0:9<br />

% Segmentera ut siffrorna<br />

saveSegmentedImages(user , number);<br />

% Spara motsvarande basbilder<br />

saveBaseImages(user, number);<br />

end<br />

disp(sprintf(’Total tid: %.2f s’, cputime-t));<br />

saveSegmentedImages<br />

function saveSegmentedImages(user, number)<br />

% Segmentera ut siffrorna som finns i bilderna som ligger i<br />

% katalogen sourceImages/{user}/{number}/. Bilderna sparas i filen<br />

% ’segmentedImages/{user}/segmentedImages{number}.dat’<br />

% Sätt filnamn<br />

imageName = sprintf(’sourceImages/%s/%d/img0001.png’, user, number);<br />

imageNumber = 1;<br />

% Öppna fil för lagring av segmenterade bilder<br />

fid = fopen(sprintf(’segmentedImages/%s/segmentedImages%d.dat’, ...<br />

user, number), ’w’);<br />

% Skriv storleken p˚a varje segmenterad bild<br />

fwrite(fid, [30, 20], ’ubit8’);<br />

fclose(fid);<br />

fid = fopen(sprintf(’segmentedImages/%s/segmentedImages%d.dat’, ...<br />

user, number), ’a’);<br />

% G˚ar igenom alla bilder som tillhör ’user’<br />

while (exist(imageName))<br />

originalImage = imread(imageName);<br />

% Tröskla<br />

originalImage = originalImage < 200;<br />

% Ta bort sm˚a skräp samt dilatera ihop siffror<br />

currentImage = imopen(originalImage, ones(2));<br />

currentImage = imdilate(currentImage, ones(8));<br />

% Koordinater för var nästa bild att extrahera finns<br />

imagePosition = findFirst(currentImage, [1 1]);<br />

% Extrahera denna bilden och uppdatera ursprungsbilden<br />

[foundDigitImage, currentImage] = ...<br />

getImage(currentImage, imagePosition, originalImage);<br />

% G˚a igenom alla siffror i nuvarande bild<br />

while imagePosition ~= -1<br />

% Spara aktuell bild till fil om bilden är tillräckligt stor<br />

if (foundDigitImage ~= -1)


fwrite(fid, foundDigitImage(:), ’ubit1’);<br />

end<br />

% Hitta första icke-nollpositionen i bilden<br />

imagePosition = findFirst(currentImage, imagePosition);<br />

% Segmentera ny siffra om det finns n˚agon<br />

if (isempty(imagePosition) == 0)<br />

[foundDigitImage, currentImage] = ...<br />

getImage(currentImage, imagePosition, originalImage);<br />

end<br />

end<br />

% Sätt namn p˚a nästa fil<br />

imageNumber = imageNumber + 1;<br />

imageName = ...<br />

sprintf(’sourceImages/%s/%d/img%0.4d.png’, user, number, imageNumber);<br />

end<br />

% Stäng filen för lagring av segmenterade siffror<br />

fclose(fid);<br />

disp(sprintf(’Segmenteringen för %d:orna klara’, number));<br />

function updatedPosition = findFirst(inPic, currentPosition)<br />

% Hittar index [row, col] för första elementet i ’inPic’<br />

% vars värde är icke-noll. Returnerar en tom matris om<br />

% alla element är noll.<br />

[nRows nCols] = size(inPic);<br />

for i = currentPosition(1):nRows<br />

for j = 1:nCols<br />

if (inPic(i,j) ~= 0)<br />

updatedPosition = [i j];<br />

return;<br />

end<br />

end<br />

end<br />

updatedPosition = [];<br />

saveBaseImages<br />

function saveBaseImages(user, number)<br />

% Skapa basbilder för siffra ’number’ till de segmenterade<br />

% siffrorna som tillhör ’user’.<br />

% Läs in bilderna som basbilderna ska baseras p˚a. Varje bild lagras<br />

% som en (radstaplad) kolonnvektor i matrisen A.<br />

fid = fopen(sprintf(’segmentedImages/%s/segmentedImages%d.dat’, ...<br />

user, number), ’r’);<br />

% Konrollera om bilder finns<br />

if fid == -1<br />

disp(’Inga segmenterade siffror finns’)<br />

return<br />

end<br />

imageSize = fread(fid, 2, ’ubit8’);<br />

A = fread(fid, [prod(imageSize), inf], ’ubit1’);<br />

fclose(fid);<br />

% Normera kolonnerna i A i 1-normen<br />

A = A ./ repmat(sum(A), size(A, 1), 1);


% Att hitta egenvektorer till kovariansmatrise C=A*A’ är f˚ar sv˚art<br />

% -> studera istället egenvektorer till A’*A (som bara är av<br />

% storlek nPictures*nPictures).<br />

L = A’*A;<br />

[V, eigValues] = eig(L);<br />

% Gör egenvärdena till en vektor istället för en matris<br />

eigValues = diag(eigValues);<br />

% Egenvektorerna U till C kan nu bildas som en linjärkombination av<br />

% bilderna i kolonnerna hos A<br />

U = A*V;<br />

% Sortera egenvektorerna efter storleken p˚a motsvarande egenvärde<br />

[sortedEig eigOrder] = sort(eigValues);<br />

eigOrder = flipud(eigOrder);<br />

U=U(:,eigOrder);<br />

% Normera egenvektorerna<br />

U = U ./ repmat(sqrt(sum(U.^2)), size(U, 1), 1);<br />

% Spara basbilderna.<br />

% Basbilderna sparas i en fil där de första tv˚a talen anger<br />

% storleken p˚a varje bild.<br />

fid = fopen(sprintf(’baseImages/%s/baseImages%d.dat’, user, number), ...<br />

’w’, ’ieee-be’);<br />

% Skriv storleken p˚a varje basbild<br />

fwrite(fid, imageSize, ’ubit8’);<br />

% Skriv basbilderna och stäng filen<br />

fwrite(fid, U(:), ’float32’);<br />

fclose(fid);<br />

getImage<br />

function [digit, updatedImage, digitRect] = getImage(inPic, startPos, originalpic)<br />

% Hittar sammanhängande bild i ’inPic’ med början i ’startPos’.<br />

% Bilden lagras i ’digit’ och den funna siffran ’raderas’ i<br />

% ursprungsbilden och den nya, uppdaterade bilden sparas i<br />

% ’updatedImage’. ’digit’ blir -1 om en för liten bild hittats.<br />

% Extrahera pixlar i ett 8-sammanhängande omr˚ade kring ’startPos’.<br />

% ’updatedImage’ är en bild med detta omr˚ade satt till noll.<br />

% ’digit’ är en {antal pixlar}x2-vektor som inneh˚aller koordinater<br />

% för pixlarna som utgör siffran.<br />

[updatedImage, digit] = getContiguous(inPic, startPos(1), startPos(2));<br />

% Konvertera listan över ing˚aende pixlar till en sparse-matris<br />

digit = sparse(double(digit(:,1)), double(digit(:,2)), 1);<br />

% Plocka ut motsvarande pixlar ur originalbilden<br />

[row,col]=find(digit);<br />

orgDigit=originalpic(min(row):max(row), min(col):max(col));<br />

dilatDigit=digit(min(row):max(row), min(col):max(col));<br />

digit = full(orgDigit & dilatDigit);<br />

% Spara koordinatinformation om den funna bilden<br />

digitRect = [min(row) max(row) min(col) max(col)];<br />

% Skala om bilden till 30x20 om den är tillräckligt stor


if (max(size(digit)) > 20)<br />

digit = resizeDigitImage(digit);<br />

else<br />

digit = -1;<br />

return<br />

end<br />

% Rotera bilden s˚a att egenriktningen pekar rakt upp˚at<br />

digit = rotateImageAlongEigenDirection(digit);<br />

% Ändra storlek igen till 30x20 pixlar<br />

digit = resizeDigitImage(digit);<br />

function outPic = resizeDigitImage(inPic)<br />

% Skala om ’inPic’ s˚a att den täcker 30x20 pixlar. Ta inte hänsyn<br />

% till bildförh˚allandet.<br />

outPicSize = [30 20];<br />

[row col] = find(inPic);<br />

digitRect = [min(row) max(row) min(col) max(col)];<br />

inPic = inPic(digitRect(1):digitRect(2), digitRect(3):digitRect(4));<br />

outPic = imresize(inPic, outPicSize, ’nearest’);<br />

function outPic = rotateImageAlongEigenDirection(inPic)<br />

% Rotera bilden ’inPic’ s˚a att huvudriktningen g˚ar vertikalt.<br />

% Skifta origo till masscentrum av bilden<br />

[nRows nCols] = size(inPic);<br />

[ARows ACols] = find(inPic);<br />

centerOM = centerOfMass(inPic);<br />

ARows = ARows - centerOM(1);<br />

ACols = ACols - centerOM(2);<br />

A = [ARows ACols];<br />

% Beräkna egenvektorerna och egenvärdena. Plocka ut egenvektorn<br />

% hörande till det största egenvärdet.<br />

[eigVec eigValue] = eig(A’*A);<br />

[maxEigenValue maxIndex] = max(diag(eigValue));<br />

eigVec = eigVec(:,maxIndex);<br />

% Räkna ut vilken vinkel bilden ska vridas<br />

if (abs(eigVec(1)) < eps)<br />

theta = pi/2;<br />

else<br />

theta = atan(eigVec(2)/eigVec(1));<br />

end<br />

% Rotera bilden<br />

outPic = imrotate(inPic, -rad2deg(theta));<br />

function centerOfMass = centerOfMass(inPic)<br />

[M, N] = size(inPic);<br />

weightX = sum(inPic, 1);


weightY = sum(inPic, 2);<br />

meanX = weightX *(1:N)’/sum(weightX);<br />

meanY = weightY’*(1:M)’/sum(weightY);<br />

centerOfMass = [meanY meanX];<br />

getContiguous<br />

#include <br />

#include "mex.h"<br />

#include "matrix.h"<br />

struct list {<br />

int row;<br />

int col;<br />

struct list *next;<br />

struct list *prev;<br />

};<br />

typedef struct list item;<br />

mxArray *getContiguous(mxLogical *image, int nRows, int nCols, int startRow, int startCol) {<br />

/* Variabeldeklarationer */<br />

item *pixel, *pixelHead;<br />

item *toCheck, *toCheckHead, *toCheckTail;<br />

int currRow, currCol;<br />

int i,j, nPixels;<br />

mxArray *pixels;<br />

int pixelsDim[2];<br />

int *data;<br />

/* Initiera den nuvarande positionen till startpositionen */<br />

currRow = startRow;<br />

currCol = startCol;<br />

nPixels = 0;<br />

pixelHead = NULL;<br />

toCheck = (item *)mxCalloc(1, sizeof(item));<br />

toCheck->row = startRow;<br />

toCheck->col = startCol;<br />

toCheck->next = NULL;<br />

toCheck->prev = NULL;<br />

toCheckHead = toCheck;<br />

toCheckTail = toCheck;<br />

while(toCheckTail) {<br />

/* Utg˚a fr˚an första pixeln i listan över pixlar att g˚a igenom */<br />

currRow = toCheckTail->row;<br />

currCol = toCheckTail->col;<br />

/* Stega igenom alla grannar till den nuvarande positionen */<br />

for (i = currCol-1; i = 0 && i < nCols && image[nRows*i + j] == 1) {<br />

/* ...gör den svart,... */<br />

image[nRows*i + j] = 0;


}<br />

}<br />

/* ...spara pixelpositionen för den vita pixeln... */<br />

pixel = (item *)mxCalloc(1, sizeof(item));<br />

pixel->row = j;<br />

pixel->col = i;<br />

pixel->next = pixelHead;<br />

pixel->prev = NULL;<br />

pixelHead = pixel;<br />

nPixels++;<br />

/* ...samt spara den nya pixeln som kand. för ytterligare utvidgning. */<br />

toCheck = (item *)mxCalloc(1, sizeof(item));<br />

toCheck->row = j;<br />

toCheck->col = i;<br />

toCheck->next = NULL;<br />

toCheck->prev = toCheckHead;<br />

toCheckHead->next = toCheck;<br />

toCheckHead = toCheck;<br />

}<br />

}<br />

/* Ta bort den precis kontrollerade<br />

pixeln fr˚an kandidater till ytterligare utvidgning */<br />

toCheckTail = toCheckTail->next;<br />

}<br />

pixelsDim[0] = nPixels;<br />

pixelsDim[1] = 2;<br />

pixels = mxCreateNumericArray(2, pixelsDim, mxINT32_CLASS, mxREAL);<br />

data = mxCalloc(nPixels*2, sizeof(int));<br />

i = 0;<br />

pixel = pixelHead;<br />

while (pixel) {<br />

data[i] = pixel->row + 1;<br />

data[nPixels + i] = pixel->col + 1;<br />

pixel = pixel->next;<br />

i++;<br />

}<br />

mxSetData(pixels, data);<br />

return pixels;<br />

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {<br />

unsigned int *pixelList;<br />

mxLogical *image;<br />

mxLogical *updatedImage;<br />

int i,n;<br />

double *startRow, *startCol;<br />

image = mxGetLogicals(prhs[0]);<br />

startRow = mxGetPr(prhs[1]);<br />

startCol = mxGetPr(prhs[2]);


}<br />

/* Skapa en array som inneh˚aller en uppdaterad bild med den<br />

utsegmenterade delen raderad */<br />

plhs[0] = mxCreateNumericArray(mxGetNumberOfDimensions(prhs[0]),<br />

mxGetDimensions(prhs[0]),<br />

mxLOGICAL_CLASS, mxREAL);<br />

updatedImage = mxGetData(plhs[0]);<br />

/* Konvertera alla element i inbilden till heltal */<br />

n = mxGetNumberOfElements(prhs[0]);<br />

for (i=0; i < n; i++) {<br />

updatedImage[i] = (bool) image[i];<br />

}<br />

plhs[1] =<br />

(mxArray *) getContiguous(updatedImage,<br />

mxGetM(prhs[0]),<br />

mxGetN(prhs[0]),<br />

(int)*startRow,<br />

(int)*startCol);<br />

identifyImage<br />

function result = identifyImage(file, user, doPlot)<br />

% Identifierar siffror i bilden ’file’ med basbilder som tillhör<br />

% användaren ’user’. Om ’doPlot’ sätts till ’plot’ f˚as en bild där<br />

% identifikationen överlagras p˚a bilden ’file’.<br />

% Sätt doPlot till false ifall den ej initieras vid anrop<br />

if (nargin < 3)<br />

doPlot = false;<br />

end<br />

% Kontrollera att användaren finns<br />

if (exist(sprintf(’baseImages/%s/baseImages0.dat’, user)) == false)<br />

disp(’Användaren finns inte’);<br />

return;<br />

end<br />

% Intitiera nödvändiga variabler<br />

result = ’’;<br />

digitRect = [1 1 1 1];<br />

% Kotrollera om filen existerar<br />

if (exist(file) == false)<br />

disp(’Filen ej funnen’)<br />

return;<br />

end<br />

% Läser in bild<br />

originalImage = imread(file);


% Konvertera till gr˚askala<br />

if (size(originalImage, 3) == 3)<br />

originalImage = rgb2gray(originalImage);<br />

end<br />

% Tröskla, öppna, dilatera<br />

originalImage = originalImage < 200;<br />

currentImage = imopen(originalImage, ones(2));<br />

currentImage = imdilate(currentImage, ones(8));<br />

% Plotta bilden om det är valt<br />

if (doPlot == ’plot’)<br />

close;<br />

imshow(not(originalImage));<br />

pause(0.1);<br />

hold on;<br />

end<br />

% Koordinater för var nästa bild att extrahera ’börjar’<br />

imagePosition = findFirst(currentImage);<br />

% Stega igenom inbilden tills inga fler rader p˚aträffas<br />

while (imagePosition ~= -1)<br />

% Initiera raden med identifierade siffror<br />

identifiedRow = [];<br />

% Stega ˚at vänster<br />

direction = -1;<br />

% G˚a igenom en rad i ursprungsbilden<br />

while (imagePosition ~= -1)<br />

% Extrahera siffran<br />

[foundDigitImage, currentImage, newDigitRect] = ...<br />

getImage(currentImage, imagePosition, originalImage);<br />

% Behandla bara bilder som är tillräckligt stora<br />

if (foundDigitImage ~= -1)<br />

digitRect = newDigitRect;<br />

% Spara första identifierade siffran i raden<br />

if (isempty(identifiedRow))<br />

rowStartRect = digitRect;<br />

end<br />

% Normalisera intensiteten<br />

foundDigitImage = foundDigitImage/sum(foundDigitImage(:));<br />

% Identifiera siffran<br />

idNumber = identifyImageByBaseImages(foundDigitImage, user);<br />

% Plotta identifikationen om det valts<br />

if (doPlot == ’plot’)<br />

text(imagePosition(2),imagePosition(1),num2str(idNumber), ...<br />

’color’,’k’, ’backgroundcolor’, ’c’);<br />

pause(0.0001)<br />

end<br />

% Uppdatera raden med identifierade siffror


end<br />

if (direction == -1)<br />

identifiedRow = [idNumber identifiedRow];<br />

else<br />

identifiedRow = [identifiedRow idNumber];<br />

end<br />

end<br />

% Leta upp nästa siffra i raden<br />

imagePosition = findNextInRow(currentImage, digitRect, direction);<br />

% Om det inte finns fler siffror ˚at vänster - ändra<br />

% sökriktning<br />

if (isequal(imagePosition, -1) && direction == -1 && ...<br />

isequal(foundDigitImage,-1) == false)<br />

direction = 1;<br />

imagePosition = findNextInRow(currentImage, rowStartRect, direction);<br />

end<br />

end<br />

% Spara den identifierade raden (om den inte är tom)<br />

if (isempty(identifiedRow) == false)<br />

result = sprintf(’%s%s\n’, result, num2str(identifiedRow));<br />

end<br />

% Hitta nästa siffra (som nu är p˚a en ny rad)<br />

imagePosition = findFirst(currentImage);<br />

function firstIndex = findFirst(inPic)<br />

% Hittar index [row, col] för första elementet i ’inPic’<br />

% vars värde är icke-noll. Returnerar en tom matris om<br />

% alla element är noll.<br />

nRows = size(inPic,2);<br />

% Hitta första icke-noll elementet. Linjärt index.<br />

firstIndex = min(find(inPic’));<br />

% Konvertera linjärt index till [row col]-format<br />

firstIndex = [ceil(firstIndex/nRows) mod(firstIndex,nRows)];<br />

function updatedPosition = findNextInRow(inPic, digitRect, direction)<br />

% Söker i riktningen ’direction’ efter nästa pixel som är<br />

% icke-noll. Index för denna pixel lagras i ’updatedPosition’.<br />

% direction == 1 => höger<br />

% direction == -1 => vänster<br />

% Kontrollera riktning<br />

if (direction == 1)<br />

col = digitRect(4);<br />

else<br />

col = digitRect(3);<br />

end<br />

% Sök efter nästa till man n˚att kanten p˚a bilden<br />

while (sum(inPic(digitRect(1):digitRect(2), col)) == 0)<br />

col = col + direction;


% Kolla om vi g˚att utanför bilden<br />

if (col == 0 || col > size(inPic, 2))<br />

% Reurnera -1 d˚a raden är slut<br />

updatedPosition = -1;<br />

return<br />

end<br />

end<br />

% Returnera position för nästa siffra<br />

updatedPosition = ...<br />

[min(find(inPic(digitRect(1):digitRect(2), col))) + digitRect(1) - 1 ...<br />

col];<br />

identifyImageByBaseImages<br />

function idNumber = identifyImageByBaseImages(inPic, user)<br />

% Avgör vilken siffra bilden ’inPic’ inneh˚aller baserat p˚a<br />

% jämförelse med basbilder tillhörande användaren ’user’.<br />

maxNorm = 0;<br />

nUsedBaseImages = 20;<br />

for number = 0:9;<br />

% Läs in basbilderna<br />

fid = fopen(sprintf(’baseImages/%s/baseImages%d.dat’, user, number), ...<br />

’r’, ’ieee-be’);<br />

% Kontrollera om basbilder finns<br />

if fid == -1<br />

disp(’Basbilder saknas’);<br />

return<br />

end<br />

baseImageSize = fread(fid, 2, ’ubit8’);<br />

baseImages = fread(fid, [prod(baseImageSize), nUsedBaseImages], ’float32’);<br />

fclose(fid);<br />

% Radstapla<br />

currentImage = inPic(:);<br />

% Beräkna skalärprodukterna mellan currentImage och alla<br />

% basebilderna för en given siffra.<br />

coordinates = currentImage’*baseImages;<br />

% Identifiera siffran som den som har störst 2-norm av<br />

% koordinaterna<br />

normCoords(number + 1) = norm(coordinates);<br />

if norm(coordinates) > maxNorm<br />

maxNorm = norm(coordinates);<br />

idNumber = number;<br />

end<br />

end

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!