Sunteți pe pagina 1din 52

Introducere

Proiectul de diplom cu titlul Recunoatere optic de caractere reprezint un program realizat n Visual C++.NET care interpreteaz o imagine luat cu un aparat de fotografiat digital sau un telefon care are autofocus i transform litere din imagine n text care poate fi editat. Condiiile pe care trebuie s le ndeplineasc imaginea sunt: Poza s fie fcut cu un aparat digital sau cu telefonul mobil Focalizare foarte bun (de preferat aparat cu autofocus) Literele s fie de culoare mult mai nchis dect background-ul Rezoluia imaginii: aproximativ 3 Megapixeli pentru o pagin A5 Poza s fie fcut la lumin natural (dac este fcut la lumin artificial se poate ca Contribuiile mele la acest proiect sunt urmtoarele: Crearea algoritmului, a programului i a documentaiei Programul i algoritmul fiind originale, folosesc metode de recunoatere a textului total diferite de programele existente pentru Recunoatere Optic de Caractere. Dintre toate programele OCR (Optical Character Recognition), singurul pe care l-am gsit pentru recunoaterea imaginilor luate cu un aparat de fotografiat a fost ABBY FineReader. Toate celelalte programe pentru OCR gsite sunt numai pentru imagini luate cu scanerul. Ajuns la versiunea 9.0, ABBY FineReader ofer o recunoatere foarte bun a textului, dar are unele limitri: n unele condiii face confuzii ntre litere (cnd literele sunt foarte asemntoare sau sunt puin confuze) nu respect capetele de nceput i sfrit ale rndurilor din imaginea original nu respect ncadrarea n pagin (dac n imaginea original este o singur pagin, programul o intrpreteaz ca fiin 2, 3 sau 4 pagini nu recunoate formule matematice (dei este la versiunea 9) Aceste limitri se datoreaz folosirii inteligenei artificiale (atribiue probabiliti pentru litere) i folosirii funciilor oferite de Microsoft pentru deschiderea programului Word i introducerea textului n el (nu creaz un fiier Word octet cu octet).

recunoaterea s nu fie foarte bun dect pentru aparatele de fotografiat profesionale).

Fundamente teoretice

Variabila este o locaie de memorie n care se ncarc o valoare de un anumit tip (numr ntreg, caracter, valoare adevrat sau fals). Funia este o secven de program care se execut de cte ori este apelat. Clasa este o structur care conine funcii i variabile. n Visual C++, dac funciile i variabilele sunt membre ale unei clase, ele pot fi folosite i n alte clase. O clas trebuie s aib cel puin o funcie constructor i o singur funcie destructor. Funcia constructor se apeleaz automat la instanierea clasei (crearea unui obiect de o anumit clas) i are rolul de a da valori iniiale pentru variabilele acelei clase. n momentul n care se creaz un obiect de o anumit clas, se ncarc n memorie toate funciile i variabilele acelei clase. La apelarea destructorului, toate funciile i variabilele unei clase se terg din memorie. Obiectul este folosirea efectiv a unei clase. Atunci cnd se creaz o clas, n aceasta se introduc funcii i variabile, dar nu sunt folosite n programul principal dac nu se creaz un obiect de clasa respectiv. Nu pot folosi funciile dintr-o clas n programul principal dac nu creez un obiect de tipul clasei din care vreau s apelez funciile. Pentru proiectul Licenta 2009 am folosit urmtoarele clase oferite de Microsoft: CImage ofer funcii pentru prelucrarea imaginilor (crearea de imagini n CString operarea cu iruri de caractere (concatenare, lungimea unui ir, CFile prelucrarea de fiiere (citirea octeilor dintr-un fier, scrierea de octei memorie, setarea pixelilor n imagine, scoaterea pixelilor din imagine) obinerea unui caracter dintr-un ir) ntr-un fiier, salvarea fiierului) Funciile din clasa Cimage pe care le-am folosit n proiectul Licenta 2009: Create (Width, Height, nr_bii_pe_pixel, flag) creaz o nou imagine [2] GetHeight() ofer nlimea imaginii [3] GetWidth() ofer limea imaginii [4] GetPixel(x, y) ofer un pixel de coordonate x, y sub form de numr ntreg [5] Load(string) ncarc o imagine [6] Save(string) salveaz o imagine [7] SetPixel(x, y, culoare) seteaz culoarea ntr-un pixel de coordonate x, y [8]

Crearea proiectului n limbajul de programare Visual C++ .NET

1. Modul design [1]


Creearea proiectului ncepe n modul design. n acest mod se aeaz controalele grafice (butoane, controale Picture Control, controale Progress Control). Deoarece o aplicaie de tip Windows Forms ateapt un eveniment (click cu mouse-ul, apsarea unei taste, trecerea cu mouse-ul pe suprafaa unui control grafic), trebuie creeat un eveniment pentru unul sau mai multe controale grafice. Pentru proiectul Licenta 2009 n care am realizat recunoaterea optic de caractere, am aezat urmtoarele controale grafice n fereatra de dialog: butonul Deschide fiier, butonul ncepe recunoaterea, controlul Picture Control i controlul Progress Control (Fig. 1). Controalele grafice se iau prin Drag and Drop din fereastra Toolbox.

Fig. 1 Proiectul Licenta 2009 n modul design

2. Codul surs [1]


Cnd se execut dublu click pe butonul Deschide fisier, mediul de programare creeaz o funcie membr a clasei Clicenta2009Dlg.cpp numit OnBnClickedButton1(), deschide automat fiierul Clicenta2009Dlg.cpp i poziioneaz cursorul la aceast funcie. Pentru butonul ncepe recunoaterea se procedeaz similar, dar funcia de rspuns la evanimentul de click se va numi OnBnClickedButton2(). Pentru a avea acces la un control grafic trebuie creat o variabil pentru acel control. Pentru a creea o variabil asociat controlului Progress Control se execut click-dreapta pe acesta i va apare urmtoarea fereastr de dialog:

Fig. 2 Crearea unei variabile asociate controlului Progress Control n textbox-ul Variable name se scrie numele variabilei i se apas butonul Finish. Dup ce am adugat variabila asociat controlului grafic Progress Control, am acces din codul surs la funciile acestui control. Astfel, cnd scriu ntr-o funcie membr a clasei CLicenta 2009Dlg numele variabilei i operatorul punct (.), apar funciile i variabilele pentru acest control:

Fig. 3 Funciile asociate controlului Progress Control

Pentru controlul Picture Control trebuie schimbat din fereastra Proprietes ID-ul din IDC_STATIC n alt nume pentru c nu se poate aduga o variabil la un control cu acest ID. Eu l-am numit IDC_PICTURE. Variabila asociat controlului Picture Control am numit-o picture_box. Butonului Deschde fiier i-am asocia numele b_1 i butonului ncepe recunoasterea iam asociat numele b_2. Pentru ambele butoane am creat un eveniment de click care execut o bucat de cod. n proiectul Licenta 2009 am dou clase principale: CLicenta2009Dlg i Litere i nc dou care se creaz automat pentru proiect: CAboutDlg i Clicenta2009App. n clasa CLicenta2009Dlg am urmtoarele funcii create de mine: cadrane(void), cauta_pixel(void), Copiaza_Libie(void), creaza_fisier(void), desparte(void), functie_h(void), functie_v(void), functie_zoom(void), linie_1(void), linie_2(void), numara(void), PentruButonul2(void), Seteaza_Pixel(void), Sterge_Linie(void) i alte funcii generate automat de limbajul de programare la crearea proiectului. n clasa litere am funcia getLitera(void); Funcia getLitera este de tip char i nu are nici un parametru.

Algoritmul care desparte literele de background i funciile care implementeaz acest algoritm

Cel mai dificil lucru n proiectarea unui program de recunoatere optic de caractere este creearea unui algoritm care s decid dac un pixel de coordonate x i y aparine unei litere sau aparine background-ului. ntr-o imagine, doi pixeli identici pot s aparin unul literei i unul background-ului (Fig. 4). Pixelul de coordonate 0, 0 i pixelul de coordonate 477, 256 sunt identici (R = 115, G = 109, B = 119). Primul aparine background-ului i al doilea aparine unei litere (litera v). RGB = 115, 109, 119 x = 0 y = 0 RGB = 115, 109, 119 x = 477 y = 256

Fig. 4 Doi pixeli identici, unul aparinnd literei i unul background-ului Deoarece iniial programul a fost realizat n Visual C# s-a folosit o funcie din acest limbaj de programare care ofer intensitatea luminoas dintr-un pixel sub form de numr zecimal ntre 0 i 1. Aceast funcie se numete GetPixel(x, y).GetBrightness(). Pentru pixelul de coordonate 0, 0 din figura 4, intensitatea luminoas este 0.4470588. n tabelul urmtor sunt readai pixeli n nuane de gri n care intensitatea luminoas a pixelului actual difer de intensitatea luminoas a pixelului urmtor cu 0.1:
pixel

Intensitatea Luminoas
4

0.1

0.2

0.3

0.4

0.5

0.6

0.7

0.8

0.9

Componentele R= 2 6 RGB G = 25 G = 5 Intensitatea luminoas dintr-un pixel 2 5 Fig. n limbajul Visual C++ nu exist o funcie care s returneze intensitatea luminoas, informaia despre un pixel de coordonate x, y fiind doar un numr ntreg pozitiv care corespunde conversiei n baza 10 a celor 24 de bii pe care i are un pixel. Orice pixel dintr-o

imagine JPG sau BMP este un ir de 24 de bii (8 bii pentru Red, 8 bii pentru Green i 8 bii pentru Blue). Dac se va citi binar un fiier bitmap se va observa c la octeii alocai unui pixel de coordonate x, y ordinea este invers: primul octet este Blue, al doilea Green i al treilea Red. Deci structura culorii unui pixel pe 24 de bii este n realitate Blue, Green, Red. Exemplu: n Paint setez culorile: R = 80 G = 90 B = 100 Din acest motiv, cnd se face conversia din binar n zecimal trebuie inut cont de aceast ordine a culorilor. Dac doresc s obin culoarea unui pixel n Visual C++, trebuie s creez un obiect de clas Cimage i s apelez funcia GetPixel(x, y), n care x i y sunt coordonatele pixelului. Aceast funcie returneaz un numr ntreg ntre 0 i 16777215. Pentru separarea background-ului de liter am folosit echivalentul unei funcii GetPixel.GetBrightness() din Visual C#. Astfel, dac tiu c n Visual C++ culorile sunt de la negru (0) la alb (16777215) n ordinea creterii luminii, tot ce se afl ntr-un anumit interval are intensitate a luminii foarte apropiat. Exemplu: culorile de la 61440 la 65 535 cu un decalaj de 15 de culori: 00000000 11100001 00000000 = 57 600 (B = 0, G = 225, R = 0) 00000000 11111111 11111111 = 65 535 (B = 0, G = 255, R = 255) n Realitate ele sunt: 100, 90, 80

Fig. 6 Culorile de la 57 600 la 65 635 Pentru a decide dac un pixel aparine de o liter sau de background se procedeaz astfel: 1. Se mparte imaginea n fragmente de 50 x 50 pixeli. Pentru imaginea din Fig. 7 care are o rezoluie de 2044 x 1336 pixeli se obine un total de 1040 de fragmente a cte 50 pixeli (40 de fragmente pe orizontal i 26 pe vertical) plus 40 de fragmente pe orizontal cu rezoluia 50 x 36 pixeli i 26 de fragmente pe vertical cu rezoluia 44 x 50 pixeli.

Fig. 7 mprirea imaginii originale n fragmente de 50 x 50 pixeli 2. Pentru fiecare fragment se analizeaz care interval de culori predomin pentru a stabili culorea de background pentru acel fragment. Intervalele de culori sunt urmtoarele: ntre 6710886 i 8355710, echivalentul pentru intensitatea luminoas 0.4 0.5 ntre 8355710 i 10066329, echivalentul pentru intensitatea luminoas 0.5 0.6 ntre 10066329 i 11711155, echivalentul pentru intensitatea luminoas 0.6 0.7 mai mare ca 11711155, adic mai mare ca intensitatea luminoas 0.7 Fiecrui interval i-am asociat o variabil: pentru echivalentul intensitii luminoase 0.4 0.5, adic pentru intervalul de pixeli 6710886 8355710 am asociat variabila numara_4. Aceasta este o variabil de tip ntreg care iniial are valoarea 0. Cnd este ndeplinit condiia ca pixelul de coordonate x, y s fie n acest interval variabila numara_4 se incrementeaz (crete cu 1). Dup ce s-a parcurs tot fragmentul de 50 x 50 pixeli, una dintre variabilele asociate fiecrui interval va fi cea mai mare sau n cel mai ru caz, unele variabile vor fi egale ntre ele. Prima dat se verific dac pixelul de coordonate x, y este n intervalul 8355710 10066329 (adic n intervalul 0.5 0.6 ca i intensitate luminoas). Probabilitatea cea mai mare este s fie n acest interval. Dac 8355710 <= pixel(x, y) < 10066329, variabile numara_5 crete cu 1 i cutarea se oprete pentru c sigur nu se afl n celelalte intervale. Dac nu este ndeplinit condiia de mai sus, se continu cutarea n intervalul 8

10066329 11711155 (echivaletul ntensitii luminoase 0.6 0.7) deoarece probabilitatea cea mai mare este s fie n acest interval. Dac se ndeplinete condiia 10066329 <= pixel(x, y) < 11711155, se incrementeaz variabila numara_6 i cutarea se oprete pentru c n intervalele urmtoare sigur nu mai este. Dac pixelul de coordonate x, y nu se afl nici n acest interval, cutarea se continu cu intervalul de pixeli mai mari ca 11711155 (adic pixelii care au intensitatea luminoas mai mare ca 0.7). Dac este ndeplinit condiia pixel(x, y) > 11711155, variabila numara_7 i crete valoarea cu 1 i cutarea se oprete. Dac nu este ndeplinit nici condiia de mai sus, se verific i ultima condiie: pixelul s fie ntre 6710886 i 8355710. Aceast condiie este cel mai puin probabil s apar i este echivalentul intervalului 0.4 0.5 a intensitii luminoase, deci culori foarte ntunecate. Dac 6710886 <= pixel(x, y) < 8355710, variabila numara_4 crete cu o unitate. Dac s ntmpl ca pixelul de coordonate x, y s nu fie n nici unul din intervalele de mai sus, atunci are intensitatea luminoas mai mic de 0.4 (deci este sub 6710886) i este prea ntunecat ca s fie culoare de background. Exemplu: Pentru fragmentul de 50 x 50 pixeli din Fig. 8 se simuleaz funcionarea codulului de program care desparte background-ul de liter.

Fig. 8 Fragment de 50 x 50 pixeli pentru care se caut intervalul predominant


void CLicenta2009Dlg::numara(void) { for (y = y_1; y < y_2; y++) for (x = x_1; x < x_2; x++) { predomina(); } desparte(); kx++; }

Functia numara() parcurge fragmentul pixel cu pixel i pentru fiecare pixel se apeleaz funcia predomina() care verific condiiile pentru intervalele explicate mai sus. Dup ce s-au parcurs toi ce 50 x 50 pixeli, se apeleaz funcia desparte() care va decide, n funcie de intervalul care a predominat, dac pixelul de coordonate x, y aparine literei sau aparine background-ului.

Exemplu: simularea funciei numara() pentru fragmentul x_1 = 0, x_2 = 49, y_1 = 0, y_2 = 49: Pasul 1: x = 0, y = 0 Apelez funcia predomina()
void CLicenta2009Dlg::predomina(void) { if (Imagine.GetPixel(x, y) < 10066329) if (Imagine.GetPixel(x, y) >= 8355710) { numara_5++; return; }

Dac pixelul de coordonate 0, 0 este mai mare sau egal cu 8355710 i este mai mic dect 10066329 (ceea ce nseamn c are intensitatea luminoas cuprins ntre 0.5 i 0.6), cutarea se oprete pentru pixelul de coordonate 0, 0 prin comanda return. Dac nu s-a ndeplinit condiia, programul continu:
if (Imagine.GetPixel(x, y) < 11711155) if (Imagine.GetPixel(x, y) >= 10066329) { numara_6++; return; }

Verific urmtorul interval (0.6 0.7 n intensitate luminoas sau 10066329 11711155 n pixeli). Dac este ndeplinit condiia, funcia predomina() se oprete aici. Dac nu, continu:
if (Imagine.GetPixel(x, y) >= 11711155) { numara_7++; return; }

Funcionarea este identic: dac se ndeplinete condiia, funcia se oprete aici. Dac nu, continu cu ultima verificare:
if (Imagine.GetPixel(x, y) < 8355710) if (Imagine.GetPixel(x, y) >= 6710886) { numara_4++; return; }

De fiecare dat cnd condiia a fost ndeplinit, variabila corespunztoare intervalului s-a incrementat: numara_4++ nseamn c variabila numara_4 ia valoarea pe care a avut-o plus 1. Dup ce s-a terminat de executat funcia predomina(), coordonatele se mut la urmtorul pixel: x = 1, y = 0.

10

Pasul 2 . Pasul 49: x = 1, y = 0 .. x = 49, y = 0 Din nou se apeleaz funcia predomin care stabilete n ce interval se ncadreaz pixelul de coordonate 1, 0. Apoi coordonatele se mut la 2, 0 i se verific intervalul, apoi la 3, 0 i ciclul acesta continu pn cnd s-a parcurs toat linia pe orizontal (50 pixeli). Dup ce s-a parcurs prima linie pe orizontal (s-a ncheiat ciclul while pentru x), se trece la urmtoarea linie pe orizontal (y devine 1 i se intr din nou n ciclul while pentru x). Funcionarea este similaar cu cea descris mai sus n care y = 0 i x variaz de la punctul de nceput al fragmentului pn la punctul de sfrit al fragmentului. Pasul 50: x = 49, y = 0 De fiecare dat cnd s-a ncheiat parcurgerea pe orizontal (s-a ieit din ciclul while pentru x), y crete cu 1 (se trece la urmtoarea linie). Pasul 2500: x = 49, y = 49 Cnd y = y_2 se iese din ciclul while pentru y pentru c anterior (cnd y a fost egal cu y 1) s-a parcurs i ultima linie din fragment. n acest moment s-a finalizat de parcurs tot fragmentul de 50 x 50 pixeli i pentru fiecare pixel s-a incrementat o variabil (numara_4, numara_5, numara_6 sau numara_7) asociat unui interval. Pentru fragmentul din Fig. 8: numara_4 = 167, numara_5 = 168, numara_6 = 160, numara_7 = 1821. Sub intervalul 6710886 (adic sub intensitatea luminoas 0.4) sunt ceilali 184 pixeli pn la totalul de 2500. Se apeleaz funcia desparte() care, n funcie de intervalul care a predominat pentru un fragment, desparte litera de background, adic decide dac un pixel de coordonate x, y aparine literei sau aparine background-ului. Intervalul care a predominat este dat de cea mai mare valoare pe care o are una din cele patru variabile asociate fiecrui interval. Pentru Fig. 8, variabila cu cea mai mare valoare este numara_7. Aceasta nseamn c a predominat intervalul de pixeli peste 11711155 (sau intensitate luminoas peste 0.7). Algoritmul este astfel construit nct toi pixelii care sunt sub intensitatea luminoas mai mic cu 0.1 dect cea care predomin s fie considerai liter, ceilali s fie considerai background. n exemplul din Fig. 8 unde predomin intensitatea luminoas 0.7 (pixeli peste 11711155), toi pixelii care au intensitatea luminoas sub 0.6 (adic sunt sub 10066329) sunt setai pe liter, ceilali pe background. Setarea pe liter se face prin colorarea pixelului de coordonate x, y a unei imagini negre cu dimensiunile imaginii originale n alb. n momentul n care ncarc imaginea original, creez trei imagini colorate n negru care au dimensiunile imaginii originale. Aceste trei imagini se numesc Imagine_Alb_Negru, Imagine_Sterge i Imagine_Seteaza_Pixel. Fiecare dintre ele vor fi folosite ulterior n scopuri 11

diferite. Pentru exemplul din Fig. 8, codul de program din funcia desparte() care seteaz pixelii de culoare alb pentru litere este urmtorul:
if (numara_7 >= numara_6) if (numara_7 >= numara_5) if (numara_7 >= numara_4) { for (y = y_1; y < y_2; y++) for (x = x_1; x < x_2; x++) { if (Imagine.GetPixel(x, y) < 10066329) { Imagine_Alb_Negru.SetPixel(x, y, RGB(255,255,255)); Imagine_Sterge.SetPixel(x, y, RGB(255,255,255)); } } return; }

Funcionarea acestui cod de program este foarte asemntoare cu cea din funcia numara(): se parcurge fragmentul de 50 x 50 pixeli de la coordonata x_1 pn la coordonata x_2 pe orizontal i de la y_1 la y_2 pe vertical i pentru fiecare pixel se verific dac este mai mic de 10066329 (0.6 n intensitate luminoas). Dac pixelul de coordonate x, y este mai mic dect 10066329 n Imagine_Alb_Negru i n Imagine_Sterge (care sunt complet negre) se seteaz la coordonatele x, y culoarea alb. Din fragmentul colorat din Fig. 8 obin dou fragmente alb-negru (Fig. 9). Imagine_Alb_Negru

Imagine_Sterge Fig. 9 Un fragment de 50 x 50 pixeli nainte i dup desprirea background-ului de litere Funcia complet care decide dac pixelul de coordonate x, y aparine sau nu de o liter este urmtoarea (decizia se ia n funcie de intervalul care predomin i este pentru un fragment de 50 x 50 pixeli sau pentru fragmentele ce rmn la capete) :
void CLicenta2009Dlg::desparte(void) { if (numara_5 >= numara_7) if (numara_5 >= numara_6) if (numara_5 >= numara_4) { for (y = y_1; y < y_2; y++) for (x = x_1; x < x_2; x++) { if (Imagine.GetPixel(x, y) < 6710886) {

12

Imagine_Alb_Negru.SetPixel(x, y, RGB(255,255,255)); Imagine_Sterge.SetPixel(x, y, RGB(255,255,255)); } } return;

if (numara_6 >= numara_7) if (numara_6 >= numara_5) if (numara_6 >= numara_4) { for (y = y_1; y < y_2; y++) for (x = x_1; x < x_2; x++) { if (Imagine.GetPixel(x, y) < 8355710) { Imagine_Alb_Negru.SetPixel(x, y, RGB(255,255,255)); Imagine_Sterge.SetPixel(x, y, RGB(255,255,255)); } } return; } if (numara_7 >= numara_6) if (numara_7 >= numara_5) if (numara_7 >= numara_4) { for (y = y_1; y < y_2; y++) for (x = x_1; x < x_2; x++) { if (Imagine.GetPixel(x, y) < 10066329) { Imagine_Alb_Negru.SetPixel(x, y, RGB(255,255,255)); Imagine_Sterge.SetPixel(x, y, RGB(255,255,255)); } } return; } if (numara_4 >= numara_7) if (numara_4 >= numara_6) if (numara_4 >= numara_5) { for (y = y_1; y < y_2; y++) for (x = x_1; x < x_2; x++) { if (Imagine.GetPixel(x, y) < 5000269) { Imagine_Alb_Negru.SetPixel(x, y, RGB(255,255,255)); Imagine_Sterge.SetPixel(x, y, RGB(255,255,255)); } } return; } }

Funcia de mai sus primete valorile x_1, x_2, y_1 i y_2 de la alte dou funcii: una pentru parcurgerea fragmentelor pe orizontal (functie_h) i una pentru parcurgerea fragmentelor pe vertical (functie_v).

13

Funcia care parcurge fragmentele pe orizontal:


void CLicenta2009Dlg::functie_h(void) { while (kx <= h_p) { x_1 = x_1 + 50; x_2 = x_2 + 50; numara(); numara_7 = 0; numara_6 = 0; numara_5 = 0; numara_4 = 0; progress_bar.SetRange(0, interval); progress_bar.SetStep(1); progress_bar.SetPos(pozitia); progress_bar.StepIt(); pozitia = pozitia + 1;

kxx++; x_1 = x_1 + 50; x_2 = Imagine.GetWidth() - 2; numara(); numara_7 = 0; numara_6 = 0; numara_5 = 0; numara_4 = 0;

Variabila kx reprezint poziia pe orizontal a fragmentului la care s-a ajuns. Aceast variabil se incrementeaz n funcia numara() descris anterior. Variabila h_p reprezint numrul de fragmente de 50 x 50 pixeli pe orizontal fr fragmentul de la capt, care n exemplul din Fig. 7 are dimensiunea 44 x 50 pixeli. Iniial variabila x_1 are valoarea -48 i variabila x_2 are valoarea 2. Att timp ct rmn n ciclul while pentru parcurgerea fragmentelor spre dreapta, variabilele x_1 i x_2 cresc cu 50. Pentru primul fragment x_1 = -48 + 50 = 2 i x_2 = 2 + 50 = 52. Motivul pentru care se las doi pixeli de culoarea backgroundului pe marginea din stnga a imaginii este acela c voi avea nevoie ulterior de un punct de referin de culoarea background-ului fa de care s scad 1 pixel. ntotdeauna pixelul de referin este unit cu un pixel de culoare alb. Uneori acest punct fi de coordonat x = 1. Dac a fi lsat o margine de doar un pixel, referina ar fi ajuns 0 (primul pixel din stnga imaginii) la care a fi sczut 1 i a fi ajuns la coordonata x = -1, moment n care a fi ieit din imagine i programul ar fi dat eroare. Marginea de doi pixeli se las i n dreapta imaginii deoarece uneori, fa de punctul de referin va trebui s adun 1 pixel. Dac punctul de referin este exact pe marginea din dreapta i adaug 1 pixel ies din imagine i programul d eroare.

14

Funcia care parcurge fragmentele pe vertical:


void CLicenta2009Dlg::functie_v(void) { while (kxx <= v_p) { kx = 1; x_1 = -48; x_2 = 2; y_1 = y_1 + 50; y_2 = y_2 + 50; functie_h(); } kx = 1; x_1 = -48; x_2 = 2; y_1 = y_1 + 50; y_2 = Imagine.GetHeight() - 2; functie_h(); //resetez valorile dac doresc s ncarc nc o imagine numara_7 = 0; numara_6 = 0; numara_5 = 0; numara_4 = 0; x_1 = -48, x_2 = 2, y_1 = -48, y_2 = 2, kx = 1, kxx = 1; progress_bar_1 = 0, pozitia = 0, interval; progress_bar.SetPos(0); //golesc controlul grafic Progress Control }

Variabila kxx reprezint linia pe care se afl fragmentul actual sau poziia pe vertical. Variabila v_p reprezint numrul total de fragmente pe vertical fr fragmentul din capt, care n exemplul din Fig. 7 are dimensiunea de 50 x 36 pixeli. Se reiniializeaz valorile pentru kx, x_1 i x_2 deoarece de fiecare dat dup ce am parcurs funcia functie_h() aceste valori s-au modificat. Se pregtesc coordonatele pe vertical: y_1 = y_1 + 50 i y_2 = y_2 + 50. Iniial y_1 = -48 i y_2 = 2. Se las margini de 2 pixeli pentru marginea de sus i pentru cea de jos din acelai motiv ca i la functie_h(): va trebui s scad n sus sau s adun n jos un pixel i s nu ias din imagine. Se apeleaz funcia functie_h() care parcurge fragmentele pe orizontal. Se reseteaz variabilele kx, x_1 i x_2, se trece la urmtoarea linie de fragmente (kxx = 2, y_1 = 52, y_2 = 102) i se intr din nou n parcurgerea pe orizontal. Dup ce kxx = v_p, se parcurge i ultima linie, care pentru Fig. 7 este format din 40 de fragmente de dimensiunea 50 x 36 pixeli. Dup ce am terminat de parcurs toate fragmentele din imagine trebuie s resetez valorile kx, x_1, x_2, kxx, y_1 i y_2 pentru o nou imagine.

15

sensul de parcurgere

cu functia functie_h( )

Fig. 10 Desprirea background-ului de litere la momentul kxx = 6 i kx = 17 Dup ce s-au parcurs toate cele 1040 de fragmente a cte 50 pixeli plus cele 40 de fragmente pe orizontal cu rezoluia 50 x 36 pixeli i cele 30 de fragmente pe vertical cu rezoluia 44 x 50 pixeli (Fig. 8), se vor obine dou imagini identice (Imagine_Alb_Negru i Imagine_Sterge) n care vor exista doar dou culori: alb pentru liter i negru pentru background (Fig. 11).

16

Fig. 11 Imaginea original din Fig.8 dup desprirea background-ului de litere

17

Cutarea unui pixel de culoarea literei

Dup ce am obinut imaginile Imagine_Alb_Negru i Imagine_Sterge n care tiu c pixelii de culoare alb sunt litere i cei de culoare negru sunt background, programul trebuie s extrag fiecare zon n care pixelii de culoare alb sunt unii i s analizeze dac acea zon este o liter. Se parcurge fiecare linie orizontal de la stnga la dreapta pn se gsete un pixel de culoare ab. Funcia care face acest lucru se numete cauta_pixel() i conine dou cicluri while: unul pentru parcurgerea unei linii orizontale i unul pentru naintarea pe vertical n jos. n momentul n care s-a terminat de parcurs o linie orizontal i nu s-a gsit nici un pixel alb, se iese din primul ciclu while i se intr n al doilea unde se mut coordonata y mai jos cu un pixel. Dup ce s-a mutat coordonata y, x devine 0 i se intr din nou n ciclul while pentru parcurgerea liniei orizontale. Aceast alternan de intrare n ciclul while pentru x, parcurgerea liniei, ieirea din acest ciclu, intrarea n ciclul while pentru y, incrementarea variabilei y i intrarea din nou n ciclul while pentru x, se repet pn cnd se gsete un pixel alb sau pn cnd y este egal cu nlimea imaginii. Funcia caut pixel():
void CLicenta2009Dlg::cauta_pixel(void) { x = 2; y = 2; //resetez valorile pt ca au mai fost folosite la trecerea in alb-negru rand_actual = ""; while (trecere_litera_y == true) { while (trecere_litera_x == true) //gaseste pixel alb, face conturul, copiaza litera, o recunoaste si se deplaseaza spre dreapta pana ajunge la capatul randului { if (Imagine_Alb_Negru.GetPixel(x, y) == 16777215) { //se execut funcii pentru recunoaterea unei litere posibile care ncepe cu pixelul gsit } x++; }

Secvena de cod de mai sus face urmtoarele: pornete cutarea unui pixel alb, de la coordonatele x = 2 i y = 2 i parcurge fiecare pixel dintr-o linie orizontal pn cnd se gsete un pixel alb sau pn cnd s-a ajuns la captul liniei. Parcurgerea unei linii orizontale se face cu un ciclul while, unde condiia de ieire din ciclu este ca variabila trecere_litera_x s 18

fie false (aceasta se ntmpl cnd s-a ajuns la captul liniei). Deci att timp ct trecere_litera_x este true se continu cutarea pe orizontal. Cnd s-a gsit un pixel alb se apeleaz funcii pentru copierea i recunoaterea unei litere posibile care ncepe cu pixelul gsit. Indiferent dac s-au gsit sau nu pixeli de culoarea literei, cnd s-a ajuns la captul unei linii orizontale se iese din ciclul while pentru acea linie i se intr n ciclul while pentru naintarea pe vertical n jos.
if (x == Imagine.GetWidth() - 1) { x = 1; capat_rand = true; trecere_litera_x = false; } //secven de cod pentru revenirea n punctul n care s-a gsit pixel de culoarea literei deoarece se poate ca rndul s fie nclinat i primul pixel gsit s fie la jumtatea rndului sau n interiorul rndului (s nu fie la nceputul rndului) if (y == Imagine.GetHeight() - 1) { trecere_litera_y = false; } trecere_litera_x = true; y++; }

Cnd s-a intrat n ciclul while pentru naintarea n jos, coordonata y crete cu 1 pixel. Att timp ct nu s-a ajuns la ultima linie din imagine (cea mai de jos) se intr din nou n ciclul while pentru coordonata x (parcurgerea pe orizontal). La poziia 644, 2 s-a gsit primul pixel de culoarea literei

Fig. 12 Gsirea unui pixel de culoarea literei

Copierea unei litere ntr-o nou imagine


19

Dup ce am gsit un pixel care are culoarea literei, trebuie s separ litera de restul imaginii. Pentru aceasta trebuie parcurs conturul exterior al literei i trebuie coipat ntr-o nou imagine tot ce se afl n interiorul conturului. n momentul n care se parcurge conturul exterior al literei, o funcie va marca nceputul fiecrei linii orizontale (captul din stnga al unei linii care ncepe cu culoarea literei). Apoi se parcurge din nou conturul literei i cnd se ajunge la sfritul unei linii orizontale (captul din dreapta al unei linii care se termin cu culoarea literei) se copiaz linia format din cele dou puncte: captul de sfrit i captul de nceput care l-am marcat la trecerea anterioar. Algoritmul care face conturul unei litere Orice liter este format din pixeli de culoare alb (Fig. 13).

Fig. 13 Pixeli de culoare alb ce formeaz o liter Pentru ca s parcurg conturul exterior al punctelor de culoare alb ce reprezont litera, aleg 4 perechi pe pixeli, fiecare pereche fiind format dintr-un pixel alb i unul negru aezai orizontal sau vertical. Obin astfel urmtoarele perechi: ambii pixeli orizontali, primul negru, urmtorul alb ambii pixeli orizontali, primul alb, urmtorul negru ambii pixeli verticali, cel de jos alb, cel de sus negru ambii pixeli verticali, cel de jos negru,cel de sus alb

Aceste perechi sunt redate n Fig. 14:

20

Fig. 14. Cele 4 perechi de pixeli cu care parcurg conturul exterior al literei Aceste perechi de pixeli le-am numit astfel: orizontal alb negru, orizontal negru alb, vertical negru alb, vertical alb negru. Fiecrei perechi i-am atribuit n codul surs o variabil de tip boolean: h_n_a pentru orizontal negru alb h_a_n pentru orizontal alb negru v_n_a pentru vertical negru alb v_a_n pentru vertical alb negru

Pentru o mai bun nelegere a modului n care am gndit algoritmul, am fcut o analogie de denumiri cu algoritmul care implementeaz un automat (ex: automatul care servete cafea). n construirea unui autiomat care sevete cafea intervin noiunile de stri. Iniial m aflu ntr-o anumit stare i trec dintr-o stare n alta. Exemplu:iniial m aflu n starea 1; din stare 1 trec n starea 2, din starea 2 n starea 3, din starea 3 n starea 1 i dup o succesiune de treceri dintr-o stare n alta, obin rezultatul final. Din proiectarea unui automat eu am mprumutat noiunea de stare: starea h_a_n (orizontal alb negru), starea h_n_a (orizontal negru alb), starea v_a_n (vertical alb negru) i starea v_n_a (vertical negru alb). Cele 4 variabile, fiind de tip boolean pot lua doar dou valori: true i false. Cnd m aflu n starea orizontal negru alb, setez variabila h_n_a pe valoarea true. Cnd am trecut n alt stare (ex: vertical alb negru) setez variabila h_n_a pe valoarea false (nu mai sunt n starea orizontal alb negru) i variabila v_a_n pe true (sunt n starea vertical alb negru). Trecerea dintr-o stare n alta este ilustrat n Fig. 15.

21

a)

b)

c)

d)

Fig. 15 Trecerea dintr-o stare n alta pentru parcurgerea conturului exterior al unei litere ntotdeauna pixelul de referin din fiecare pereche de stri va fi cel de culoare neagr. n Fig. 15 a) este ilustrat trecerea din starea h_n_a n: starea v_n_a dac n dreapta pixelului de referin se afl un pixel alb, starea v_a_n dac sub pixelul de referin i pe diagonal se afl pixeli de culoare neagr i rmnerea n starea h_n_a dac sub pixelul de referin se afl un pixel de culoare neagr i n diagonala din dreapta jos se afl un pixel de culoare alb. Sgeata orientat n jos indic faptul c rmn n starea h_n_a dar m deplasez un pixel n jos. n Fig. 15 b) trec din starea h_a_n n starea v_n_a deasupra punctului de referin i pe diagonal se afl cte un pixel negru, trec n starea v_a_n dac deasupra punctului de referin se afl un pixel alb i rmn n starea h_a_n dac deasupra punctului de referin se afl un pixel negru i pe diagonala din stnga sus se afl un pixel alb. Sgeata orientat n sus indic faptul c rmn n starea h_a_n dar m deplasez n sus cu un pixel. n Fig. 15 c) trec din starea v_n_a n starea h_n_a dac n stnga pixelului de referin i pe diagonal se afl cte un pixel negru, trec n starea h_a_n dac n stnga pixelului de referin se afl un pixel alb i rmn n starea v_n_a dac n stnga pixelului de referin se

22

afl un pixel negru i pe diagonala din stnga jos se afl un pixel alb. Sgeata orientat nspre stnga indic faptul c rmn n starea v_n_a dar m deplasez nspre stnga cu un pixel. n Fig. 15 d) trec din starea v_a_n n starea h_a_n dac n dreapta pixelului de referin i pe diagonal se afl cte un pixel negru, trec n starea h_n_a dac n dreapta pixelului de referin se afl un pixel alb i rmn n starea v_a_n dac n dreapta pixelului de referin se afl un pixel negru i pe diagonala din dreapta sus se afl un pixel alb. Sgeata orientat spre dreapta indic faptul c rmn n starea v_a_n dar m deplasez spre dreapta cu un pixel. Exemplu de parcurgere al conturului unui fragment din litera din Fig. 13 (n roz sunt pixelii ce i-am parcurs, n albastru pixelul la care am ajuns; aceste culori nu apar n program):
1 2
0 0 1 2 3 4 5 6 7 8 9

1 2

0 1 2 3 4 5 6 7 8 9

a) sunt n starea h_n_a


0 0 1 2 3 4 5 6 7 8 9

b) din starea h_n_a trec n starea v_n_a (referina nu se mut)


0 0 1 2 3 4 5 6 7 8 9

1 2

1 2

c) rmn tot n v_n_a dar mut spre stnga


0 1 2 3 4 5 6 7 8 9

d) din v_n_a trec n h_n_a (referina se mut)


0 1 2 3 4 5 6 7 8 9

1 2

1 2
3

e) din h_n_a trec n v_n_a (referina nu se mut)


0 0 1 2 3 4 5 6 7 8 9

f) din v_n_a trec n h_n_a (referina se mut)


0 0 1 2 3 4 5 6 7 8 9

1 2

1 2

g) din h_n_a trec n v_n_a (referina nu se mut)

h) din v_n_a trec n _h_n_a (referina se mut)

23

0 1 2 3 4 5 6 7 8 9

1 2

0 3

0 1 2 3 4 5 6 7 8 9

1 2

5 6

i) rmn n starea h_n_a dar mut referina 1 pixel n jos j) rmn n h_n_a dar mut referina n jos
0 0 1 2 3 4 5 6 7 8 9 0 0 1 2 3 4 5 6 7 8 9

5 6

1 2

5 6

1 2

k) din h_n_a trec n v_a_n (referina se mut) l) rmn n v_a_n dar mut referina 1 pixel spre dreapta
0 0 1 2 3 4 5 6 7 8 9

5 6

1 2

0 1 2 3 4 5 6 7 8 9

5 6

1 2

m) rmn n starea v_a_n dar mut spre dreapta


0 0 1 2 3 4 5 6 7 8 9

n) rmn n v_a_n i mut referina


1 2
1 2 3 4 5 6 7 8 9 10

5 6

1 2

5 6

o) din v_a_n trec n h_a_n (referina se mut)


1 2
1 2 3 4 5 6 7 8 9 10

p) rmn n h_a_n dar mut referina 1 pixel n sus


1 2
1 2 3 4 5 6 7 8 9 10

5 6
8

5 6
8

q) rmn n h_a_n dar mut referina 1 pixel n sus


1 2
1 2 3 4 5 6 7 8 9 10

r) din h_a_n trec n v_a_n (referina nu se mut)


1 2
1 2 3 4 5 6 7 8 9 10

5 6

5 6
8

s) rmn n v_a_n dar mut referina 1 pixel spre dreapta ) din v_a_n trec n h_a_n (referina se mut)

24

5 6

1 2

1 2 3 4 5 6 7 8 9 10

5 6

1 2

1 2 3 4 5 6 7 8 9 10 11

t) din h_a_n trec n v_a_n (referina nu se mut)


1 2
5 6
8 1 2 3 4 5 6 7 8 9 10 11 12

) rmn n v_a_n dar mut referina spre dreapta


1 2
5 6 0 0 1 2 3 4 5 6 7 8 9 10 11

u) rmn n starea v_a_n i mut referina 1 pixel spre dreapta

v) Conturul parcurs din litera din Fig. 13

Fig. 16 Parcurgerea conturului exterior al unui fragment din litera din Fig. 13 Conform algoritmului de cutare a unui pixel de culoarea literei, dup ce s-a parcurs imaginea linie cu linie pe orizontal, s-a gsit primul pixel alb la coordonatele 8, 2 (Fig. 16 a). Dac la coordonatele 8, 2 este un pixel alb, nseamn c n stnga cestui pixel alb se afl sigur un pixel de culoarea backgroundului (pixel negru). Perechea format din primul pixel alb gsit la coordonatele 8, 2 i pixelul negru din stnga lui formeaz starea orizontal negru alb. Din aceast stare trebuie s trec n starea vertical negru alb (v_n_a), n starea vertical alb negru (v_a_n) sau s rmn n starea orizontal negru alb (h_n_a) dar s m deplasez un pixel n jos (Fig. 15 a). n Fig. 16 a, pentru prima pereche h_n_a gsit, pixelul de referin este la coordonatele 7, 2 (pixelul negru din perechea h_n_a). Prima dat programul ncearc s rmn n starea h_n_a i s mute referina n jos. Condiia este ca sub pixelul de referin s fie un pixel negru i pe diagonala din dreapta jos s fie un pixel alb. Aceast condiie nu este ndeplinit deoarece sub pixelul de referin se afl un pixel alb (la coordonatele 7, 3). Secvena de cod care ncearc rmnerea n starea h_n_a este urmtoarea (se verific i dac s-a ajuns la punctul de unde a nceput conturul pentru ca s nu se nvrt la infinit n jurul literei): 25

if (h_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx, yy + 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy + 1) == 16777215) { yy = yy + 1; if (t_x == xx) if (t_y == yy) { trecere = false; }

Deoarece nu s-a reuit rmnerea n starea h_n_a, se ncearc trecerea n starea v_a_n. Codul care ncearc trecerea n starea v_a_n este urmtorul:
if (h_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx, yy + 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy + 1) == 0) { xx = xx + 1; yy = yy + 1; v_a_n = true; h_n_a = false; }

Condiiile nu sunt ndeplinite (7, 3 i 8, 3 nu sunt pixeli negri). A mai rmas o singur variant: trecerea n starea v_n_a. Secvena de cod care face aceast trecere este urmtoarea:
if (h_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx, yy + 1) == 16777215) { v_n_a = true; h_n_a = false; }

Din starea v_n_a se ncearc rmnerea n aceast stare: condiia este ca n stnga punctului de referin s existe un pixel negru i pe diagonala din stnga jos s existe un pixel alb. n exemplul din Fig. 16 c) condiia este ndeplinit: pixelul de coordonate 6, 2 este negru i cel de coordonate 6, 3 este alb. Codul care ncearc rmnerea n starea v_n_a este urmtorul (referina se mut cu 1 pixel spre stnga):
if (v_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy) == 0) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy + 1) == 16777215) { xx = xx - 1; }

Din starea v_n_a se ncearc rmnerea n aceast stare. Condiia nu este ndeplinit (pixelul 5, 3 nu este un pixel alb) i se ncearc trecerea n starea h_n_a. Secvena de cod care ncearc aceast trecere este urmtoarea:
if (v_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy) == 0) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy + 1) == 0) { xx = xx - 1; yy = yy + 1; h_n_a = true; v_n_a = false;

26

if (t_x == xx) if (t_y == yy) { trecere = false; }

n Fig. 16 d) este ndeplinit condiia de trecere din starea v_n_a n starea h_n_a: pixelul de coordonate 5, 2 este negru i cel de coordonate 5, 3 este tot negru. Cnd a trecut n starea h_n_a, referina s-a mutat de la 6, 2 la 5, 3. Tot n aceast secven de cod se verific dac s-a ajuns la punctul n care s-a nceput conturul. De fiecare dat cnd ajung n starea h_n_a fac aceast verificare. Din starea h_n_a se ncearc rmnerea n aceast stare (codul care face aceasta a fost prezentat mai sus). Condiia de rmnere n starea h_n_a nu este ndeplinit (pixelul de coordonate 5, 4 nu este negru). Programul ncearc s treac n starea v_n_a. Condiia este ndeplinit (sub pixelul de referin exist 1 pixel alb: pixelul 5, 4). Codul care face trecerea a fost prezentat i explicat la un exemplu anterior. Coordonatele nu se schimb, dar nu m mai aflu n starea h_n_a, ci n starea v_n_a (Fig. 16 e). Din nou se ncearc rmnerea n starea v_n_a. Nu se reuete i se ncearc trecerea n starea h_n_a. Condiiile sunt ndeplinite (pixel negru la 4,3 i la 4, 4 Fig. 16 f). Pn la Fig. 16 i) toi paii sunt similari cu cei descrii anterior. n Fig. 16 i) se ncearc rmnerea n starea h_n_a. Se reuete rmnerea n starea h_n_a att n Fig. 16 i) ct i n Fig. 16 j). Se ncearc rmnerea n starea h_n_a, dar condiiile nu sunt ndeplinite. Se ncearc trecerea din h_n_a n v_a_n. n Fig. 16 k) este ilustrat aceast trecere. Referina se mut de la 3, 7 la 4, 8. Din starea v_a_n ncerc s rmn tot n aceast stare. Secvena de cod care face acest lucru este urmtoarea:
if (v_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy - 1) == 16777215) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy) == 0) { xx = xx + 1; }

Condiiile sunt ndeplinite (5, 7 este pixel alb i 5, 8 este pixel negru). Rmn n starea v_a_n i mut referina la 5, 8 (Fig. 16 l). Pn la Fig. 16 o) rmn tot n starea v_a_n i deplasez referina spre dreapta. n Fig. 16 o) se ncearc rmnerea n starea v_a_n dar condiiile nu sunt ndeplinite (pixelul 8, 7 este negru). Se ncearc trecerea n starea h_a_n: condiiile sunt ndeplinite (pixelii 8, 7 i 8, 8 sunt negri). Codul care face trecerea din v_a_n n h_a_n este urmtorul:
if (v_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy - 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy) == 0) { xx = xx + 1; yy = yy - 1; h_a_n = true; v_a_n = false; if (t_x == xx) if (t_y == yy) { trecere = false; }

n aceast parte de cod se verific i dac nu am ajuns la punctul n care a nceput cutarea (uneori cutarea se face de la dreapta la stnga i se ncepe cu starea h_a_n).

27

n continuare programul ncearc s rmn n starea h_a_n. Condiiile sunt ndeplinite deoarece deasupra punctului de referin exist un pixel negru i pe diagonala din stnga sus exist un pixel alb. Referina se mut 1 pixel mai sus: de la 8, 7 la 8, 6 (Fig. 16 p). Codul care ncearc rmnerea n starea h_a_n este urmtorul:
if (h_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy - 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx, yy - 1) == 0) { xx = xx - 1; yy = yy - 1; v_n_a = true; h_a_n = false; }

n urmtorul pas (Fig. 16 q) se ntmpl acelai lucru: rmn n starea v_a_n i mut referina de la 8, 6 la 8, 5. Fa de referina 8, 5 se ncearc condiiile pentru rmnerea n starea h_a_n: 7, 4 s fie alb i 8, 4 s fie negru. Condiia nu este ndeplinit deoarece pixelul 8, 4 este alb. Se ncearc trecerea din starea h_a_n n starea v_n_a: 7, 4 s fie negru i 8, 4 s fie negru. Secvena de cod care ncearc aceast trecere este urmtoarea:
if (h_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy - 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx, yy - 1) == 0) { xx = xx - 1; yy = yy - 1; v_n_a = true; h_a_n = false; }

Condiia nu este ndeplinit deoarece 7, 4 i 8, 4 sunt pixeli de culoare alb. Se ncearc i ultima variant: din h_a_n n v_a_n. Secvena de cod care ncearc aceast trecere este urmtorea:
if (h_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx, yy - 1) == 16777215) { v_a_n = true; h_a_n = false; }

Cnd se face trecerea din starea h_a_n n starea v_a_n coordonatele nu se schimb (Fig. 16 r). Din starea v_a_n prima ncercare este de a rmne n aceast stare. Condiiile sunt ndeplinite (pixelul 9, 4 este alb i pixelul 9, 5 este negru). Coordonatele punctului de referin se mut spre dreapta cu 1 pixel (de la 8, 5 la 9, 5 - Fig. 16 s). Din starea v_a_n avnd referina 9, 5 ncerc s rmn n aceast stare. Nu se ndeplinesc condiiile (pixelul 10, 4 este negru) i ncerc trecerea n starea h_a_n. Condiiile sunt ndeplinite pentru trecerea n starea h_a_n i referina se mut la 10, 4 (Fig. 16 ). Din starea h_a_n ncerc s rmn n aceast stare, dar s mut referina 1 pixel n sus. Deoarece pixelul de coordonate 10, 4 este alb, nu pot s rmn n aceast stare. ncerc trecerea n starea v_n_a. Nu se poate face aceast trecere deoarece la

28

coordonatele 9, 3 i 10, 3 nu se afl pixeli negri. Ultima variant posibil este trecerea n starea v_a_n. Att n Fig. 16 ) ct i n Fig. 16 u) rmn n starea v_a_n dar mut coordonatele punctului de referin spre stnga. n Fig. 16 am descris o parte din secvenele ruleaz la parcurgerea conturului exterior al unei litere. Unele treceri dintr-o stare n alta nc nu au fost aplicate. Acestea sunt: trecerea din starea v_n_a n starea h_a_n

Fig. 17 Trecerea din starea v_n_a n starea h_a_n (referina nu se mut) Codul care face aceast trecere este urmtorul (verific i dac referina este punctul de la care a nceput parcurgerea conturului ca s nu se nvrt la infinit n jurul literei):
if (v_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy) == 16777215) { h_a_n = true; v_n_a = false; if (t_x == xx) if (t_y == yy) { trecere = false; } }

trecerea din starea v_a_n n starea h_n_a

Fig. 18 Trecerea din starea v_a_n n starea h_n_a (referina nu se mut) Secvena de cod care face aceast trecere este urmtoarea (se verific i dac referina este aceeai cu punctul unde s-a nceput conturul):
if (v_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy) == 16777215) { h_n_a = true; v_a_n = false; if (t_x == xx) if (t_y == yy) { trecere = false; }

29

Algoritmul este astfel creat nct s existe urmtoarea ordine de treceri dintr-o stare n alta: pentru h_a_n: se ncearc rmnerea n aceast stare; dac nu se reuete, se ncearc trecerea n starea v_n_a; dac nici aici nu s-a reuit trecerea, a mai rmas o singur variant posibil: trecerea n starea v_a_n care sigur se va ndeplini. Pentru v_a_n: ncerc s rmn n aceast stare; dac nu reuesc, ncerc s trec n starea h_a_n; dac nu a reuit trecerea n h_a_n, condiiile se vor ndeplini pentru trecerea n starea h_n_a. pentru h_n_a: se ncearc rmnerea n aceast stare; dac nu se ndeplinesc condiiile se ncearc trecerea n starea v_a_n; dac nici aici nu se ndeplinesc condiiile sigur se vor ndeplini pentru trecerea n starea v_n_a. pentru v_n_a: prima dat se ncearc rmnerea n aceast stare; dac nu se ndeplinesc condiiile, se ncearc trecerea n starea h_n_a; dac nu s-a intrat n starea h_n_a, sigur se va intra n starea h_a_n. Pentru litera din Fig. 16 v) se prezint conturul la care s-a ajuns n dou momente diferite:
0

22 14

a) conturul cnd referina a ajuns la 14, 22

b) conturul cnd referina a ajuns la 7, 0

Fig. 19 Conturul literei la dou momente diferite

30

Algoritmul care seteaz pixelul de nceput al fiecrei linii

Pentru ca o liter s poat fi copiat trebuie s cunosc nceputul i sfritul fiecrei linii ce o compune (deoarece o copiez linie cu linie). Dup ce s-a gsit un pixel de culoarea literei se apeleaz o funcie care parcurge conturul exterior al literei i seteaz ntr-o imagine separat pixelii de nceput ai fiecrei linii ce compun litera. Pixelii de nceput ai unei linii reprezint toate strile h_n_a (orizontal negru alb). Imaginea n care se seteaz aceti pixeli este o imagine care are dimensiunile egale cele ale imaginii Alb_Negru i n care toi pixelii sunt de culoarea background-ului. Aceast imagine se numete Imagine_Seteaza_Pixel. Cnd algoritmul ce parcurge conturul a ajuns n starea h_n_a, n imaginea Imagine_Seteaza_Pixel se seteaz punctul la care s-a ajuns. Acest punct marcheaz nceputul unei linii din liter. Exemplu de setare a pixelului ce reprezint nceputul unei linii din liter (culoarea albastr din Imagine_Alb_Negru nu apare n prgram):
0 0 1 2 3 4 5 6 7 8 9 0 0 1 2 3 4 5 6 7 8 9

1 2

Imagine_Alb_Negru
1 2
0 0 1 2 3 4 5 6 7 8 9

Imagine_Seteaza_Pixel
0
2

0 1 2 3 4 5 6 7 8 9

Imagine_Alb_Negru
1 2
3 0 0 1 2 3 4 5 6 7 8 9

Imagine_Seteaza_Pixel
1 2
3

0 1 2 3 4 5 6 7 8 9

Imagine_Alb_Negru

Imagine_Seteaza_Pixel

31

1 2 4
3

0 1 2 3 4 5 6 7 8 9

1 2 4

0 1 2 3 4 5 6 7 8 9

Imagine_Alb_Negru
0 0 1 2 3 4 5 6 7 8 9

Imagine_Seteaza_Pixel
0 0 1 2 3 4 5 6 7 8 9

1 2
5

3 4 5

1 2

Imagine_Alb_Negru
0 0 1 2 3 4 5 6 7 8 9

Imagine_Seteaza_Pixel
0 0 1 2 3 4 5 6 7 8 9

1 2
5

3 4 5

1 2

Imagine_Alb_Negru
1 2
5

Imagine_Seteaza_Pixel
1 2
5 6 0 0 1 2 3 4 5 6 7 8 9

0 1 2 3 4 5 6 7 8 9

3 4

Imagine_Alb_Negru

Imagine_Seteaz_Pixel

Fig. 20 Setarea unui pixel ce reprezint nceputul de linie Funcia care seteaz pixelul de nceput al fiecrei linii se numete Seteaza_Pixel(). Aceast funcie este alctuit dintr-un ciclu while care parcurge conturul exterior al literei. Condiia de ieire din ciclu este ca variabila boolean trecere s ia valoarea false. Aceast variabil ia valoarea false cnd am ajuns la punctul n care s-a nceput parcurgerea conturului. Pentru contur se folosete algoritmul explicat mai sus. Codul de program care seteaz pixelul de referin n imaginea Imagine_Seteaza_Pixel este urmtorul: din starea h_n_a rmn n aceast stare dar mut referina;
if (h_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx, yy + 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy + 1) == 16777215)

32

yy = yy + 1; Imagine_Seteaza_Pixel.SetPixel(xx, yy, RGB(255,255,255)); if (xx < x_stanga) { x_stanga = xx; } if (t_x == xx) if (t_y == yy) { trecere = false; } }

din starea v_n_a ajung n starea h_n_a


if (v_n_a == true) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy) == 0) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy + 1) == 0) { xx = xx - 1; yy = yy + 1; h_n_a = true; v_n_a = false; Imagine_Seteaza_Pixel.SetPixel(xx, yy, RGB(255,255,255)); if (xx < x_stanga) { x_stanga = xx; } if (t_x == xx) if (t_y == yy) { trecere = false; } }

din starea v_a_n ajung n starea h_n_a


if (v_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx + 1, yy) == 16777215) { h_n_a = true; v_a_n = false; Imagine_Seteaza_Pixel.SetPixel(xx, yy, RGB(255,255,255)); if (xx < x_stanga) { x_stanga = xx; } if (t_x == xx) if (t_y == yy) { trecere = false; }

33

Se observ c de fiecare dat cnd am ajuns n starea h_n_a se verific dac punctul la care s-a ajuns este cel mai din stnga punct din liter. Pe msur ce se parcurge conturul literei se verific i celelalte extreme din liter (extrema din dreapta, de sus i de jos). Pixelul cel mai din dreapta se verific atunci cnd intru n starea h_a_n:
if (xx > x_dreapta) { x_dreapta = xx; }

Pixelul cel mai de sus este verificat cnd sunt n starea v_n_a:
if (yy < y_sus) { y_sus = yy; }

Cel mai de jos punct din liter se verific n starea v_a_n:


if (yy > y_jos) { y_jos = yy; }

Dup ce am terminat de parcurs conturul literei cu funcia Seteaza_Pixel(), am obinut urmtoarele informaii (Fig. 21 litera implic i conturul negru exterior care s-a parcurs):
1 2
5 6 0

punctele de nceput pentru fiecare linie (pixelii de culoare alb din imaginea Imagine_Seteaza_Pixel); cel mai din stnga punct din liter (valoarea variabilei x_stanga); cel mai din dreapta punct din liter (valoarea variabilei x_dreapta); cel mai de sus punct din liter (valoarea variabilei y_sus); cel mai de jos punct din liter (valoarea variabilei y_jos);
y_sus
1 2
5 6 0 0 1 2 3 4 5 6 7 8 9 10 11

0 1 2 3 4 5 6 7 8 9 10 11

x_stanga

x_dreapta

]y_jos

21 a) Imagine_Alb_Negru (culoarea albastr nu apare n program)

23

b)Imagine_Seteaza_Pixel

34

Fig. 21 Gsirea punctelor de extrem i setarea nceputului de linie Pentru Fig. 21 a) punctele de extrem sunt urmtoarele: x_dreapta = 21 x_stanga = 0 y_sus = 0 y_jos = 23

Cnd s-a ieit din funcia Seteaza_Pixel() se calculeaz urmtoarele dou valori: jumatate_v (jumtatea nlimii unei litere) jumatate_h (jumtatea limii unei litere)
jumatate_v = (y_jos - y_sus) / 2 + y_sus;

jumatate_h = (x_dreapta - x_stanga) / 2 + x_stanga;

Algoritmul care copiaz fiecare linie ce compune o liter Dup ce am setat nceputul fiecrei linii, trebuie s fac din nou conturul literei i de fiecare dat cnd am ajuns n starea h_a_n s copiez toi pixelii care se afl ntre punctul de referin la care am ajuns i punctul care l-am setat n funcia Seteaza_Pixel(). Pentru aceasta se creeaz o nou imagine numit Imagine_Litera care are dimensiunile literei. Codul care creaz imaginea Imagine_Litera este urmtorul:
Imagine_Litera.Create(x_dreapta - x_stanga+1, y_jos - y_sus+1, 24, 0);

Starea h_a_n reprezint sfritul unei linii. Pentru copierea liniei se apeleaz funcia Copiaza_Linie. Aceasta parcurge conturul exterior al literei cu algoritmul folosit i la funcia Seteaza_Pixel() i de fiecare dat cnd ajunge n starea h_a_n intr ntr-un ciclu while care copiaz toi pixelii de la stnga punctului n care s-a ajuns pn la punctul de nceput al liniei setat n imaginea Imagine_Seteaza_Pixel. Exemplu de copiere a unei linii dintr-o liter (linia dintre 18, 3 i 3, 3 culorile albastru i roz nu apar n program):
0

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0
1

2
3

Imagine_Alb_Negru

Imagine_Seteaza_Pixel

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0
1

2
5

2 4
3

35

Imagine_Litera
1

x = 18, y = 3
1

Imagine_Litera

x = 17, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
5

2 4
3

Imagine_Litera

x = 16, y = 3

Imagine_Litera

x = 15, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0
1

2
5

2 4
3

Imagine_Litera
0

x = 14, y = 3

Imagine_Litera

x = 13, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0
1

2
5

2 4
3

Imagine_Litera
1

x = 12, y = 3
1

Imagine_Litera

x = 11, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
5

2 4
3

Imagine_Litera

x = 10, y = 3
1

Imagine_Litera
2 4
3

x = 9, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
5

Imagine_Litera
1

x = 8, y = 3
1

Imagine_Litera

x = 7, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
5

2 4
3

Imagine_Litera

x = 6, y = 3
1

Imagine_Litera
2 4
3

x = 5, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
5

Imagine_Litera
1

x = 4, y = 3
1

Imagine_Litera

x = 3, y = 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
5

2 4
3

36

Fig. 22 Copierea unei linii dintr-o liter din Imagine_Alb_Negru n Imagine_Litera Codul de program care face copierea liniei y = 3 prezentat n Fig. 22 este urmtorul:
if (h_a_n == true) if (Imagine_Alb_Negru.GetPixel(xx, yy - 1) == 0) if (Imagine_Alb_Negru.GetPixel(xx - 1, yy - 1) == 16777215) { yy = yy - 1; l_x = xx; if (Imagine_Seteaza_Pixel.GetPixel(l_x, yy) == 16777215) //sa nu suprapuna 2 pixeli (pixelul de referninta cu linia copiata) { l_x = xx - 1; } while (capat_linie == false) { Imagine_Litera.SetPixel(l_x - x_stanga, yy - y_sus, Imagine_Alb_Negru.GetPixel(l_x, yy)); if (Imagine_Seteaza_Pixel.GetPixel(l_x, yy) == 16777215) { capat_linie = true; } l_x--; } capat_linie = false; if (yy == jumatate_v) //Nu pot sa calculez x_d si x_s la Seteaza_Pixel() pt ca nu am jumatate_v if (xx > x_d) { x_d = xx; } if (t_x == xx) if (t_y == yy) { trecere = false; }

Variabila l_x reprezint coordonata pe orizontal pentru copierea liniei. Aceasta iniial primete valoarea coordonatei xx (punctul la care s-a ajuns cnd s-a intrat n starea h_a_n). Variabila l_x se decrementeaz (scade cu o unitate) pe msur ce se copiaz linia. Cnd n imaginea Imagine_Seteaza_Pixel s-a ajuns la pixelul alb nseamn c am ajuns la nceputul liniei. Aici opresc copierea i ies din ciclul while. Parcurgerea unei linii din imaginea Imagine_Alb_Negru se face n paralel cu parcurgerea aceleai linii din imaginea Imagine_Seteaza_Pixel. Diferena dintre cele dou imagini este aceea c n imaginea Imagine_Alb_Negru se afl linia ntreag iar n imaginea Imagine_Seteaza_Pixel se afl numai nceputul liniei. Dup cum se observ n exemplul prezentat n Fig. 22 o linie este compus din pixeli albi i negri. Capetele liniei sunt ntotdeauna pixeli negri.

37

Deoarece algoritmul prezentat n Fig. 22 se repet pentru fiecare linie a literei, n urmtorul exemplu se va prezenta o liter n diferite faze ale copierii. Exemplu pentru diferite linii copiate n imaginea Imagine_Litera (culorile roz i albastru nu apar n program):
0
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0
1

2
3

2
5

Imagine_Alb_Negru
1

Imagine_Litera (s-a copiat linia y = 4)

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0

2
3

Imagine_Alb_Negru

Imagine_Litera (s-a copiat linia y = 3)

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0
1

2
3

2
5

Imagine_Alb_Negru
1

Imagine_Litera (s-a copiat linia y = 2)


2
5

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0 3

Imagine_Alb_Negru
1 2
5 6 0

Imagine_Litera (s-a copiat linia y = 1)


1 2
5 6

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 10 11 12 13 14 15 16 17 18 19 20 21 0 1 2 3 4 5 6 7 8 9 0 3

Imagine_Litera dup ce s-au copiat 9 linii Imagine_Litera dup ce s-au copiat 18 linii

38

1 2
5 6

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 0

8 9 1 0

x_s
jumatate_v

x_d

11

Imagine_Alb_Negru

23

Imagine_Litera Imagine_Litera dup ce s-au copiat toate liniile Exemplu de extragere a unei litere Fig. 23 Copierea unei litere din imaginea Imagine_Alb_Negru n imaginea Imgine_Litera Dup ce am terminat de parcurs funcia Copiaza_Linie() am urmtoarele informaii: x_d,

x_s i litera copiat n imaginea Imagine_Litera. Variabila x_d reprezint cel mai din dreapta punct din liter care este pe axa jumatate_v, variabila x_s reprezint cel mai din stnga punct din liter care este pe axa jumatate_v i variabila jumtate_v reprezint axa y care se afl la jumtatea nlimii literei.

39

mprirea literei n patru cadrane

Pn n acest moment am separat litera de restul imaginii astfel nct tiu c toi pixelii de culoare alb din imaginea Imagine_Litera aparin unei singure litere. n imaginea Imagine_ Alb_Negru pixelii de culoare alb aparineau de mai multe litere i nu se tia dac doi pixeli de culoare alb luai la ntmplare aparin aceleai litere. Urmtorul pas este atribuirea unui nume acelei litere: a, b, c, A, B, C, liter neidentificat, parantez. Pn acum cuvntul liter a avut sensul de orice zon n care pixelii de culoare alb sunt unii. i dac se gsete un singur pixel izolat el este analizat cu etapele descrise pn acum: setarea nceputului de linie i copierea liniei. Din acest moment, cuvntului liter i se va asocia un sens nou: liter identificat i transformat ntr-un caracter i liter neidentificat i tears din imagine. ncepnd cu aceast parte a programului se face trecerea de la o liter care nu poate fi editat (imagine, mulime de
1 2
5 6

pixeli) la o 20 21 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 liter pe 0

care o pot edita (un

caracter de tip Unicode). Pentru aceasta se ncepe analizarea pixelilor de culoare alb din imaginea Imagine_Litera i ncercarea de a gsi unele caracteristici unice pe care le are o anumit formaiune de pixeli. Se ncepe prin a mpri

8 9 1 0

11

formaiunea de pixeli n patru cadrane (Fig. 24).

23

Fig. 24 mprirea literei n patru cadrane Pentru exemplul din Fig. 24 cadranele sunt urmtoarele: cadranul 1: 1...9 x 1...10 cadranul 2: 10...20 x 1...10 cadranul 3: 1...9 x 11...22

40

cadranul 4: 10...20 x 11...22 cadranul 1: x = x_stanga + 1 ...... y = jumatate_h y = y_sus + 1 ...... y = jumatate_v 1 cadranul 2: x = jumatate_h ...... x = x_dreapta - 1 y = y_sus + 1 ...... y = jumatate_v 1 cadranul 3: x = x_stanga + 1 ...... x = jumatate_h y = jumatate_v ...... y = y_jos - 1 cadranul 4: x = jumatate_h ...... x = x_dreapta - 1 y = jumatate_v ...... y = y_jos - 1

n general, delimitarea cadranelor se face astfel:

Pentru fiecare dintre cele 4 cadrane se calculeaz ct la sut reprezint fiecare stare dintre cele foloite la parcurgerea conturului: h_n_a, h_a_n, v_n_a i v_a_n (de data aceasta se ia n calcul i interiorul literei, nu doar conturul exterior prezentat n Fig. 19). Exemplu: (culoarea albastr nu apare n program):
9 9 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 9

Pentru cadranul 1 (numr total de stri: 21):


1 2 1 2
3 5 6

1 2
5 6

8 9 1 0 Strile h_n_a 6 (28%)

5 6

1 2

8 9 1 0

5 6

8 9 1 0

8 9 1 0

Strile h_a_n 4 (19%)

Strile v_a_n 7 (33%)

Strile v_n_a 4 (19%)

10 1 1 12 13 14 15 16 17 18 1 12 13 14 15 16 17 18 1920 13 14 15 16 1710 11920 13 14 15 16 17 18 1920 10 1 1920 10 1 1 12 18 1 12

Pentru cadranul 2 (numr total de stri: 29):


1 2
5 6 3

1 2
5 6

1 2
5 6

1 2
5 6

4 7

8 9 1 0

8 9 1 0

8 9 1 0

8 9 1 0

Strile h_n_a 8 (27%)

Strile h_a_n 10 (34%)

Strile v_a_n 5 (17 %)

Strile v_n_a 6 (20 %)

11

Pentru cadranul 3 (numar total de stri: 33):


11 11 11

1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 9 9 1 9

22

41

22

22

22
Strile h_n_a 7 (21%) Strile h_a_n 7 (21%) Strile v_a_n 8 (24%) Strile v_n_a 11 (33%)

11

Pentru cadranul 4 (numr total de stri: 41):


11 11 11

10 1 1920 10 1 1 12 13 14 15 16 17 18 1 12 13 14 15 16 17 18 1920 13 14 15 16 17 18 1 12 13 14 15 16 17 18 1920 10 1 1920 10 1 1 12

22

22

22

22

Strile h_n_a 11 (26%)

Strile h_a_n 13 (31%)

Strile v_a_n 9 (21%)

Strile v_n_a 8 (10%)

Fig. 25 Cadranele ce apar n litera din Fig. 24 i procentul de stri pentru fiecare cadran Motivul pentru care numrul total de procente nu este 100 este acela c, atunci cnd se calculeaz procentele se memoreaz ca numr ntreg i limbajul Visual C++ trunchiaz un numr real la cel mai mic ntreg. Dac n cadranul 4 sunt n total 41 de stri i se calculeaz ct la sut din ele reprezint cele 9 stri v_a_n, rezultatul exprimat printr-un numr real este
9 100 = 21,9 . Variabila care trebuie s memoreze acest numr este de tip real i valoarea 41

21,9 este trunchiat la cel mai mic ntreg adic 21. Astfel, procentul care reprezint starea v_a_n din cadranul 4 este memorat ca 21 i nu ca 21,9 (valoarea exact) sau 22 (cel mai apropiat ntreg). Dup ce s-au obinut cele 16 variabile (4 stri pentru fiecare din cele 4 cadrane) se compar fiecare din aceste variabile cu cele din baza de date. Baza de date reprezint o clas separat n care am 16 intervale pentru fiecare liter, 1 interval pentru fiecare variabil. Aceste intervale sunt procente de stri calculate pentru mai multe litere cu acelai nume (mai multe litere de a, mai multe litere de b, etc). Modul de creare al intervalelor este urmtorul: Se introduc ntr-o imagine mai multe litere cu acelai nume (Fig. 26).

Fig. 26 Litere pentru crearea intervalelor

42

Se verific procentele pentru prima liter i se gsesc urmtoarele valori: cadran_1_h_a_n = 3 cadran_1_h_n_a = 19 cadran_1_v_a_n = 30 cadran_1_v_n_a = 46 cadran_2_h_a_n = 33 cadran_2_h_n_a = 20 cadran_2_v_a_n = 13 cadran_2_v_n_a = 33 cadran_3_h_a_n = 19 cadran_3_h_n_a = 31 cadran_3_v_a_n = 29 cadran_3_v_n_a = 19 cadran_4_h_a_n = 35 cadran_4_h_n_a = 21 cadran_4_v_a_n = 32 cadran_4_v_n_a = 10 Se verific procentele pentru a doua liter i se compar valorile cu cele anterioare. Dac valorile gsite acum sun diferite de cele gsite anterior, se procedeaz n felul urmtor: captul din stnga al intervalului pentru starea h_a_n va fi cea mai mic valoare din cele dou i captul din dreapta va fi cea mai mare valoare din cvele dou. La fel se procedeaz i cu celelalte stri. Se verific procentele i pentru urmtoarea liter. Din nou se compar valorile gsite cu intervalul creat anterior. Dac o valoare gsit acum este mai mic dect captul din stnga al intervalului, captul din stnga devine acea valoare. Dac valoarea gsit este mai mare dect captul din dreapta , captul din dreapta devine acea valoare. Diferena dintre captul din stng al intervalului i cel din dreatpa nu trebuie s fie mai mare de 30. Dac diferena dintre captul din stnga i cel din dreapta este mai mare de 30, valoarea gsit nu va deveni un nou capt stnga sau dreapta. Pentru Fig. 26 s-au obinut urmtoarele intervale cu diferena maxim dintre capete 30: Captul din stnga 0 19 21 31 27 Stare cadran_1_h_a_n cadran_1_h_n_a cadran_1_v_a_n) cadran_1_v_n_a cadran_2_h_a_n Captul din dreapta 11 34 40 48 41

43

13 10 24 14 26 29 15 27 18 30 10

cadran_2_h_n_a cadran_2_v_a_n cadran_2_v_n_a cadran_3_h_a_n cadran_3_h_n_a cadran_3_v_a_n cadran_3_v_n_a cadran_4_h_a_n cadran_4_h_n_a cadran_4_v_a_n cadran_4_v_n_a Intervalele gsite pentru Fig. 26

20 19 42 23 36 36 21 35 23 35 16

Aceste intervale sunt introduse ntr-un fiier text n felul urmtor: if ((0 <= cadran_1_h_a_n) && (cadran_1_h_a_n <= 11)) if ((19 <= cadran_1_h_n_a) && (cadran_1_h_n_a <= 34)) if ((21 <= cadran_1_v_a_n) && (cadran_1_v_a_n <= 40)) if ((31 <= cadran_1_v_n_a) && (cadran_1_v_n_a <= 48)) if ((27 <= cadran_2_h_a_n) && (cadran_2_h_a_n <= 41)) if ((13 <= cadran_2_h_n_a) && (cadran_2_h_n_a <= 20)) if ((10 <= cadran_2_v_a_n) && (cadran_2_v_a_n <= 19)) if ((24 <= cadran_2_v_n_a) && (cadran_2_v_n_a <= 42)) if ((14 <= cadran_3_h_a_n) && (cadran_3_h_a_n <= 23)) if ((26 <= cadran_3_h_n_a) && (cadran_3_h_n_a <= 36)) if ((29 <= cadran_3_v_a_n) && (cadran_3_v_a_n <= 36)) if ((15 <= cadran_3_v_n_a) && (cadran_3_v_n_a <= 21)) if ((27 <= cadran_4_h_a_n) && (cadran_4_h_a_n <= 35)) if ((18 <= cadran_4_h_n_a) && (cadran_4_h_n_a <= 23)) if ((30 <= cadran_4_v_a_n) && (cadran_4_v_a_n <= 35)) if ((10 <= cadran_4_v_n_a) && (cadran_4_v_n_a <= 16)) Dup ce am scris ntr-un fiier textul de mai sus, l copiez n codul surs. Acest text reprezin secvena de cod pentru verificarea intervalelor pentru litera a. Dac, atunci cnd se analizeaz o imagine nou, pentru o liter gsit se potrivesc toate cele 16 intervale, atunci litera gsit este interpretat ca i litera "a". Pn n acest punct, programul a gsit o zon n care pixelii sunt unii, a copiat acei pixeli ntr-o imagine separat, a mprit formaiunea de pixeli n patru cadrane i a calculat procentele de stri pentru fiecare din cele 4 cadrane, obinnd 16 variabile. Dac cele 16 variabile se potrivesc cu cele din baza de date, litera este identificat i este returnat sub

44

form de caracter. Dac litera nu este identificat, acea liter este tears din imaginea Imagine_Alb_Negru. Funcia care terge litera este exact ca i funcia care copiaz litera cu urmtoarele deosebiri: Funcia Copiaza_Linie() Liniile sunt copiate Liniile se copiaz n Imagine_Litera Verific punctele de pe jumtatea nlimii care sunt cel mai n stnga i cel mai n dreapta n liter Funcia Sterge_Linie() Liniile sunt terse Liniile se terg n Imagine_Alb_Negru Nu verific nici un alt punct de pe liter

Imaginea Alb_Negru dup tergerea unei litere este cea din Fig. 26 b:

a) Imagine_Alb_Negu nainte de tergerea literei b)Imagine_Alb_Negru dup tergerea literei Fig. 26 tergerea unei litere neidentificate Dac litera a fost identificat se procedeaz n felul urmtor: Dac parcurgerea se face de la dreapta la stnga, se verific coordonatele literei gsite anterior pentru determinarea spaiului dintre litere. De asemenea, se verific dac nu s-a trecut pe alt rnd, deoarece este posibil ca rndurile s fie nclinate. Dac parcurgerea se face de la stnga la dreapta, se verific coordonatele literei urmtoare pentru determinarea spaiului dintre litere. Determinarea spaiului dintre litere i urmrirea unui rnd Litera a fost identificat i am urmtoarele informaii despre liter: x_dreapta, x_stnga, y_jos, y_sus, jumatate_h, jumatate_v, x_d i x_s. Se apeleaz funcia litere_1() care verific dac litera gsit este din acelai rnd cu litera gsit anterior i verific distana dintre litera gsit i litera anterioar.

Exemplu: Pasul 1: Verific dac litera gsit este pe acelai rnd cu litera anterioar (Fig. 27):
litera anterioara
litera gasita

45

Fig. 27 Urmrirea unui rnd n exemplul din Fig. 27 litera gsit (litera din cuvntul continu) nu este pe acelai rnd cu litera anterioar (litera i din concluzii). n acest moment se iese din cutarea spre dreapta i se pornete cutarea spre stnga de la prima liter identificat n rnd (Fig. 28).

prima litera identifica ta

Fig. 28 Trecerea la cutarea spre stnga cnd a ajuns la captul rndului Codul de program care compar dac litera gsit este pe acelai rnd cu litera anterioar este urmtorul (se compar i diferena pe orizontal pentru spaiul dintre litere):
if (jumatate_v_1 < y_jos_2) //compar diferenta pe verticala (caracterul actual(2)[indentificat anterior] cu urmatorul(1)[ultimul identificat]) { s_1 = s; //ca sa nu am ultima litera din randul actual luata din randul de jos daca randul de jos este inclinat if ((x_stanga_1 - x_dreapta_2) >= l.p_v) if (x_dreapta_2 != 0) { rand_actual = rand_actual + _T(" "); } } else { capat_rand = true; trecere_litera_x = false; }

Pasul 2: Dac litera gsit este pe acelai rnd cu litera anterioar, se msoar distana dintre cel mai din dreapta punct pentru litera gsit i cel mai din stnga punct pentru litera anterioar (Fig. 29).
x_dreapta

46
x_stanga

a) x_stanga i x_dreapta b) distana dintre litere Fig. 27 Distana pe orizontal n funcie de distana dintre cele dou litere se pune sau nu spaiu ntre litere. Distana se calculeaz pentru fiecare liter n funcie de nlimea ei. Pentru unele litere este suficient o distan de jumtate din liter pentru c literele au o nlime mic. Pentru alte litere, care au nlimea mare, distana se calculeaz ca fiind o treime din liter.

Trecerea la rndul urmtor

47

Parcurgerea unui rnd de litere se face astfel: se caut un pixel de culoarea literei, se copiaz litera ntr-o imagine separat, se mparte n 4 cadrane i se ncearc s se identifice. Dac s-a identificat, se trece la urmtoarea liter. Dup ce s-a trecut la urmtoarea liter, se verific dac aceasta este pe acelai rnd cu litera gsit anterior. Dac nu este pe acelai rnd, s-a ajuns la captul rndului i cutarea se reia de la prima liter gsit n rnd. Dac este pe acelai rnd, se ncearc identificarea ei i dac s-a identificat se trece la litera urmtoare. Se continu trecerea la litera urmtoare pn cnd s-a ajuns la captul imaginii. Cnd s-a ajuns la captul imaginii, se revine la prima liter gsit n rnd i se face cutarea spre stnga pn se ajunge la marginea din stnga a imaginii. n acest moment se trece la rndul urmtor de litere.

Identific litera Caut pixel de culoarea literei Spre dreapta

Compar distana pe vertical cu litera urmtoare

Distan bun

Nu identific litera

Distan mare

Revine la prima liter din rnd i caut spre stnga

Distana bun

Compar distana pe vertical cu litera urmtoare

Identific litera Caut pixel de culoarea literei spre stnga Nu identific litera Cnd a ajuns la captul din stnga imaginii trece la rndul urmtor

Distan mare

Trece la rndul urmtor Fig. 28 Algoritmul de urmrire a unui rnd Dup ce s-a terminat de parcurs un rnd, se trece la rndul urmtor astfel: Coordonata x se mut la nceputul imaginii

48

Coordonata y se mut la cel mai de jos punct din rnd

De fiecare dat cnd s-a identificat o liter se compar dac punctul cel mai de jos din liter este mai mare dact cel mai de jos punct din rnd. Dac este, cel mai de jos punct din liter devine cel mai de jos punct din rnd. Iniial cel mai de jos punct din rnd are valoarea 0. Dup ce s-a trecut la rndul urmtor, se reia din nou algoritmul descris n Fig. 28. Trecerea la rndul urmtor se face atta timp ct coordonata y este mai mic dect nlimea imaginii 2 pixeli (marginea de 2 pixeli care s-a fcut la trecerea n alb-negru). Cnd s-a terminat de parcurs imaginea (y = nlimea imaginii 2) se deschide o fereastr de salvare a unui fiier n care se cere calea unde s se salveze fiierul text care conine literele.

Concluzii

Programul ofer o acuratee bun pentru imaginile luate cu aparat de fotografiat digital sau telefon care are autofocus, litere de culoare neagr tiprite pe foaie alb i rezoluie de 3 4 Megapixeli pentru pagini A5. De asemenea, literele trebuie de fonturi Arial. Programul nu recunoate tabele, funcii matematice sau imagini. De asemenea, nu recunoate nici semnele de punctuaie. Programul respect structura unui rnd i nu mparte documentul n mai multe pagini. Un avantaj major este acela c, probabilitatea de a face confuzii ntre litere este foarte mic.

49

Comparaie ntre ABBY Fine Reader i Licena 2009

Caracteristici Recunoatere text Recunoatere formule matematice Recunoatere imagini Confuzii

ABBY Fine Reader DA NU DA Uneori

Licenta 2009 DA NU NU n cazuri excepionale Desprirea imaginii n 2 culori, extragerea fiecrei litere i mprirea ei n 4 cadrane. Compararea fiecrei litere cu 16 intervale unice pentru un caracter

Algoritmul pe care se bazeaz

Inteligen Artificial i dicionar de cuvinte

Respect numrul de pagini din original Respect captul de nceput i de sfrit al fiecrui rnd Este limitat de funciile oferite de Microsoft (motivul) Numrul de persoane care au realizarat softul (fr noiunile de sintax ale limbajului de programare) Timpul de realizare al

DA NU DA pentru c folosete Automate pentru Word DA NU pentru c scrie un fiier nou octet cu octet

Echip de cel puin 10 persoane 20 de ani de experien n

1 persoan

8 luni din care 5 luni pentru

50

softului

OCR din care cel puin 5 pentru aparate de fotografiat Inteligen artificial mai

aparat de fotografiat (3 luni puntru studierea sintaxei i a imaginilor luate cu scanerul) Crearea unui algoritm care s recunoasc i formule matematice

Perspective

bun, fr recunoaterea formulelor matematice

Anexa 1 Funcia de zoom


Deoarece Visual C++ nu are funcie de zoom pentru vizualizarea unei imagini dintr-un PictureControl, am realizat eu o funcie care face zoom pentru imaginea ncrcat i apoi o afieaz n PictureControl. Codul funciei functie_zoom() este urmtorul:
void CLicenta2009Dlg::functie_zoom(void) { int raport_pixel, diferenta, raport_pixel_1 = 0, latime_zoom = 0, inaltime_zoom = 0; raport_pixel = Imagine.GetHeight() / 271; diferenta = Imagine.GetHeight() - raport_pixel * 271; if (diferenta != 0) //daca am o diferenta arpoximez la cea mai mare valoare din intreg (programul trunchiaza diferenta la cea mai mica valoare din intreg) { raport_pixel = raport_pixel + 1; } while (raport_pixel_1 < Imagine.GetWidth()) { latime_zoom = latime_zoom + 1; raport_pixel_1 = raport_pixel_1 + raport_pixel; } raport_pixel_1 = 0; //resetez valoarea pentru raport_pixel_1 while (raport_pixel_1 < Imagine.GetHeight()) { inaltime_zoom = inaltime_zoom + 1; raport_pixel_1 = raport_pixel_1 + raport_pixel; } int raport_pixel_x = 0, raport_pixel_y = 0; //am nevoie de raport_pixel_x si y pt parcurgerea imaginii originale //functia de zoom Imagine_Zoom.Create(latime_zoom, inaltime_zoom, 24, 0); for (int y_zoom = 0; y_zoom < Imagine_Zoom.GetHeight(); y_zoom++) { for(int x_zoom = 0; x_zoom < Imagine_Zoom.GetWidth(); x_zoom++) { Imagine_Zoom.SetPixel(x_zoom, y_zoom, Imagine.GetPixel(raport_pixel_x, raport_pixel_y)); raport_pixel_x = raport_pixel_x + raport_pixel; } raport_pixel_x = 0; raport_pixel_y = raport_pixel_y + raport_pixel;

51

} picture_box.SetBitmap(Imagine_Zoom); raport_pixel_1 = 0; //resetez valoarea pentru raport_pixel_1 pentru urmatoarea imagine latime_zoom = 0; //resetez valoarea pt urmatoarea imagin inaltime_zoom = 0; //resetez valoarea pt urmatoarea imagine }

Bibliografie
[1] Emanuel Ciprian Sasu, Informatic Industrial, Editura Universitii Aurel Vlaicu Arad 2005 [2] http://msdn.microsoft.com

52