Igenkänning av handskrivna siffror med hjälp av basbilder



Relevanta dokument
Ett enkelt OCR-system

Miniprojekt: MEX och molekyldynamik

Ansiktsigenkänning med MATLAB

Övning från förra gången: readword

Beräkningsvetenskap föreläsning 2

Laboration 4: Digitala bilder

TANA17 Matematiska beräkningar med Matlab

BINÄRA TRÄD. (X = pekarvärdet NULL): struct int_bt_node *pivot, *ny; X X X 12 X X 12 X X -3 X X

Matematisk modellering fortsättningskurs Visuell variation

Laboration: Grunderna i MATLAB

Programmeringsuppgift Game of Life

Datorlära 6. Arbeta med strängar Inmatning med tangentbordet Bygga ett program med inmatning, funktioner, osv

Programmeringsteknik med C och Matlab

Tentamen, Programmeringsteknik för BME, F och N

MATLAB the Matrix Laboratory. Introduktion till MATLAB. Martin Nilsson. Enkel användning: Variabler i MATLAB. utvecklat av MathWorks, Inc.

Vektorgeometri för gymnasister

Exempel :: Spegling i godtycklig linje.

MATLAB. Python. Det finns flera andra program som liknar MATLAB. Sage, Octave, Maple och...

MMA132: Laboration 2 Matriser i MATLAB

HI1024, Programmering, grundkurs, 8hp KTH STH TENTAMEN. HI1024:TEN2 - Praktisk tentamen Tid: Fredagen den 21 oktober 2011,

Robotarm och algebra

Matriser och linjära ekvationssystem

Länkade listor kan ingå som en del av språket, dock ej i C Länkade listor är ett alternativ till:

Exempel :: Spegling i godtycklig linje.

12. SINGULÄRA VÄRDEN. (u Av) u v

Per-Emil Eliasson, Claes Fälth, Manne Gustafson, Andreas Gustafsson

Laboration 0: Del 2. Benjamin Kjellson Introduktion till matriser, vektorer, och ekvationssystem

Teknisk dokumentation MCIV

Tentamen TANA17 Matematiska beräkningar Provkod: DAT1 Godkänd: 8p av totalt 20p Tid: 14:e januari klockan

Lösningar Datastrukturer TDA

TANA17 Matematiska beräkningar med MATLAB för M, DPU. Fredrik Berntsson, Linköpings Universitet. 9 november 2015 Sida 1 / 28

Enklast att skriva variabelnamn utan ; innehåll och variabelnamn skrivs ut

Laboration: Vektorer och matriser

6. Matriser Definition av matriser 62 6 MATRISER. En matris är ett rektangulärt schema av tal: a 11 a 12 a 13 a 1n a 21 a 22 a 23 a 2n A =

Laboration 1. Grafisk teknik (TNM059) Introduktion till Matlab. R. Lenz och S. Gooran (VT2007)

Datastrukturer och Algoritmer D0041D

MMA132: Laboration 1 Introduktion till MATLAB

Laboration 3. Redovisning Uppgifterna skall vara demonstrerade och godkända av en handledare senast måndag 22/2.

Tentamen OOP

Datalogi för E Övning 3

Introduktion till MATLAB

Linjär algebra. 1 Inledning. 2 Matriser. Analys och Linjär Algebra, del B, K1/Kf1/Bt1. CTH/GU STUDIO 1 TMV036b /2013 Matematiska vetenskaper

Tentamen TANA17 Matematiska beräkningar Provkod: DAT1 Godkänd: 9p av totalt 20p Hjälpmedel: MATLAB

Skriv i mån av plats dina lösningar direkt i tentamen. Skriv ditt kodnummer längst upp på varje blad.

Textsträngar från/till skärm eller fil

Matriser och linjära ekvationssystem

TDIU01 - Programmering i C++, grundkurs

Inlämningsuppgift : Finn. 2D1418 Språkteknologi. Christoffer Sabel E-post: csabel@kth.se 1

Medicinska Bilder, TSBB31. Lab: Mätvärden på Medicinska Bilder

SP:PROG3 HT12 Tenta

Frågorna 1 till 6 ska svaras med ett kryss för varje korrekt påstående. Varje uppgift ger 1 poäng. Använd bifogat formulär för dessa 6 frågor.

Föreläsning 4. Kö Implementerad med array Implementerad med länkad lista Djup kontra bredd Bredden först mha kö

Magnus Nielsen, IDA, Linköpings universitet

Algoritmer, datastrukturer och komplexitet

public static void mystery(int n) { if (n > 0){ mystery(n-1); System.out.print(n * 4); mystery(n-1); } }

Histogram över kanter i bilder

MATLAB Laboration problem med lokala extremvärden

Transformationer i R 2 och R 3

KPP053, HT2016 MATLAB, Föreläsning 2. Vektorer Matriser Plotta i 2D Teckensträngar

Övervakningssystem. -skillnader i bilder. Uppsala Universitet Signaler och System ht Lärare: Mathias Johansson

BMI = (vikt i kg) / (längd i m) 2. Lösningsförslag

Projekt i bildanalys Trafikövervakning

Datorlära 3 Octave Workspace ovh mijlö Skriva text på skärmen Värdesiffror Variabler och typer Strängar Makro Vektorer

Matematisk Modellering

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Värmedistribution i plåt

Lösningar till tentauppgifterna sätts ut på kurssidan på nätet idag kl 19. Omtentamen i Programmering C, 5p, fristående, kväll,

TAIU07 Matematiska beräkningar med Matlab

Variabler. TANA81: Beräkningar med Matlab. Matriser. I Matlab skapas en variabel genom att man anger dess namn och ger den ett värde:

Tentamen i Grundläggande Programvaruutveckling, TDA548

Matriser och vektorer i Matlab

Syftet med den här laborationen är att du skall bli mer förtrogen med följande viktiga områden inom matematisk statistik

Algoritmer, datastrukturer och komplexitet

Medicinska Bilder, TSBB31. Lab3: Mätvärden på Medicinska Bilder

Enkla datatyper minne

Uppgift 1 (grundläggande konstruktioner)

Dagens program. Programmeringsteknik och Matlab. Administrativt. Viktiga datum. Kort introduktion till matlab. Övningsgrupp 2 (Sal Q22/E32)

Tentamen TEN1 HI

Tentamen, EDAA10 Programmering i Java

Matematisk Modellering

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

String [] argv. Dagens Agenda. Mer om arrayer. Mer om arrayer forts. String [] argv. argv är variabelnamnet. Arrayer och Strängar fortsättning

Numeriska Metoder och Grundläggande Programmering för P1, VT2014

Instruktion för laboration 1

Geometriska transformationer

Innehåll. Föreläsning 12. Binärt sökträd. Binära sökträd. Flervägs sökträd. Balanserade binära sökträd. Sökträd Sökning. Sökning och Sökträd

TMV206: Linjär algebra

Labb i Datorsystemteknik och programvaruteknik Programmering av kalkylator i Visual Basic

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

Laboration 3. I1 Programmerade system, HT15

Lösningar till uppgifterna sätts ut på kurssidan och på WebCT (Gamla Tentor) i dag kl 19. Tentamen i Programmering C, 5p, Distans, övriga,

Exempelsamling Assemblerprogrammering

DIAGNOSTISKT PROV. Tid. Hjälpmedel. Antaganden. Rättning. Övrigt. Diagnostiskt Prov. Klockan Inga

Linjära ekvationssystem

Lösningar till uppgifterna sätts ut på kurssidan på nätet i dag kl Omtentamen i Programmering C, Fri, Kväll,

Digitalitet. Kontinuerlig. Direkt proportionerlig mot källan. Ex. sprittermometer. Elektrisk signal som representerar ljud.

TANA17 Matematiska beräkningar med Matlab

Objektorienterad programmering E. Algoritmer. Telefonboken, påminnelse (och litet tillägg), 1. Telefonboken, påminnelse (och litet tillägg), 2

Bildmosaik. Bilddatabaser, TNM025. Anna Flisberg Linne a Mellblom. linme882. Linko pings Universitet

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

Transkript:

Igenkänning av handskrivna siffror med hjälp av basbilder Klas Josephson F-00 E-mail: f00kj@efd.lth.se Magnus Bjernstad F-00 E-mail: f00mbj@efd.lth.se Handledare: Jacob Sternby Sammanfattning I den här artikeln visar vi ett exempel på en Matlabimplementation av identifiering av handskrivna siffror genom att utnyttja basbilder skapade från ett inlärningsmaterial. Dessutom visas en algoritm för identifiering av hur rader löper i en sida med okända tecken. 1 Nyckelord OCR, handskrivna siffror, basbilder, raddetektion 2 Introduktion Vid många tillfällen är det intressant att kunna tolka handskrivna tecken och överföra dessa på textform, till exempel vid inmatning av text och siffror i handdatorer. Det finns flera metoder för att konstruera ett program som utför denna uppgift. Vi har använt oss av basbilder och jämför sedan de okända objekten med dessa för att avgöra vilken siffra som tecknet ska identifieras som. 3 Metod Segmentering av siffror Inledningsvis trösklas inbilden så att en binär bild erhålls. Ströpixlar tas bort med hjälp av att morfologiskt öppna bilden med ett 2 2-pixlars strukturelement. För att koppla samman siffror som består av mer än ett sammanhängande område dilateras därefter bilden med ett kvadratiskt strukturelement med åtta pixlars bredd. Nästa delproblem består i att från en bild med siffror identifiera sammanhängande områden som vardera innehåller en siffra. Genom att söka linjärt pixel för pixel i bilden hittas en punkt där en siffra finns. Utgåe från denna punkten lokaliseras alla andra punkter i en 8-sammanhängande väg. För att inte behöva arbeta vidare med den morfologiskt modifierade bilden maskas motsvarande pixlar ut från originalbilden. För att förenkla sökningen efter nästa siffra raderas den utsegmenterade siffran från originalbilden. På så sätt kan nästa siffra också hittas genom linjär sökning. Homogenisering av siffror Att göra de olika utsegmenterade siffrorna så lika som möjligt torde bidra till ett bättre resultat. Särskilt med avsee på basbildsmetoden finns det vissa fördelar med att göra bilderna invarianta under lutning i originalbilden. Två ettor som skrivits med olika lutning rätas därför upp. Detta kan uppnås genom att låta nedre vänstra hörnet av siffran motsvara origo. För att avgöra hur mycket siffran ska roteras identifieras masscentrum varpå bilden translateras så att masscentrum hamnar i origo. Därefter bildas matrisen A med två kolonner och med lika många rader som antal pixlar som är icke-noll. Varje rad innehåller koordinaterna till motsvarande pixel. Egenvektorerna till A T A ger då två huvudriktningar för bilden med tillhörande egenvektorer. Bilden vrids så mycket att egenvektorn som hör till det största egenvärdet blir vertikal. För att de segmenterade siffrorna ska kunna jämföras krävs att de är lika stora. Varje inbild skalas till 30 20 pixlar oberoe om inbilden inte har dessa proportioner. Resultat av olika skalningsmetoder ses i figur 1. Om denna skalning inte utförs innan bilden roteras så riskeras att en felaktig huvudriktning för platta bilder identifieras. 2 2 2 2 2 2 Figur 1: Skillnaden mellan skalning då proportionerna bibehålls (vänstra halvan) och då proportionerna inte bibehålls (högra halvan).

Generering av basbilder De m stycken bilderna varifrån basbilder ska genereras läses in i en matris x 11 x 21... x m1 A x 12 x 22... x m2 =......, x 1n x 2n... x mn där varje kolumn är en radstaplad bild med n pixlar. För att kunna generera relevanta basbilder måste alla inbilder viktas lika, dvs. varje kolonn i A ska normeras enligt A i = A i A i. 1 Kovariansmatrisen C = AA T beskriver variationen mellan de enskilda pixlarna i bilderna. Egenbilderna är egentligen egenvektorer till denna kovariansmatris. Denna är dock av storlek n n vilket för stora inbilder är alltför tungt att beräkna numeriskt. Istället beräknas egenvektorerna till L = A T A som ast är av storlek m m - i de flesta fall ett numeriskt betydligt enklare problem. Låt V vara en matris med egenvektorerna till matrisen L i dess kolonner. Då kan m stycken egenvektorer u i till kovariansmatrisen C, hörande till de m största egenvärdena, beräknas som en linjärkombination av de radstaplade bilderna i A. Detta låter sig lätt göras med en matrismultiplikation U = AV, där kolonnerna i U är egenvektorerna u i. Efter en normering fås de sökta basbilderna. De mest intressanta basbilderna är de med störst tillhörande egenvärde. Med hjälp av ast ett fåtal av dessa kan bilderna i A approximativt återskapas med gott resultat. Huvudsyftet med utnyttjandet av basbilder är att oidentifierade siffror kan approximeras med dessa basbilder. Figur 2 visar fyra basbilder för tvåor. Där syns det att de sista bilderna är betydligt brusigare än de första. Figur 2: Basbild nummer ett, två, 99 och 100 för tvåor. Identifikation av okända siffror När väl basbilderna är konstruerade är det enkelt att avgöra vilken siffra en viss bild innehåller. Skalärprodukterna mellan en okänd bild och alla basbilderna för en viss siffra beräknas. Normen av dessa koordinater blir då ett mått på hur lik bilden är denna siffra. Raddetektion För att kunna använda sifferigenkänningen krävs en presentation av identifikationen som tar hänsyn till siffrornas inbördes placering. Detta innebär i praktiken att en rimlig raddetektion måste utföras. Figur 3 visar principen för en sådan algoritm. Steg ett 3 4 5 Figur 3: Princip för raddetektion. är att genom linjär sökning hitta en pixel i den högst belägna siffran på första raden. Detta motsvaras att den mittersta siffran i figuren. Utgåe från denna siffra söks sedan inledningsvis åt vänster i dess förlängning till dess att nästa pixel påträffas. Proceduren upprepas fram till vänsterkanten på bilden. Samma sak utförs åt höger utgåe från startsiffran. När en siffra är behandlad raderas den från urbilden så att den inte påträffas igen. 4 Experiment Implementation av ett program som identifikation av handskrivna siffror var den praktiska delen av vårt projekt. Programmet är skrivet i Matlab förutom en beräkningstung funktion skriven i C. Programkoden är bifogad i appix A. Användning av programmet Programmet kan använda sig av olika användare för att kunna identifiera siffror skrivna av en viss person med särskilda särdrag i sin handstil. För att kunna använda programmet måste inledningsvis basbilder genereras från ett inlärningsmaterial. Dessa inlärningsbilder innehåller vardera ast en typ av siffra och ska vara i PNG-format med namn enligt formen img0001.png, img0002.png,.... Bilder med en typ av siffror placeras under motsvarande underkatalog till respektive användare (se figur 4). För att skapa basbilder från inlärningsbilderna till en viss användare körs kommandot setupuser(user), där user är en sträng med användarnamnet. Basbilderna som skapas läggs i filerna baseimages/user/baseimages0.dat, baseimages/user/baseimages1.dat,....

--sourceimages/ --user1/ --0/ : --9/ --user2/ --0/ : --9/ --segmentedimages/ --user1/ --user2/ : --baseimages/ --user1/ --user2/ : Figur 4: Katalogstruktur för programmet. Att identifiera okända siffror görs med kommandot identifyimage(image, user, plot ). image anger en sökväg till en bildfil med siffror. Siffrorna måste vara mörka och bakgrunden ljus. Anges det frivilliga argumentet plot visas identifikationen grafiskt i realtid. Med programmet bifogas kompilerade versioner av getcontiguous.c för Sun (getcontiguous.mexol) och för Windows (getcontiguous.dll). Om programmet ska köras på en annan plattform behöver getcontiguous.c kompileras med Matlabkommandot mex getcontiguous.c. Testresultat I våra testar har vi använt tre olika användare; Magnus, Klas och Both. Basbilderna till användaren Both är genererade från både Magnus och Klas siffror. Tabell 1 och tabell 2 visar hur stor procentandel rätt som fås vid identifiering av cirka 200 siffror av varje slag. Resultaten visar att det är en framkomlig väg att identifiera siffrorna med hjälp av basbilder. Vid identifikation av användarens egna siffror erhölls en träffsäkerhet på nästan 100%. Noteras bör att denna träffsäkerhet knappt sjunker när basbilder som skapats från bådas siffror används. Intressant vore att undersöka hur dessa resultat förändras med fler samtidiga användare. Att identifiera siffror med basbilder skapade från en annan persons siffror ger överlag mycket dåligt resultat. Detta är väntat eftersom det inte krävs särskilt stor förändring av en handstil för att basbildernas mest signifikanta delar ska förskjutas till områden med tidigare mycket låg signifikans. Raddetektionen ger i allmänhet korrekta resultat. Även kraftigt lutande rader hanteras väl. I vissa extremfall kan ordningen på raderna kastas om. Magnus Klas Both 0 100 86 100 1 100 31 100 2 99 38 96 3 100 38 96 4 97 6 96 5 100 89 100 6 99 100 100 7 95 21 98 8 98 92 98 9 97 41 99 Totalt 99 54 98 Tabell 1: Procentandel rätt vid test på Magnus siffror med basbilder genererade med de tre användarna Magnus, Klas och Both, där Both använder bådas inlärningsbilder. Klas Magnus Both 0 100 100 100 1 100 1 100 2 99 58 97 3 98 81 98 4 91 0 89 5 99 86 100 6 98 97 98 7 100 0 96 8 98 92 99 9 98 38 94 Totalt 98 55 97 Tabell 2: Procentandel rätt vid test på Klas siffror med basbilder genererade med de tre användarna Klas, Magnus och Both, där Both använder bådas inlärningsbilder. Möjliga förbättringar Önskvärt vore att ha en metod för att avgöra om ett tecken inte är en siffra. Det a vi använder oss av för att urskilja felaktiga tecken är en storlekskontroll för att hindra alltför små områden att tolkas som siffror. En möjligt förbättring vad gäller klassificeringen är att utveckla en mer sofistikerad metod för att analysera de koordinater som får efter skalärprodukt mellan den okända bilden och basbilderna. I nuläget avgörs vilken siffra det är av normen av koordinaterna. En utveckling av detta är att även ta hänsyn till hur koordinaterna varierar mellan de olika möjliga siffrorna. Vidare skulle man kunna göra noggrannare undersökningar på siffror som ofta ger resultat nära varandra, främst fyror och nior. Detta skulle kunna ske genom att man även tittar på specifika särdrag för siffror. Ett sådant exempel är att studera hur många hål en siffra har. Där skulle fyror i normalfallet ge noll hål, nior ge ett hål och åttor ge två hål. Om man skulle använda sig av flera metoder är en

idé att vikta dessa vid klassificeringen så att metoder som är bra på att till exempel skilja mellan två specifika siffror ast har signifikans i detta fall. Sammanfattning Vi har i denna rapport gett ett exempel på hur en implementation av identifiering av handskrivna siffror kan se ut. Våra resultat visar på goda resultat när basbildsmetoden används. Bra resultat erhålls även när antalet personer som basbilderna bygger på utökas till två. Hur detta utvecklas med ännu fler personer är något som skulle vara intressant att undersöka. Speciellt hur bra det då skulle fungera på en person som inte varit med och genererat basbilderna.

A m-filer setupuser function utbild = setupuser(user) % Segmentera alla bilder som ligger i katalogen % sourceimages/{user/[0:9] samt skapa basbilder % från dessa. t = cputime; for number=0:9 % Segmentera ut siffrorna savesegmentedimages(user, number); % Spara motsvarande basbilder savebaseimages(user, number); disp(sprintf( Total tid: %.2f s, cputime-t)); savesegmentedimages function savesegmentedimages(user, number) % Segmentera ut siffrorna som finns i bilderna som ligger i % katalogen sourceimages/{user/{number/. Bilderna sparas i filen % segmentedimages/{user/segmentedimages{number.dat % Sätt filnamn imagename = sprintf( sourceimages/%s/%d/img0001.png, user, number); imagenumber = 1; % Öppna fil för lagring av segmenterade bilder fid = fopen(sprintf( segmentedimages/%s/segmentedimages%d.dat,... user, number), w ); % Skriv storleken på varje segmenterad bild fwrite(fid, [30, 20], ubit8 ); fclose(fid); fid = fopen(sprintf( segmentedimages/%s/segmentedimages%d.dat,... user, number), a ); % Går igenom alla bilder som tillhör user while (exist(imagename)) originalimage = imread(imagename); % Tröskla originalimage = originalimage < 200; % Ta bort små skräp samt dilatera ihop siffror currentimage = imopen(originalimage, ones(2)); currentimage = imdilate(currentimage, ones(8)); % Koordinater för var nästa bild att extrahera finns imageposition = findfirst(currentimage, [1 1]); % Extrahera denna bilden och uppdatera ursprungsbilden [founddigitimage, currentimage] =... getimage(currentimage, imageposition, originalimage); % Gå igenom alla siffror i nuvarande bild while imageposition ~= -1 % Spara aktuell bild till fil om bilden är tillräckligt stor if (founddigitimage ~= -1)

fwrite(fid, founddigitimage(:), ubit1 ); % Hitta första icke-nollpositionen i bilden imageposition = findfirst(currentimage, imageposition); % Segmentera ny siffra om det finns någon if (isempty(imageposition) == 0) [founddigitimage, currentimage] =... getimage(currentimage, imageposition, originalimage); % Sätt namn på nästa fil imagenumber = imagenumber + 1; imagename =... sprintf( sourceimages/%s/%d/img%0.4d.png, user, number, imagenumber); % Stäng filen för lagring av segmenterade siffror fclose(fid); disp(sprintf( Segmenteringen för %d:orna klara, number)); function updatedposition = findfirst(inpic, currentposition) % Hittar index [row, col] för första elementet i inpic % vars värde är icke-noll. Returnerar en tom matris om % alla element är noll. [nrows ncols] = size(inpic); for i = currentposition(1):nrows for j = 1:nCols if (inpic(i,j) ~= 0) updatedposition = [i j]; return; updatedposition = []; savebaseimages function savebaseimages(user, number) % Skapa basbilder för siffra number till de segmenterade % siffrorna som tillhör user. % Läs in bilderna som basbilderna ska baseras på. Varje bild lagras % som en (radstaplad) kolonnvektor i matrisen A. fid = fopen(sprintf( segmentedimages/%s/segmentedimages%d.dat,... user, number), r ); % Konrollera om bilder finns if fid == -1 disp( Inga segmenterade siffror finns ) return imagesize = fread(fid, 2, ubit8 ); A = fread(fid, [prod(imagesize), inf], ubit1 ); fclose(fid); % Normera kolonnerna i A i 1-normen A = A./ repmat(sum(a), size(a, 1), 1);

% Att hitta egenvektorer till kovariansmatrise C=A*A är får svårt % -> studera istället egenvektorer till A *A (som bara är av % storlek npictures*npictures). L = A *A; [V, eigvalues] = eig(l); % Gör egenvärdena till en vektor istället för en matris eigvalues = diag(eigvalues); % Egenvektorerna U till C kan nu bildas som en linjärkombination av % bilderna i kolonnerna hos A U = A*V; % Sortera egenvektorerna efter storleken på motsvarande egenvärde [sortedeig eigorder] = sort(eigvalues); eigorder = flipud(eigorder); U=U(:,eigOrder); % Normera egenvektorerna U = U./ repmat(sqrt(sum(u.^2)), size(u, 1), 1); % Spara basbilderna. % Basbilderna sparas i en fil där de första två talen anger % storleken på varje bild. fid = fopen(sprintf( baseimages/%s/baseimages%d.dat, user, number),... w, ieee-be ); % Skriv storleken på varje basbild fwrite(fid, imagesize, ubit8 ); % Skriv basbilderna och stäng filen fwrite(fid, U(:), float32 ); fclose(fid); getimage function [digit, updatedimage, digitrect] = getimage(inpic, startpos, originalpic) % Hittar sammanhängande bild i inpic med början i startpos. % Bilden lagras i digit och den funna siffran raderas i % ursprungsbilden och den nya, uppdaterade bilden sparas i % updatedimage. digit blir -1 om en för liten bild hittats. % Extrahera pixlar i ett 8-sammanhängande område kring startpos. % updatedimage är en bild med detta område satt till noll. % digit är en {antal pixlarx2-vektor som innehåller koordinater % för pixlarna som utgör siffran. [updatedimage, digit] = getcontiguous(inpic, startpos(1), startpos(2)); % Konvertera listan över ingåe pixlar till en sparse-matris digit = sparse(double(digit(:,1)), double(digit(:,2)), 1); % Plocka ut motsvarande pixlar ur originalbilden [row,col]=find(digit); orgdigit=originalpic(min(row):max(row), min(col):max(col)); dilatdigit=digit(min(row):max(row), min(col):max(col)); digit = full(orgdigit & dilatdigit); % Spara koordinatinformation om den funna bilden digitrect = [min(row) max(row) min(col) max(col)]; % Skala om bilden till 30x20 om den är tillräckligt stor

if (max(size(digit)) > 20) digit = resizedigitimage(digit); else digit = -1; return % Rotera bilden så att egenriktningen pekar rakt uppåt digit = rotateimagealongeigendirection(digit); % Ändra storlek igen till 30x20 pixlar digit = resizedigitimage(digit); function outpic = resizedigitimage(inpic) % Skala om inpic så att den täcker 30x20 pixlar. Ta inte hänsyn % till bildförhållandet. outpicsize = [30 20]; [row col] = find(inpic); digitrect = [min(row) max(row) min(col) max(col)]; inpic = inpic(digitrect(1):digitrect(2), digitrect(3):digitrect(4)); outpic = imresize(inpic, outpicsize, nearest ); function outpic = rotateimagealongeigendirection(inpic) % Rotera bilden inpic så att huvudriktningen går vertikalt. % Skifta origo till masscentrum av bilden [nrows ncols] = size(inpic); [ARows ACols] = find(inpic); centerom = centerofmass(inpic); ARows = ARows - centerom(1); ACols = ACols - centerom(2); A = [ARows ACols]; % Beräkna egenvektorerna och egenvärdena. Plocka ut egenvektorn % hörande till det största egenvärdet. [eigvec eigvalue] = eig(a *A); [maxeigenvalue maxindex] = max(diag(eigvalue)); eigvec = eigvec(:,maxindex); % Räkna ut vilken vinkel bilden ska vridas if (abs(eigvec(1)) < eps) theta = pi/2; else theta = atan(eigvec(2)/eigvec(1)); % Rotera bilden outpic = imrotate(inpic, -rad2deg(theta)); function centerofmass = centerofmass(inpic) [M, N] = size(inpic); weightx = sum(inpic, 1);

weighty = sum(inpic, 2); meanx = weightx *(1:N) /sum(weightx); meany = weighty *(1:M) /sum(weighty); centerofmass = [meany meanx]; getcontiguous #include <stdio.h> #include "mex.h" #include "matrix.h" struct list { int row; int col; struct list *next; struct list *prev; ; typedef struct list item; mxarray *getcontiguous(mxlogical *image, int nrows, int ncols, int startrow, int startcol) { /* Variabeldeklarationer */ item *pixel, *pixelhead; item *tocheck, *tocheckhead, *tochecktail; int currrow, currcol; int i,j, npixels; mxarray *pixels; int pixelsdim[2]; int *data; /* Initiera den nuvarande positionen till startpositionen */ currrow = startrow; currcol = startcol; npixels = 0; pixelhead = NULL; tocheck = (item *)mxcalloc(1, sizeof(item)); tocheck->row = startrow; tocheck->col = startcol; tocheck->next = NULL; tocheck->prev = NULL; tocheckhead = tocheck; tochecktail = tocheck; while(tochecktail) { /* Utgå från första pixeln i listan över pixlar att gå igenom */ currrow = tochecktail->row; currcol = tochecktail->col; /* Stega igenom alla grannar till den nuvarande positionen */ for (i = currcol-1; i <= currcol+1; i++) { for (j = currrow-1; j <= currrow+1; j++) { /* Om vi hittat en vit granne till currentpos... */ if (j>=0 && j < nrows && i >= 0 && i < ncols && image[nrows*i + j] == 1) { /*...gör den svart,... */ image[nrows*i + j] = 0;

/*...spara pixelpositionen för den vita pixeln... */ pixel = (item *)mxcalloc(1, sizeof(item)); pixel->row = j; pixel->col = i; pixel->next = pixelhead; pixel->prev = NULL; pixelhead = pixel; npixels++; /*...samt spara den nya pixeln som kand. för ytterligare utvidgning. */ tocheck = (item *)mxcalloc(1, sizeof(item)); tocheck->row = j; tocheck->col = i; tocheck->next = NULL; tocheck->prev = tocheckhead; tocheckhead->next = tocheck; tocheckhead = tocheck; /* Ta bort den precis kontrollerade pixeln från kandidater till ytterligare utvidgning */ tochecktail = tochecktail->next; pixelsdim[0] = npixels; pixelsdim[1] = 2; pixels = mxcreatenumericarray(2, pixelsdim, mxint32_class, mxreal); data = mxcalloc(npixels*2, sizeof(int)); i = 0; pixel = pixelhead; while (pixel) { data[i] = pixel->row + 1; data[npixels + i] = pixel->col + 1; pixel = pixel->next; i++; mxsetdata(pixels, data); return pixels; void mexfunction(int nlhs, mxarray *plhs[], int nrhs, const mxarray *prhs[]) { unsigned int *pixellist; mxlogical *image; mxlogical *updatedimage; int i,n; double *startrow, *startcol; image = mxgetlogicals(prhs[0]); startrow = mxgetpr(prhs[1]); startcol = mxgetpr(prhs[2]);

/* Skapa en array som innehåller en uppdaterad bild med den utsegmenterade delen raderad */ plhs[0] = mxcreatenumericarray(mxgetnumberofdimensions(prhs[0]), mxgetdimensions(prhs[0]), mxlogical_class, mxreal); updatedimage = mxgetdata(plhs[0]); /* Konvertera alla element i inbilden till heltal */ n = mxgetnumberofelements(prhs[0]); for (i=0; i < n; i++) { updatedimage[i] = (bool) image[i]; plhs[1] = (mxarray *) getcontiguous(updatedimage, mxgetm(prhs[0]), mxgetn(prhs[0]), (int)*startrow, (int)*startcol); identifyimage function result = identifyimage(file, user, doplot) % Identifierar siffror i bilden file med basbilder som tillhör % användaren user. Om doplot sätts till plot fås en bild där % identifikationen överlagras på bilden file. % Sätt doplot till false ifall den ej initieras vid anrop if (nargin < 3) doplot = false; % Kontrollera att användaren finns if (exist(sprintf( baseimages/%s/baseimages0.dat, user)) == false) disp( Användaren finns inte ); return; % Intitiera nödvändiga variabler result = ; digitrect = [1 1 1 1]; % Kotrollera om filen existerar if (exist(file) == false) disp( Filen ej funnen ) return; % Läser in bild originalimage = imread(file);

% Konvertera till gråskala if (size(originalimage, 3) == 3) originalimage = rgb2gray(originalimage); % Tröskla, öppna, dilatera originalimage = originalimage < 200; currentimage = imopen(originalimage, ones(2)); currentimage = imdilate(currentimage, ones(8)); % Plotta bilden om det är valt if (doplot == plot ) close; imshow(not(originalimage)); pause(0.1); hold on; % Koordinater för var nästa bild att extrahera börjar imageposition = findfirst(currentimage); % Stega igenom inbilden tills inga fler rader påträffas while (imageposition ~= -1) % Initiera raden med identifierade siffror identifiedrow = []; % Stega åt vänster direction = -1; % Gå igenom en rad i ursprungsbilden while (imageposition ~= -1) % Extrahera siffran [founddigitimage, currentimage, newdigitrect] =... getimage(currentimage, imageposition, originalimage); % Behandla bara bilder som är tillräckligt stora if (founddigitimage ~= -1) digitrect = newdigitrect; % Spara första identifierade siffran i raden if (isempty(identifiedrow)) rowstartrect = digitrect; % Normalisera intensiteten founddigitimage = founddigitimage/sum(founddigitimage(:)); % Identifiera siffran idnumber = identifyimagebybaseimages(founddigitimage, user); % Plotta identifikationen om det valts if (doplot == plot ) text(imageposition(2),imageposition(1),num2str(idnumber),... color, k, backgroundcolor, c ); pause(0.0001) % Uppdatera raden med identifierade siffror

if (direction == -1) identifiedrow = [idnumber identifiedrow]; else identifiedrow = [identifiedrow idnumber]; % Leta upp nästa siffra i raden imageposition = findnextinrow(currentimage, digitrect, direction); % Om det inte finns fler siffror åt vänster - ändra % sökriktning if (isequal(imageposition, -1) && direction == -1 &&... isequal(founddigitimage,-1) == false) direction = 1; imageposition = findnextinrow(currentimage, rowstartrect, direction); % Spara den identifierade raden (om den inte är tom) if (isempty(identifiedrow) == false) result = sprintf( %s%s\n, result, num2str(identifiedrow)); % Hitta nästa siffra (som nu är på en ny rad) imageposition = findfirst(currentimage); function firstindex = findfirst(inpic) % Hittar index [row, col] för första elementet i inpic % vars värde är icke-noll. Returnerar en tom matris om % alla element är noll. nrows = size(inpic,2); % Hitta första icke-noll elementet. Linjärt index. firstindex = min(find(inpic )); % Konvertera linjärt index till [row col]-format firstindex = [ceil(firstindex/nrows) mod(firstindex,nrows)]; function updatedposition = findnextinrow(inpic, digitrect, direction) % Söker i riktningen direction efter nästa pixel som är % icke-noll. Index för denna pixel lagras i updatedposition. % direction == 1 => höger % direction == -1 => vänster % Kontrollera riktning if (direction == 1) col = digitrect(4); else col = digitrect(3); % Sök efter nästa till man nått kanten på bilden while (sum(inpic(digitrect(1):digitrect(2), col)) == 0) col = col + direction;

% Kolla om vi gått utanför bilden if (col == 0 col > size(inpic, 2)) % Reurnera -1 då raden är slut updatedposition = -1; return % Returnera position för nästa siffra updatedposition =... [min(find(inpic(digitrect(1):digitrect(2), col))) + digitrect(1) - 1... col]; identifyimagebybaseimages function idnumber = identifyimagebybaseimages(inpic, user) % Avgör vilken siffra bilden inpic innehåller baserat på % jämförelse med basbilder tillhörande användaren user. maxnorm = 0; nusedbaseimages = 20; for number = 0:9; % Läs in basbilderna fid = fopen(sprintf( baseimages/%s/baseimages%d.dat, user, number),... r, ieee-be ); % Kontrollera om basbilder finns if fid == -1 disp( Basbilder saknas ); return baseimagesize = fread(fid, 2, ubit8 ); baseimages = fread(fid, [prod(baseimagesize), nusedbaseimages], float32 ); fclose(fid); % Radstapla currentimage = inpic(:); % Beräkna skalärprodukterna mellan currentimage och alla % basebilderna för en given siffra. coordinates = currentimage *baseimages; % Identifiera siffran som den som har störst 2-norm av % koordinaterna normcoords(number + 1) = norm(coordinates); if norm(coordinates) > maxnorm maxnorm = norm(coordinates); idnumber = number;