Dezvoltarea Unei Aplicatii PE Platforma Android Pentru Extragerea Obiectelor DIN Imagini
UNIVERSITATEA "TITU MAIORESCU" DIN BUCUREȘTI
FACULTATEA DE INFORMATICĂ
LUCRARE DE LICENȚĂ
DEZVOLTAREA UNEI APLICAȚII PE PLATFORMA ANDROID PENTRU EXTRAGEREA OBIECTELOR DIN IMAGINI
COORDONATOR ȘTIINTIFIC:
Conf.univ.dr. Ana Cristina DĂSCĂLESCU
ABSOLVENT:
Radu-Cristian Oțelea-Baciu
SESIUNEA IUNIE -IULIE
2016
Introducere
Este secolul 21 iar omenirea a început să devină din ce în ce mai dependentă pe tehnologia și dispozitivele construite. Numărul tranzistoarelor încă urmează tendința crescătoare a legii lui Moore iar puterea de procesoare a dispozitivelor electronice nu a fost niciodată mai mare.
Atât în industria IT cât și în viața de zi cu zi, interacțiunea dintre om și dispozitivele electronice a fost constant gândita ca fiind într-un singur sens, unul în care omul înțelegea dispozitivele și le oferea acestora comenzi explicite pentru a funcționa. Cu toate acestea, considerând creșterea exponențială atât a interacțiunii om-mașina cât și a puterii de procesare a dispozitivelor din ziua de azi, nevoia ca și dispozitivele să înțeleagă lumea umana este de asemeni una în creștere. Tehnologia recunoașterii optice a caracterelor (OCR din eng. Optical Character Recognition) încearcă să facă această legătură, oferind dispozitivelor posibilitatea de a înțelege caractere din lumea umană.
Tehnologia OCR a fost pentru prima oară folosită de către biblioteci în timpul anilor 1990 pentru a trece cărți și ziare într-un format digital, care să permită stocarea mai sigură a conținutului acestora. Cu toate acestea, primele tentative, atât a bibliotecii londoneze cu colecția Burney de ziare și gazete, cât și a unui proiect din Australia având în vedere colecția Ferguson au rezultat în eșecuri. Dată fiind starea în care se aflau aceste materiale cât și a acurateței scăzute a tehnologiei OCR, cele două colecții au putut fi salvate doar parțial. Din fericire, pe parcursul următoarelor două decenii, acuratețea software-ului OCR a crescut drastic iar acestea au început să fie folosite din ce în ce mai mult într-o gamă largă de aplicații, precum introducerea datelor, recunoașterea numerelor de înmatriculare, ca ajutor pentru nevăzători, etc.
Considerente pentru dezvoltarea unei aplicații OCR pentru mobil
În aplicațiile OCR pe PC, imaginile trebuie încărcate manual sau capturate cu un periferic precum un webcam sau un scanner. Din moment ce nu toată lumea are constant acces la un calculator iar dispozitivele de tip smartphone au căpătat deja un monopol pe piața telefoniei mobile, dezvoltarea unei aplicații OCR pentru mobil este favorabilă datorită accesibilității oferite.
La început singurul detriment al dezvoltării pe mobil era calitatea imaginilor capturate cu un astfel de dispozitiv, dar din moment ce smartphone-urile de pe piața au început să prezinte camere de 12 megapixeli, calitatea a încetat să mai fie o problemă.
Considerente pentru dezvoltarea unei aplicații OCR offline
Majoritatea aplicațiilor mobile de OCR necesită acces constant la internet pentru a realiza recunoașterea caracterelor. Din păcate, acest fapt constrânge utilizatorii să folosească aplicațiile doar în prezența unei conexiuni active la internet, lucru care limitează caracterul mobil al aplicației. Deși în ziua de azi o conexiune mobilă la internet este mai ușor de realizat, fie prin 3G/4G, fie prin WIFI, realizarea unei aplicații offline va elimina orice situație neplăcută în care niciuna dintre opțiunile de mai sus nu este disponibilă.
Scopul proiectului
Conform celor menționate, scopul acestei lucrări este realizarea unei aplicații mobile, offline care folosește recunoașterea automată a caracterelor pentru a extrage text din imagini realizate cu camera aparatului mobil.
Capitolul 1
1.1 Ce înseamnă OCR?
Recunoașterea optică a caracterelor (abreviată ca OCR din eng. Optical Character Recognition) reprezintă conversia mecanică sau electronică a textului scris de mână, tastat sau imprimat în text editabil. Textul se extrage de obicei dintr-o suită de posibile surse, precum documente scanate, fotografii ale documentelor, fotografii scenice sau din text supraimpus asupra altor medii (spre ex. subtitrări televizate). Tehnologia OCR este folosită în principiu ca o formă de introducere a datelor imprimate pe hârtie, din documente precum pașapoarte, facturi, rapoarte bancare, cărți de vizită, chitanțe electronice sau orice alt tip de documentație la îndemână. Textul astfel obținut este digitalizat, fapt care permite operațiuni variate asupra sa, incluzând editarea electronică, stocarea și distribuirea online, aplicarea unor căutări avansate asupra textului, sau folosit mai departe în sintetizarea unor fișiere audio.
OCR poate fi considerat un domeniu de cercetare în inteligența artificiala, recunoașterea modelelor și vederea mecanică.
Tehnologia OCR poate fi împărțită în doua proceduri distincte, scanarea și recunoașterea. În timpul scanării, o versiune digitala este produsă sub forma unei imagini bitmap și salvata în cadrul aplicației. Procesul de a recunoaște textul scris poate fi la rândul său împărțit în 5 procese distincte:
Identificarea textului din blocul/blocurile de imagini
Recunoașterea caracterelor
Identificarea string-urilor de caractere (cuvintele)
Corectarea textului
Returnarea textului extras
Versiunile inițiale de software OCR trebuiau învățate cu imagini ale fiecărui caracter în parte pentru a putea recunoaște text și de obicei funcționau doar asupra unui singur font. Versiunile curente oferă un grad crescut de acuratețe care acoperă o gama larga de fonturi, suportând o varietate de formate de imagini. Unele sisteme sunt capabile sa reproducă sursa textului citit returnând atât formatarea textului și aranjarea în pagina cat și elemente auxiliare precum imagini.
1.2 Istoria tehnologiei OCR
Începuturile tehnologiei OCR pot fi trasate până la începutul secolului XX, când Emanuel Goldberg și Edmund Fournier d’Albe dezvoltau concomitent un aparat care putea citi caractere și le transforma în semnale telegrafice și un dispozitiv care putea citi caractere, emițând un sunet diferit pentru fiecare caracter identificat.
Spre finalul anilor 1920, același Emanuel Goldberg crease așa-zisa mașină statistica ce verifica arhive cu microfilm folosind un sistem de recunoaștere a unui cod optic.
Fig 1.1. Brevetul primului sistem OCR
Interesul pentru o metoda de a converti și extrage textul dintr-o imagine a început sa se facă din ce în ce mai resimțit.
Raymond Kurzweil, un inventator de origine americană a stabilit în anul 1974 firma Kurzweil Computer Products, Inc, care a condus cercetarea către realizarea primului program de recunoaștere optică a caracterelor care avea ca scop citirea oricărui font normal dintr-o imagine. Scopul principal al acestei cercetări era dezvoltarea unui aparat menit sa citească texte care apoi puteau fi redate pe cale sonora persoanelor cu dizabilități vizuale. Cercetările sale au deschis noi orizonturi atât pentru tehnologiile OCR, cât și pentru tehnologii de captura imagine (firma Xerox a început prin dispozitive bazate pe creațiile lui Kurzweil) cât și pentru tehnologiile de tip Text to speech,
1.3 Tipuri de sisteme OCR
De-a lungul timpului au fost dezvoltate sisteme de OCR pentru mai multe platforme și cu scopul de a oferi diverse servicii
Desktop OCR
Software OCR și ICR pentru desktop sunt sisteme analitice de inteligență artificială. Deși în ultimul timp, diverse dicționare au apărut, menite să ușureze procesul de recunoaștere, majoritatea programelor de recunoaștere observă caracterele pe care le așează în cuvinte. Pe măsura ce cuvintele sunt întâmpinate din ce în ce mai des, programul poate fi proiectat să învețe treptat ce cuvinte sunt uzuale, fapt care să permită o recunoaștere mai ușoara a textelor bazându-se pe repetiție.
Web/Online OCR
Pe măsura ce a devenit mai presantă nevoia de a avea servicii accesibile nu doar de la calculatorul personal, dar de la orice calculator, a survenit și o schimbare în mentalitatea dezvoltării de software de recunoaștere. Ținta a început să se schimbe treptat de la un singur PC, la sisteme bazate pe conceptul de multi-platforming, precum PC-Web-Cloud-dispozitive mobile. După 30 de ani de dezvoltare a programelor pentru un singur PC, noul trend a început să se facă simțit prin Web OCR. Cunoscut și ca Online OCR, acest tip de sistem a început să fie din ce în ce mai răspândit după anul 2000, când diverse firme au început să se folosească de explozia tehnologiilor internet pentru a oferi servicii de OCR atât pentru uz personal, cât și pentru uz corporativ.
OCR orientat pe aplicații
Din moment ce tehnologia recunoașterii caracterelor a început să fie mai răspândita, au apărut o serie de noi concepte spre a fi luate în seama. Imagini complicate de fundal, combinate cu o serie de alte probleme precum obiecte degradate, probleme în orientare, zgomot, rezoluții mici și fonturi din ce în ce mai diferite au dus la dezvoltarea de sisteme specializate pe o anumita trăsătura. Aceasta customizare poarta numele de OCR orientat pe aplicație sau OCR personalizat, fiind folosit în domenii de specialitate precum recunoașterea de chitanțe, recunoașterea de cărti de credit sau cărți de vizita, etc.
1.4 Aplicabilitatea tehnologiei OCR
Recent, tehnologia OCR a început sa fie recunoscuta ca fiind un atu și deci a început sa fie aplicată în din ce în ce mai multe sectoare, revoluționând procesele de management ale documentelor.
Dintre sectoarele afectate de progresul tehnologiilor OCR pot fi menționate:
Banking – Aplicarea OCR în domeniul banking permite realizarea unor tranzacții automate fiind necesara doar o intervenție redusa din partea oamenilor. Tehnologia aproape ca a fost perfectata, în unele cazuri fiind posibila citirea și recunoașterea din scrisul de mana
Rutier – Aplicarea OCR în instanțe precum puncte radar permit recunoașterea unor elemente precum numărul de înmatriculare pentru a permite posibilitatea de a face un profil într-un timp cât mai scurt a persoanelor care încalcă legea. Aplicabilitatea este îmbunătățita de o legătura la o baza de date din care aplicația sa facă verificări.
Legal – Posibilitatea de a extrage text din imagini ale unor documente, împreună cu posibilitatea de a caută anumite elemente în cadrul textului obținut permite ca multe ore pierdute în căutarea de informații să poată fi eliminate.
Sănătate – În sănătate aplicarea unui sistem OCR permite o procesare mai rapida a formularelor pacienților și evita orele pierdute pe introducerea datelor. Asemeni punctului de aplicabilitate rutieră, conectarea datelor la o baza de date ar crește eficiența.
1.5 Alternative curente pentru tehnologii OCR pe Android
În prezent exista un număr de aplicații pe piață care au ca scop extragerea de obiecte din capturi de imagine. Dintre acestea, cele mai la îndemâna sunt:
OCR Test
Aceasta este o aplicație experimentală ce rulează pe Tesseract, permițând o recunoaștere a textului direct de pe telefon fără sa fie necesară o conexiune la un server. Aplicația poate recunoaște cuvinte sau propoziții scurte. Aplicația permite rularea unui serviciu de traducere ce poate fi executat după ce textul a fost extras.
Fig 1.2 OCR Test
Text Fairy
Text fairy este o aplicație ce executa extragere de text din imagini, permițând utilizatorului sa editeze textul găsit și sa îl folosească prin clipboard în cadrul altor aplicații. Programul, publicat pe Google Play suportă până la 50 de limbi și permite transformarea unui text extras în format pdf.
Fig 1.3 Text Fairy
ABBYY Business Card Reader Free
Acesta este una dintre cele mai răspândite aplicații de OCR de pe piață. Scopul său este sa captureze imagini cu cărți de vizită pe care apoi le va procesa și le va salva într-un serviciu de cloud. Aplicația permite transferul informațiilor pe propriul telefon, incluzând elaborarea unui nou contact în agenda telefonica cat și distribuirea cărților de vizita salvate prin SMS sau prin Wi-Fi.
Fig 1.5 ABBYY business Card reader
Capitolul 2
2.1 Sistemul Android
Sistemul Android se regăsește peste tot, de la telefoane și tablete, la ceasuri și televizoare Google TV. Se preconizează ca în viitorul apropiat, sistemul sa se găsească în mașinile personale, în mijloacele de transport în comun sau în trenuri și avioane ca și sisteme de divertisment pentru perioade lungi de călătorie.
Creat de către Andy Rubin ca un sistem de operare pentru telefoanele mobile la începutul secolului 21, sistemul a fost achiziționat de Google în anul 2005, plasându-l pe creatorul sistemului în poziția de director al noului departament. Se speculează ca achiziția s-a datorat în principal apariției pe piață a sistemului iOS și a iPhone-ului de la Apple., deși unele voci exclama ca alte sisteme precum Windows Mobile sau Symbian încă reprezentau o prezență puternică pe piața mobilă.
Fig 2.1 Ecranul principal pentru android 6.0.1
În ultimul deceniu Android s-a maturizat și a evoluat într-un sistem de operare foarte stabil, pornind de la versiunea 1.0 pana la actuala 4.4
Versiunea Nume de cod Nivel API
1.5 Cupcake 3
1.6 Donut 4
2.1 Eclair 7
2.2 Froyo 8
2.3.2 Gingerbread 9
2.3.7 Gingerbread 10
3.1 Honeycomb 12
3.2 Honeycomb 13
4.0.2 Ice Cream Sandwich 14
4.0.4 Ice Cream Sandwich 15
4.1 Jelly Bean 16
4.2 Jelly Bean 17
4.3 Jelly Bean 18
4.4 KitKat 19
5.0 Lolllipop 21
5.1 Lollipop 22
6.0 Marhsmallow 23
Fig 2.2: Distribuția versiunilor de Android în lume
Din punct de vedere al programării, una dintre trăsăturile cele mai importante ale sistemului de operare pe care se rulează o aplicație este nivelul de API pe care acesta îl rulează. API, interfața de programare a aplicației (eng. Application programming interface) specifică componentele software și modul în care acestea pot interacționa între ele, cât și componentele hardware la care are acces aplicația. Pe scurt, API vine sub forma unei librării care include specificații pentru rutine, structuri de date, clase, obiecte și variabile.
Un sistem de operare nu este altceva decât un program mare care administrează rularea unui calculator sau dispozitiv. Majoritatea sistemelor de operare sunt construite în straturi, cele mai uzuale fiind starturile de suprafață cu care interacționează majoritatea utilizatorilor. Spre exemplu, pe Microsoft Windows se regăsește spațiul de lucru standard unde utilizatorul poate lansa programe, sa le administreze, totul prin intermediul unei interfețe grafice.
Straturile inferioare ale unui sistem de operare, sunt adeseori, invizibile unui utilizator obișnuit: Cat timp utilizatorul este preocupat cu un program, sistemul se asigura ca toate procesele și serviciile din spate funcționează corect astfel încât totul sa meargă corect. În esența, un sistem de operare se asigura ca în fundat sunt administrate toate
La cel mai de baza strat al unui sistem de operare se afla kernel-ul. Acesta rulează direct pe hardware-ul procesorului și face munca de jos necesare pentru a face procesorul sa funcționeze. Într-un sistem bazat pe mai multe straturi, straturile superioare își fac treaba apelând starturile inferioare direct sau indirect pana la kernel.
Cele mai cunoscute și apreciate sisteme de operare de pe piață sunt Microsoft Windows, aflat acum la versiunea 10, Mac OS X (aflat acum la a 10 versiune) și Linux. Cum primele doua sunt proprietățile intelectuale ale respectivelor companii, Linux este open-source, acesta fiind unul dintre motivele pentru care Android are platforma bazata pe kernel-ul Linux.
Android este bazat pe kernel-ul Linux 2.6.x și folosește versiuni simple a multor pachete linux ce sunt des utilizate. Shell-ul Android nu are multe din comenzile suportate de către shell-ul sistemului Linux de pe calculator. Iar în schimb android folosește propria librărie în limbajul C numita Bionic. De asemenea multe din driverele pe care le folosește android sunt bazate pe cele ale linux-ului.
Pentru a rula un sistem de operare incorporat ar fi necesar în mod normal un calculator întreg care să ruleze pe un chip destul de mic încât să permită spațiu și pentru restul componentelor electronice (cameră, placa de bază, etc), dar și destul de puternic pentru a putea rula aplicații din ce în ce mai performante și care necesită din ce în ce mai multe resurse. Asemeni calculatoarelor din ziua de azi, dispozitivele mobile (smartphones, tablete, e-readere, etc) sunt într-un proces continuu de evoluție, avansând de la procesoare cu 2 nuclee în urmă cu câțiva ani la procesoare cu 8 nuclee în prezent, pe lângă memoria RAM care a crescut sa atingă 8 GB, de la 2 GB în. Deși și calculatoarele și laptop-urile au ținut pasul constant, unele operațiuni care înainte erau considerate posibile doar de la un terminal de calculator, sunt acum posibile on-the-go, de pe un telefon.
Android constituie atât în putere de procesare cât și în memorie este un sistem de operare complet, bazat pe platforma Linux, open-source. Android suportă dezvoltarea prin limbajul de operare Java, unul dintre cele mai populare limbaje întâlnite pe piață.
Termenul de open-source se referă la faptul că un software a fost dezvoltat în cooperare de mai mulți indivizi ai unei comunități și este liber disponibil pentru uzul comercial, venind împreună cu întregul cod sursă pentru a putea fi modificat mai mult dacă este necesar. Deși este dezvoltat direct de Google, acesta este deschis și publicat permițând oricui să folosească mai departe codul sursă, inclusiv pe plan comercial.
Una dintre evoluțiile cele mai interesante survenite de către sistemul Android este creșterea utilizării conexiunii la internet în cadrul variilor aplicații. Deși aceasta creștere poate fi observată abia începând cu ultimii ani, tendința de a folosi mesaje text împreună cu folosirea touchscreen-urilor, duc la o folosire exponențial mai răspândită a telefoanelor capabile să se conecteze la internet față de telefoanele convenționale.
Cu toate acestea, utilizarea și implicit, dezvoltarea pe Android poate întâmpina o serie de probleme. Majoritatea acestora au fost ameliorate într-o anumită măsură, mai ales ținând cont de diversele metode de dezvoltare care sunt folosite pe piață și de numărul de telefoane care sunt disponibile. Dintre potențialele probleme amintim:
-Dimensiunea ecranelor: Telefoanele folosite pe piață au ecranele relativ mici, cu toate ca tendința a fost ca acestea să crească în dimensiune. Din păcate, dimensiunea nu va putea crește la nesfârșit, datorita riscului de a deveni inutilizabile dispozitivele.
-Tastaturile și butoanele mecanice: Sistemul Android a reușit să facă trecerea către tastaturile digitale mai grațios decât unele seturi de dispozitive pe piață, însă aici problema se va reflecta în felul în care diversele acțiuni de sistem (back button, home button, recents, interaction between multiple hardware buttons) vor influența aplicația și experiența utilizatorului. Spre exemplu, deși majoritatea telefoanelor folosesc butoanele standard menționate mai sus, nu toți producătorii le folosesc iar unul dintre cei mai răspândiți, SAMSUNG continuă să producă telefoane care folosesc un dispozitiv de meniu. Din moment ce sunt printre singurii dar și printre cei mai răspândiți pe piață, dezvoltatorii trebuie sa ia în considerare comportamentul aplicației când utilizatorul ar dori sa folosească butonul cu pricina.
-Puterea de procesare: În 2016 s-a observat că pe piață peste 70% dintre dispozitive rulau un sistem de operare Android. Deși acest lucru trebuie considerat un câștig, nu toate dispozitivele erau noi sau rulau pe un sistem de operare recent. Acest lucru va limita dezvoltatorii de aplicații întrucât puterea de procesare, memoria disponibilă și sistemul de operare trebuie luate constant în seamă și luate măsuri cu privire la orice neconcordanțe între aplicație și dispozitiv.
– Sisteme actualizate constant: Cu cât sunt mai multe modele de dispozitive pe piață, cu atât trebuie ca sistemul de operare să fie actualizabil pe majoritatea. Dacă pe piață ar exista numai dispozitive care rulează versiunea stock, de baza a sistemului, atunci actualizarea s-ar putea face mai ușor. Din moment ce multe dispozitive pe piață vin cu un sistem de operare încărcat din start cu o multitudine de modificări, cu atât va întârzia mai mult actualizarea sa. Combinând acest punct cu puterea de procesare se ajunge ca anumite dispozitive sa rămână în urmă și să nu poată fi folosite pe deplin de către utilizatori
Ultimele două probleme sunt din păcate unele dintre cele mai periculoase deoarece pot strica experiența utilizatorului ducând la iritare, mai ales în condițiile în care
Aplicația încarcă procesorul până în momentul în care așa mult încât nu se pot recepționa apeluri sau mesaje;
Aplicația nu reacționează în mod corect la acțiuni care întrerup funcționalitatea sa. Astfel, aplicația poate sa nu fie trimisă în fundal în momentul în care se recepționează un apel, nu se afișează un push notification în cazul în care aplicația nu permite afișarea status bar-ului.
Aplicația se blochează sau blochează sistemul de operare cauzând ca restul aplicațiilor/sistemului să nu mai răspundă;
Din aceste cauze, dezvoltarea de programe pentru un telefon este diferită de cea pe un calculator personal. Uneltele și framework-urile sunt diferite iar spre deosebire de programele de calculator care pot fi rulate în același timp pe același ecran, pe mobil, aplicațiile trebuie gândite în așa fel încât o singura aplicație poate fi afișata la un moment dat dar în același timp restul aplicațiilor sa ruleze în fundal.
Ceea ce Google încearcă sa facă este sa ușureze munca rezolvând o parte din probleme:
Android folosește un limbaj de programare des utilizat (Java) împreună cu diverse librarii, de asemenea frecvent folosite, iar Google oferă un suport constant pentru programul de dezvoltare cel mai des folosit, Android Studio.
Android funcționează intr-un cadru unic, în care celelalte programe trebuie sa ruleze intr-un așa fel încât sa nu interfereze cu alte programe sau cu propriul sistem de operare al telefonului.
În ceea ce privește ecranele dispozitivelor care rulează sistemul, doua tehnologii sunt folosite pentru afișare:
LCD ( eng. liquid crystal displays), ecrane cu cristale lichide;
LED (eng. light emmiting diodes), diode emițătoare de lumina.
Un lucru care pune dispozitivele Android de-o parte fata de alte platforme, cum ar fi iOS este numărul de dispozitive aflate pe piață, care datorita dimensiunilor variate ale ecranelor au făcut ca granița intre telefoane mobile și tablete sa fie din ce în ce mai ambigua, rezultând așa-zisele „phablets”. Ca regula de baza însă, se pot întâmpina 4 dimensiuni:
Small – Samsung Galaxy S2
Medium – LG Nexus 5
Large – Asus Nexus 7
Extra Large – HTC Nexus 9
În funcție de rezoluția ecranului, dispozitivele vor cădea într-o anumita categorie de dispozitive care vor cere asset-uri grafice de o anumita densitate:
ldpi (low) ~120 dpi – Deprecated
mdpi (medium) ~160 dpi – Samsung Galaxy Tab 10
hdpi (high) ~240 dpi – LG Nexus 4
xhdpi (extra-high) ~320 dpi – HTC Nexus 9
xxhdpi (extra-extra-high) ~480 dpi – LG Nexus 5
xxxhdpi (extra-extra-extra-high) ~640 dpi – Motorola Nexus 6
Fig 2.3 Intervalele de densități diferite pentru Android
Pentru ca o aplicație sa poată fi folosita de o gama cat mai mare de dispozitive, dimensiunile de ecrane suportate trebuie declarate în Android Manifest iar pentru ca o gama cat mai mare de densități sa poată fi folosita, asset-uri de rezoluții diferite trebuie adăugate în directorul drawable
Unul dintre cele mai importante avantaje pe care Android-ul îl are fata de alte produse de pe piață, precum iOS este sistemul de multiprocesare și de widget-uri.
Sistemul Android nu restricționează procesorul la rularea unei singure aplicații la un anumit moment. Sistemul funcționează pe principiul în care fiecare aplicație este rulata în propriul sau mediu, fapt care previne probleme cauzate de rularea simultana a doua aplicații care ar necesita aceleași resurse. Sistemul este apoi capabil sa managerieze aplicațiile și firele de execuție ale unui singur program apelând numele acelui proces prin numele pachetului, un identificator cunoscut doar de către procesor. Astfel, diverse aplicații pot fi lăsate rulând în fundal, fără sa necesite oprirea proceselor corespondente în timp ce o singura aplicație este afișată în prim-plan. Spre exemplu, un utilizator poate fi antrenat într-un joc, în timp ce o aplicație se actualizează în fundal iar o aplicație de alarma poate porni soneria.
Unele aplicații pot suporta rularea de widget-uri. Acestea sunt obiecte speciale ce pot fi adăugate pe ecranul principal al sistemului care permit o funcționalitate redusa a aplicației fără ca aceasta sa fie rulata în mod direct. Majoritatea widget-urile permit o anumita măsură de personalizare, de la dimensiune, la informațiile afișate sau la funcționalitatea lor în cazul interacțiunii.
Fig 2.4 Personalizarea ecranului principal cu widget-uri
Touchscreen-ul este o interfața intuitiva pentru un dispozitiv portabil. Folosit corect, acesta poate permite o gama foarte variata de interacțiuni cu dispozitivul care reduce timpul pierdut pe introducerea de date. Interacțiunea cu un touchscreen este foarte ușor de învățat, interacțiunile cu acesta devenind automatisme într-un timp foarte scurt.
Deși majoritatea tipurilor de interacțiuni au deja un set de funcții atribuite și standardizate de-a lungul timpului, unele gesturi pot fi personalizate de către aplicație, sau se poate oferi utilizatorului posibilitatea de a-și seta singur gesturile dorite:
într-o aplicație de tip Image viewer, un gest de tip pinch în/out este cunoscut ca având rolul de a da zoom în/out pe imagine
un file explorer permite selecția și accesarea/executarea de fișiere prin tap
o aplicație care prezinta un grid view cu mai multe elemente va începe printr-o acțiune de tip long-tap acțiunea de multi-select în cadrul ecranului respectiv
ES File Explorer pe lângă faptul ca utilizează on set de interacțiuni precum cele menționate mai mult permite ca diverse acțiuni sa fie direct customizate de către utilizator. Spre exemplu, se poate seta ca printr-un gest de glisare a ecranului într-o anumita forma pe ecran sa se creeze un subdirector în cel actual
Unele evenimente de tip atingeri sunt puse transparent la dispoziția dezvoltatorului fără sa fie nevoie ca acesta sa implementeze singur comportamentele lor detaliate. Gesturi personalizate pot fi definite daca este necesar.
Unele aplicații necesita o verificare a dispozitivului la rulare pentru a se asigura ca aplicația funcționează în mod corespunzător și ca nu sunt necesare adaptări. Acest lucru este permis de clasa android.os.build și prin rularea comenzii
Pentru ca o aplicație sa afle tipul de dispozitiv pe care rulează și sa de adapteze în funcție de asta, aceasta poate accesa informația pe dispozitivul țintă folosind clasa android.os.Build ca de exemplu:
if(android.os.Build.MODEL.equals("Nexus+7+LTE")) {
…
}
Hardware-ul ce suporta sistemul Android împarte niște trăsături comune datorita naturii sistemului de operare. Android este organizat în următoarele imagini:
Bootloader – Inițiază încărcarea imaginii de boot la pornire
Boot image – Kernel-ul și RAM disk
System image – Sistemul de operare android și aplicațiile
Data image – Datele utilizatorului salvate
Recovery image – Fișiere folosite pentru reconstruirea sau actualizarea sistemului
Radio image – Fișiere ce aparțin de radio
Imaginile se regăsesc într-o memorie nevolatilă flash care le protejează când dispozitivul rămâne fără energie sau începe sa nu mai răspundă. Asemeni memoriei ROM, memoria flash de obicei rămâne neschimbata dar poate fi rescrisa în cazul în care dispozitivul necesita o actualizare a sistemului de operare. Memoria flash este folosita ca și read-only-memory ( de aceea i se mai spune ROM ), dar aceasta poate fi rescrisa daca este necesar, spre exemplu cu actualizări noi de sistem.
La pornire, microprocesorul executa bootloader-ul pentru a încarcă kernel-ul și RAMdisk-ul în memoria RAM pentru acces rapid la date. Apoi microprocesorul executa instrucțiunile și porțiuni din imaginile de sistem și din data image în RAM după cum este nevoie. Imaginea radio se regăsește pe procesatorul de banda care se conectează cu componentele hardware dedicate radio-ului.
Linux Kernel
Android Runtime
Libraries
Android framework
Aplicații
Fig 2.5 Pârțile componente ale platformei android
În principiu, atât utilizatorii cat și dezvoltatorii nu interacționează decât cu ultimul strat, cel care se refera strict la aplicații, fie de sistem, fie dezvoltate și instalate din alta locație. Raportul este unul indirect intre utilizatori și dezvoltatori, încât niciuna dintre părți nu interacționează direct cu cealaltă.
Din punct de vedere al design-ului și al dezvoltării, scrierea aplicației se face în mod normal ignorând restul de procese de sistem. În mod normal o aplicație simpla nu se va folosi de alte componente de sistem iar în cazul în care sunt necesare interacțiuni cu alte aplicații, acest lucru se va face prin intermediul unei interfețe de programare a aplicației (API) care permite aplicației sa folosească diverse componente de sistem sau sa fie permis accesul la o baza de date. În comparație cu dezvoltarea unei aplicații pentru calculator, se poate observa ca pentru PC, dezvoltarea se executa în mod asemănător, însă structura și pachetele de informații sunt organizate diferit, având ca scop obținerea unui rezultat care sa fie atât funcțional cat și mai rezistent la probleme de stabilitate care ar putea periclita sistemul.
Componentele principale ale unei aplicații de android sunt: Activitățile, serviciile, furnizorii (sursele) de conținut, scopurile, stocarea, rețeaua, multimedia, serviciile de locație ai serviciile de telefonie.
Activitățile : Acestea reprezintă punctul de pornire pentru interfața grafica, cu care utilizatorul va interacționa (UI). O activitate poate fi considerata ca fiind corespondentul unei ferestre în Windows, fiind reprezentata de ecrane, dialoguri, pop-up-uri care ii indica utilizatorului ca ceva se întâmplă în aplicație. Sistemul este conceput încât sa suporte un număr ridicat de activități care nu consuma multe resurse încât un utilizator sa poată naviga cu ușurință prin aplicație sau, daca este cazul sa poată sa schimbe acțiunea din prim plan
Serviciile : Serviciile sunt componente ale aplicațiilor care sunt menite sa funcționeze dincolo de nevoile și durata de viață a activităților care pot fi oprite oricând. Un serviciu poate fi proiectat sa funcționeze independent de o activitate, lucrând în spatele aplicației pentru a se asigura ca anumite procese necesare pentru aplicație dar neimportante pentru experiența utilizatorului sa continue sa funcționeze. Spre exemplu, Serviciul unei aplicații de alarme sau a unui media player pot rula deși aplicația care l-a generat nu este în fundal. Serviciul care tine evidenta progresului unui utilizator pentru un achievement într-un joc va rula în fundal pentru a se asigura ca acesta este primit la momentul ideal.
Furnizorii (sursele) de conținut : Aceștia sunt cei care oferă aplicației conținutul extern, care nu a originat în aplicație. Conținutul poate sa vina dintr-o gama variata de locații, cu precădere, dar fără sa fie limitați la sistemul de operare în sine, un serviciu web, printr-un API (spre exemplu un CMS – Content management system), o baza de date sau chiar alte aplicații. Modul în care sistemul de operare funcționează este ca informațiile provenite dintr-o aplicație, fiind salvate de obicei direct pe mediul de stocare, ar putea fi apoi folosite de către o alta aplicație.
Scopurile: Acestea sunt mesaje de sistem care parcurg diverse trasee prin cadrul sistemului și de a notifica diversele aplicații de diverse evenimente, de la anumite modificări hardware (a fost introduc un handsfree) pana la recepționarea de date. Scopurile sunt asemănătoare evenimentelor de pe alte sisteme, însă pot fi rulate în așa fel încât pot reprezenta punctul de pornire pentru alte activități și servicii. În teorie, un scop poate reprezenta punctul de pornire pentru un lanț nelimitat de acțiuni în cadrul dispozitivului care a permite automatizarea în întregime a proceselor dorite de utilizator.
Stocarea: Mediul de stocare permite aplicației sa salveze informații care nu vor fi schimbate direct de aplicație niciodată. Se pot salva fișiere grafice (iconițe) fișiere de expansiune (.obb), texte și manuale de utilizare. O aplicație nu poate rula local fără sa folosească deloc spațiu, fie ca acesta este rezervat doar streamului de informații dintr-un loc în altul sau se bazează pe salvarea de cantități crescute de informații în fișiere locale.
Rețeaua: Dispozitivele Android sunt pregătite pentru folosirea internetului ca mediu de comunicare pentru transferul de informații de la aplicație sau sistem la o locație externa (ex. un server). Fie ca este vorba de o conexiune wireless sau prin pachet de date, aplicațiile care folosesc internetul sunt deja la ordinea zilei
Multimedia: Dispozitivele Android au abilitatea de a reda și înregistra audio și video. Deși specificațiile diferă de la un telefon la altul, se pot programa aplicații care sa folosească aceste capabilități în orice mod dorește dezvoltatorul, fie ca vrea sa redea muzica, sau sa facă o poza, sau sa înregistreze cu microfonul. Mai mult, fișierele astfel obținute pot fi la rândul lor trimise spre prelucrare, fapt care permite posibilitatea de a obține rezultate aproape complet diferite de captura inițială.
Serviciile de locație: Telefoanele cu android, frecvent au acces la furnizori de locație cum ar fi GPS-ul și triangularea poziției data de operatorul de telefonie mobila, care pot informa, cu o marja redusa de eroare, care este locația dispozitivului pe glob. Aceste servicii au o gama variata de folosințe, precum informarea utilizatorului asupra locației telefonului furat, la sisteme de asistenta la volan, la înregistrarea distantelor parcurse.
Serviciile de telefonie: Dat fiind faptul ca majoritatea dispozitivelor care folosesc sistemul Android sunt telefoane, era normal ca la un anumit punct, aplicațiile sa poată fi folosite pentru a iniția/recepționa apeluri, ajungând pana în punctul unde printr-o conexiune la internet, un utilizator sa poată sa își folosească telefonul chiar și în absenta unei rețele de telefonie mobila.
2.2 Android SDK
Android SDK este un set de unelte de dezvoltare care permit creația de aplicații pentru sistemul de operare Android. Aceste unelte includ un debugger, librarii și un emulator care sa permită rularea de aplicații intr-un mediu sigur, cat și documentații precum tutoriale și coduri exemplare de tip Hello World.
Pana în anul 2014, cel mai folosit mediu integrat de dezvoltare (eng. Integrated Development Environment) era Eclipse, care folosea plugin-ul pentru Android Development deși o serie de alte IDE-uri erau disponibile, precum IntelliJ IDEA, Netbeans (prin plugin-uri) erau disponibile și utilizate în anumite cercuri.
Începând cu 2015, Google a lansat Android Studio care a devenit IDE-ul oficial pentru dezvoltarea aplicațiilor pentru Android. Acest lucru nu a făcut altceva decât sa modifice rang-ul pe care Eclipse l-a avut pe piață. Deși Android Studio este cel mai des folosit mediu de dezvoltare, acest lucru nu înseamnă ca celelalte IDE-uri nu mai sunt folosite sau permise.
Îmbunătățirile făcute la SDK-ul ANDROI merg mână în mână cu dezvoltarea aplicațiilor pentru platforma. Deși aceste îmbunătățiri efectiv ghidează traseul pe care îl urmează aplicațiile și platforma în general, SDK-ul oferă și suport pentru versiunile mai vechi ale sistemului care permit dezvoltarea de aplicații destinate unor versiuni învechite.
2.3 Android NDK
Multe librarii care se doresc a fi rulate pe Android din păcate nu sunt create în Java ci au fost scrise în C și C++ pot fi compilate cu ARM și MIPS dar necesita instalarea prin setul de dezvoltare nativ al Androidului (eng. NDK Native Development Kit). Folosind clasa System.loadlibrary, clasele native pot fi rulate direct codul Java.
Deși aplicații complete pot fi compilate și instalate folosind instrumente tradiționale de dezvoltare, documentația Android recomanda ca folosirea kit-ului nativ sa nu fie singurul folosit în dezvoltarea unei aplicații. Cu toate ca unii dezvoltatori prefera sa lucreze în C sau în C++ în loc de Java, folosirea integral doar a kitului nativ creste complexitatea pentru majoritatea aplicațiilor, fapt care poate duce la performante scăzute pentru acestea și poate reduce și randamentul sistemului în sine.
Debugger-ul ADB permite Emulatorului Android ca în shell-ul root, sa execute cod nativ ARM sau MIPS. Compilarea directa în android a unui cod nativ este complicata în principiu de faptul ca librăria C folosita de sistem nu este una standard.
2.4 Motorul de recunoaștere Tesseract
Tesseract este un motor de recunoaștere optica a caracterelor disponibil pentru o gama larga de sisteme de operare. Lansat sub licența Apache, dezvoltarea a fost sponsorizata de către Google încă din anul 2006, fapt care i-a permis acestuia sa devina unul dintre cele mai răspândite și precise motoare de pe piață.
Dezvoltat inițial de Hewlett Packard intre anii 1985 și 1994, Tesseract se regăsea deja în topul motoarelor de căutare încă din anul 1995.
Deși inițial motorul putea recunoaște doar limba engleza, cea mai recenta lansare, din 2015 suporta o gama de peste 100 de limbi, incluzând limba chineza (atât în format modern cat și tradițional), limba japoneza, cat și limbile arabă și ebraica citite în sens de la dreapta la stânga.
Unul dintre dezavantajele motorului sunt necesitățile de a preprocesa imaginea pentru a preveni o acuratețe scăzută în rezultatul citit. Orice imagine trebuie sa fie de o dimensiune minima de 20 de pixeli pentru a putea fi citita, orice problema de orientare trebuie remediata pentru ca textul sa poată fi recunoscut, orice variații în contrast trebuie filtrate pentru a nu surveni pierderi/adaosuri în timpul procesului de binarizare , iar orice bordura neagra trebuie îndepărtată manual pentru a nu fi exista riscul ca motorul sa încerce sa o citească.
Fig 2.6 Relatia dintre dimensiunea imaginii în pixeli și precizia citirii
În JAVA, motorul Tesseract este încapsulat de către clasa TessBaseApi.
2.5 Android Studio
Fig 2.7 Android Studio Screenshot
Android Studio este IDE-ul oficial pentru platforma Android, dezvoltata și lansata gratuit de către Google în 2015. Android Studio este bazat pe un IDE anterior, InteliJ IDEA. Gratie IDE-ului InteliJ IDEA, Android Studio prezinta un compilator puternic, bazat pe un sistem Gradle și prezinta o serie de trăsături menite sa îmbunătățească experiența dezvoltatorului precum:
Un emulator puternic cu o gama larga și în continua creștere de dispozitive suportate
Instrumente de testat și debugging
Instrumente LINT pentru observarea performatei
Integrare cu GitHub și Google Cloud pentru un mai bun sistem de management și stocare a progresului pe mai multe terminale de programare.
Posibilitatea de rulare directa a aplicației fără sa fie necesara construirea unui nou apk
Suport pentru C++ și NDK.
Posibilitate de semnare a aplicațiilor pentru a putea fi lansate pe piață
Un editor de layout pentru responisve care permite vizualizarea layout-ului pe o gama variata de dispozitive și în orientări
Un mediu bogat de dezvoltare pentru orice top de dispozitiv, de la telefoane mobile și tablete la Google TV și Android Wear.
Un proiect construit în Android Studio este de obicei spart într-o varietate de module care sunt împărțite la rândul lor în 3 componente principale:
Fișiere java – acestea conțin codul scris în limbajul java
Fișiere manifest – acestea sunt fișierele AndroidManifest.xml care conțin informații despre permisiunile necesare pentru aplicație/modul, respectiv componentele cu care trebuie ca acestea sa interacționeze
Fișiere res – acestea conțin fișierele cu resurse precum layout-uri, imagini și stringuri de UI.
Sistemul Android se bazează pe fișiere Gradle care permit cotonizarea procesului de dezvoltare și sa creeze APK-uri diferite pentru aplicație care sa folosească diferite librarii și module. Practic prin implementarea sistemului gradle se poate obține o versatilitate superioara fără sa fie necesara modificarea fișierelor principale.
Capitolul 3
3.1 Definirea aplicației practice și aplicabilitatea acesteia
Scopul acestei lucrări este realizarea unei aplicații mobile, offline care folosește recunoașterea automata a caracterelor pentru a extrage text din imagini realizate cu camera aparatului mobil. Recunoașterea se face direct la nivelul telefonului, fără sa fie nevoie de o conexiune la internet, fapt care permite unui utilizator să extragă un text din orice obiect întâlnit de-a lungul zilei și să îl folosească în ce mod dorește.
3.2 Schema de funcționare a aplicației
Schema de funcționare a aplicației se poate regăsi mai jos:
Fig 3.1 Schema de functionare a aplicatiei
Funcționalitatea aplicației începe și se termină cu activitatea principala (MainActivity). Aceasta prezinta un buton de Citire care în momentul acționării va deschide camera dispozitivului. Utilizatorul apoi este liber sa fotografieze textul dorit, iar când o captura agreabilă a fost realizată, se poate avansa către următorul pas. Utilizatorul este liber sa respingă imaginea obținută, optând sa reîncerce captura până când o imagine acceptabilă a fost realizată.
Odată ce imaginea cu textul este aleasă, aceasta este ajustată pentru a putea fi mai bine citită de către librăria Tesseract. Imaginea este rotită în funcție de poziția din care s-a realizat captura iar la finalul acestei operațiuni, librăria Tesseract preia imaginea pentru a o procesa. Librăria realizează o serie de ajustări proprii prin intermediul librăriei Leptonica integrată în librăria Tess-two. După ce ajustările sunt realizate, librăria va procesa imaginea și va returna textul înapoi în activitatea principală unde va fi adăugată în câmpul de introducere text. Un utilizator va putea ulterior să preia textul de aici, să îl editeze sau sa copieze spre a fi folosit în cadrul altor aplicații.
3.3 Design-ul aplicației
Scopul aplicației este sa folosească pe cât posibil componentele de bază ale sistemului de operare încât șocul și nevoia de adaptare la o aplicație nouă să fie redus pe cât posibil. Din acest motiv, aplicația se bazează pe 2 ecrane principale, unul dependent de sistemul folosit (camera) iar unul făcut cu scopul de a oferi atât un punct de pornire cât și unul de plecare pentru procesul dorit (Main Activity).
Fig 3.2 Captura screenshot cu layout-ul pentru MainActivity
Activitatea principală folosește un layout orizontal care permite orientarea fiecărui sub element unul în continuarea celuilalt, pe orizontala. Activitatea poate fi rulata în ambele orientări, atât portrait cât și landscape și este compusă din două componente principale:
Butonul de citire – Acționarea butonului va lansa camera dispozitivului pentru a începe procesul de captura a imaginii din care se va citi text.
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="66dp"
android:text="@string/button_text"
android:layout_gravity="center" />
Tag-ul Button declara sistemului ca se va folosi un element de tip buton. În interiorul tag-ului vor fi scrise atributele specifice acestui element în cadrul sistemului prin intermediul cuvintelor cheie precum „android:”. Acestea sunt, în ordinea prezentata mai sus:
android:id="@+id/set" – acest atribut conferă butonului nostru o identitate unica ce poate fi folosita ca și referință atât în codul java cat și în continuarea fișierului xml. Prin aceasta identitate putem găsi obiectul și sa facem referință către el pentru a-i atribui orice funcții dorim.
android:layout_width="wrap_content" – atributul conferă o lățime a butonului. Prin valoarea wrap_content înțelegem ca butonul va avea o lățime cat va vi necesar sa afișeze întregul text sau conținut de fundal al acestuia.
android:layout_height="66dp" – atributul specifica înălțimea butonului. Aceasta e specificata ca fiind înalta de 66 pixeli
android:text="@string/button_text" – atributul specifica ce text va fi afișat pe buton. Acest text este importat din fișierul string.xml
android:layout_gravity="center" – atributul specifica poziția din cadrul imaginii unde butonul va fi poziționat.
Câmpul de introducere text – În cadrul acestui câmp aplicația returnează textul citit și extras de librăria Tesseract. Câmpul poate fi folosit direct de către utilizator, acesta având posibilitatea atât de a adaugă și șterge text, cât și de a edita textul, sau de a acționa o serie de opțiuni precum cut, copy sau paste.
<EditText
android:id="@+id/field"
android:layout_width="match_parent"
android:layout_height="60dp"
android:hint="@string/text_hint"
android:layout_gravity="center" />
Tag-ul EditText declara sistemului ca se va folosi un element de tip text view. În interiorul tag-ului vor fi scrise atributele specifice acestui element în cadrul sistemului prin intermediul cuvintelor cheie precum „android:”. Acestea sunt, în ordinea prezentata mai sus:
android:id="@+id/set" – acest atribut conferă text view-ului nostru o identitate unica ce poate fi folosita ca și referință atât în codul java cat și în continuarea fișierului xml. Prin aceasta identitate putem găsi obiectul și sa facem referință către el pentru a-i atribui orice funcții dorim.
android:layout_width="match_parent" – acest atribut conferă obiectului lățimea specifica. Prin valoarea match_parent, acest obiect va fi prezentat la dimensiunea maxima permisa de layout.
android:layout_height="60dp" – atributul specifica înălțimea butonului. Aceasta e specificata ca fiind înalta de 60 pixeli
android:hint="@string/text_hint" – atributul specifica ce text va fi afișat pe buton. Acest text este importat din fișierul string.xml
android:layout_gravity="center" – atributul specifica poziția din cadrul imaginii unde butonul va fi poziționat.
După cum este menționat mai sus, al doilea ecran disponibil în aplicație este cel de captura imagine. Modul în care acest ecran este prezentat este complet dependent de sistemul de operare al dispozitivului pe care se rulează aplicația:
Fig 3.3 Camera activity în orientare landscape pe un LG Nexus 5
Fig 3.4 Camera activity in orientare portait pe un Samsung Galaxy S3 mini si pe un HTC Nexus 6
3.4 Manifestul aplicației
Orice aplicație pentru Android trebuie sa conțină în folderul de root un fișier AndroidManifest.xml care uzual conține o serie de informații esențiale pe care le prezinta către sistemul de operare precum:
Numele pachetului Java ce va fi folosit ca identificator unic de către sistem
Componentele aplicației (activități, servicii, furnizori de conținut, cat și clasele lor)
Determina ce procese vor hosta componentele de sistem
Declara ce permisiuni sunt necesare pentru a rula aplicația, pentru a interacționa cu alte aplicații și ce permisiuni sa ceara alte aplicații daca este nevoie.
Declara librăriile folosite de către aplicație și nivelul de Android API cerut de aceasta
Manifestul pentru aplicația Lector conține informații despre
Permisiunile necesare pentru realizarea acțiunilor de citire și scriere a spațiului de stocare. Acestea sunt necesare pentru verificarea existentei în spațiul de stocare a fișierului de training folosit de librăria de recunoaștere. În cazul în care acest fișier nu exista, va fi copiat la rulare.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Cerințele hardware necesare în momentul rulării camerei dispozitivului. Din moment ce aplicația are nevoie de camera pentru a putea captura textul ce urmează a fi citit, este nevoie ca aceasta piesa de hardware sa poată fi apelata.
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
3.5 Construirea librăriilor pentru aplicație
Pentru realizarea scopului impus, a trebuit decisa librăria ce avea să fie folosită în cadrul aplicației. Librăria Tess-two este o versiune bazata pe Tesseract Tools pentru Android care adaugă o serie de trăsături noi în cadrul colecției de API-uri printre care și librăriile de procesare imagini de la Leptonica. Librăria Tess-two poate fi descărcată de la https://github.com/rmtheis/tess-two.
Aplicația a fost dezvoltata pe un calculator ce rulează Windows 10 însă construirea librăriei urmează în principiu aceiași pași pe orice versiune de Windows mai recenta de Windows 7.
Pentru ca librăria sa poată fi integrata în aplicație trebuie mai întâi ca aceasta sa fie construită iar pentru acest scop este nevoie de cea mai recenta versiune de Android SDK, Android NDK și de Apache Ant. După ce acestea sunt instalate/actualizate, este nevoie sa se asigure ca în Environment variables locațiile pentru ANT, NDK și SDK sunt adăugate în variabila pentru Path. De asemeni este nevoie ca în variabila de sistem JAVA_HOME sa se regăsească adresa unde a fost instalat Android SDK.
Fig 3.5 Editarea variabilei JAVA_HOME
Fig 3.6 Editarea variabilei PATH
După ce librăria este descărcata aceasta trebuie dezarimata și construita. Acest lucru se face lansând un command prompt, navigând la folder-ul unde librăria a fost dezarhivată și accesând directorul tess-two unde se rulează comanda ndk-build. Acest lucru va construi librăria spre a fi folosita intr-un proiect Java. Acest proces va dura aproximativ 20 de minute.
Fig 3.7 Inițierea construirii librăriei tess-two prin NDK
Fig 3.8 Finalizarea construirii librăriei tess-two prin NDK
După ce construirea librăriei a fost finalizata, se va repeta procedeul pentru directorul eyes-two.
Fig 3.9 Construirea modulului eyes-two prin NDK
După ce comanda de ndk-build a fost rulata și pentru directorul eyes-two, se va rula, din cadrul aceluiași command prompt comanda de actualizare a librăriei tess-two cu componentele din eyes-two prin rularea comenzii android update project –-target 1 –-path <locația directorului tess-two>.
Fig 3.10 Actualizarea librăriei tess-two cu componentele din eyes-two
Librăria va fi gata construita în momentul în care se rulează comanda finala ant release. Aceasta comanda semnează librăria și permite folosirea sa ca o componenta în aplicația care este planificata.
Fig 3.11 și 3.12 Lansarea librăriei tess-two prin Apache Ant
După ce librăria a fost construita, aceasta poate fi adăugată la proiect. Acest lucru se efectuează prin crearea unui director nou intitulat Libraries la locația unde este salvat proiectul în Android Studio. În acest nou director se va adaugă dosarul tess-two. Dat fiind faptul ca librăria a fost menita sa ruleze pe o gama de dispozitive care rulează pe o versiune minima de Android de 2.3, fișierul de tip build.gradle din librărie trebuie updatat încât conținutul sau sa corespunda cu informațiile ce sunt folosite în proiectul dezvoltat în Android Studio.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
}
}
apply plugin: 'android-library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 15
targetSdkVersion 23
}
sourceSets.main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
res.srcDirs = ['res']
jniLibs.srcDirs = ['libs']
}
}
Importul librăriei este considerat finalizat cu succes în momentul în care se va deschide fișierul settings.gradle ce se regăsește în directorul proiectului și se va adaugă linia include ':libraries:tess-two'.
include ':app'
include ':libraries:tess-two'
Din păcate, doar importul librăriei nu este destul pentru ca aplicația sa știe sa apeleze librăria în momentul în care este nevoie, motiv pentru care este nevoie ca aceasta sa fie adăugată ca dependență pentru MainActivity. Acest lucru se face după ce codul a fost scris pentru a putea avea un modul care cere dependența. Pentru a se executa acest pas se deschide File>Project Structure și se selectează un modul din lista din stânga. Se selectează ultimul tab prezent Dependecies și se selectează butonul de add + Module dependencies și se adaugă librăria tess-two.
Începând cu acest moment, librăria va putea fi folosita în cadrul aplicației iar când aceasta procesează text, va putea obține textul capturat.
3.6 Fișierul .traineddata
Încă din 2005 librăria Tesseract se regăsea în topul celor mai des folosite motoare de recunoaștere optica din industria IT datorita acurateței crescute de care da dovada. Din păcate niciun motor de recunoaștere nu a poate atinge acuratețe completa, problema care combinata cu faptul ca anumite caractere sunt disponibile doar pentru anumite limbi, poate duce la rezultate invalide. Din acest motiv, este nevoie ca diverse metode de a îmbunătăți acuratețea sa fie folosite, printre care și folosirea de dicționare specifice pentru limba pentru care se extrage text. Acest lucru este obținut prin implementarea unor fișiere de tip .traineddata care au rolul de a învață librăria cum sa recunoască texte specifice pentru o limba anume, permițând aplicației sa verifice cuvintele obținute împotriva unui set predefinit din dicționar, reducând șansele ca anumite cuvinte sa fie returnate cu caractere invalide sau ca textul sa nu aibă sens.
Pentru a implementa un dicționar specific, fișierul lang.traineddata specific limbii de care suntem interesați trebuie downloadat de la https://github.com/tesseract-ocr/tessdata și adăugat în folderul app\src\main\assets\tessdata. Pentru a mă asigura ca rezultatele sunt returnate cu o acuratețe crescută, am decis sa implementez dicționarul pentru limba engleza datorita faptului ca este cel mai folosit în lume, fapt care asigura ca va fi actualizat cu o frecventa crescuta fata de celelalte limbi.
3.7 Modul de funcționare a librăriei Tess-two
Librăria Tesseract funcționează intr-un mod asemănător cu cel prevăzut în librăria originala de Tesseract, conform schemei următoare:
Fig 3.13 Schema de funcționare a librăriei tess-two
După ca librăriei ii este furnizată imaginea spre prelucrare, începe procesul de binarizare adaptivă. Procesul de binarizare folosit în cadrul sistemului Tesseract este un procedeu numit OTSU al cărui principiu este obținerea unei valori generale care va funcționa drept prag de verificare pentru fiecare porțiune din imagine (figura cu alb-negru). Aceasta valoare se calculează pentru fiecare suprafața de 8×8 pixeli din imagine și este folosita în clasificarea porțiunilor imaginii în suprafețe albe sau negre care vor permite o citire mai ușoara a textelor. Valoarea de binarizarea este calculata prin codul regăsit în fișierul Binarize.java:
Pix pixs, int sizeX, int sizeY, int smoothX, int smoothY, float scoreFraction) {
if (pixs == null)
throw new IllegalArgumentException("Source pix must be non-null");
if (pixs.getDepth() != 8)
throw new IllegalArgumentException("Source pix depth must be 8bpp");
long nativePix = nativeOtsuAdaptiveThreshold(pixs.getNativePix(),
sizeX, sizeY, smoothX, smoothY, scoreFraction);
if (nativePix == 0)
throw new RuntimeException("Failed to perform Otsu adaptive threshold on image");
return new Pix(nativePix);
Fig 3.14 Binarizarea adaptiva pe o imagine cu intensitate in degradare
Următorul pas folosit de motorul de citire este analiza componentelor conectate. Scopul este obținerea unor contururi de caractere care vor fi ulterior folosite pentru recunoașterea directa a caracterelor din imagine prin compararea acestora cu caracterele memorate în fișierul dicționar.
Astfel, după, binarizare se obține o conturul componentelor care va fi baza pentru o aproximare poligonală. Imaginea este aproximata pentru a ne asigura ca în eventualitatea unui bruiaj luminos sau al unui text citit de pe o baza învechită unde textul poate fi parțial șters.
Fig 3.15 Obținerea conturului aproximativ
Conturul aproximat este apoi verificat împotriva setului de caractere cu care este „învățat” motorul prin intermediul fișierelor de tip .traineddata.
Fig 3.16 Verificarea conturului impotriva caracterelor prototip
Caracterele recunoscute vor fi grupate împreună în ordinea în care se găsesc și se vor verifica împotriva dicționarului salvat pentru a ne asigura ca aplicația returnează rezultate corecte atât din punctul de vedere al acurateței cat și din punct de vedere al semanticii.
Capitolul 4
4.1 Dezvoltarea aplicației
Următorul pas al aplicației va fi dezvoltarea codului care va îmbina librăria Tesseract, uneltele de sistem împreună cu restul claselor și funcțiilor pentru a returna o aplicație utilizabila. Astfel, primul pas în realizarea acestui scop este menționarea numelui pachetului așa cum va apărea în cadrul sistemului de operare.
package com.license.radu.opticalcharacterrecognition;
Acesta va fi folosit pe post de identificator unic al aplicației și va permite sistemului sa identifice de care proiect depinde rularea proceselor activității.
După ce pachetul a fost menționat, este necesara adăugarea și menționarea variilor clase și feature-uri pe care aplicația le folosește:
import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.googlecode.tesseract.android.TessBaseAPI;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
În aceasta lista, importurile înseamnă:
Import android.app.Activity – permite apelarea directa a activității fără sa mai fie mereu necesara apelarea locației activității.
Import android.content.intent – permite apelarea intentului care va permite apelarea activităților și a interacțiunii dintre cod și aplicațiile sistemului
Import android.content.res.assetmanager – permite accesul la fișierele brute ale aplicației. Aceasta clasa permite vizualizarea fișierelor care au fost împachetate cu buildul, vizualizabile sub forma unui string de biți
Import android.graphics.bitmap – permite folosirea obiectelor de tip Bitmap sa fie folosite și procesate în aplicație
Import android.graphics.bitmapfactory – permite crearea de fișiere de tip bitmap ce provin din varii surse
Import android.graphics.matrix – permite folosirea de matrice 3×3 care sa permită transformarea coordonatelor
Import android.media.exifinterface – permite citirea și scrierea TAG-urilor de tip exif în cadrul fișierelor de tip JPEG
Import android.net.uri – permite folosirea de referințe de tip URI care permit identificarea unei resurse abstracte sau fizice
Import android.os.bundle – permite maparea variilor string-uri de caractere
Import android.os.environment – permite accesul la variabilele de mediu
Import android.provider.mediastore – permite accesul la informațiile găsite atât pe spațiul de stocare intern cat și cel extern
Import android.util.log – permite logarea de rezultate în android monitor prin intermediul metodelor Log.v(), Log.e(), Log.i(), Log.d(), Log.w()
Import android view.view – permite folosirea de elemente de tip viewgroup care conține clasele de baza pentru viewere și containere.
Import android.widget.button – permite folosirea de widget-uri de tip butoane. Acestea sunt folosite de utilizator pentru a executa acțiuni
Import android.widget.edittext – permite folosirea de widget-uri de tip TextView editabil
Import java.io.file – permite folosirea de acțiuni de tipul input/output I/O
Import java.io.fileoutputstream – permite folosirea de acțiuni precum scrierea unui output stream către un fișier sau către un Filedescriptor.
Import java.io.inputstream – permite acțiuni de tipul citirii unui stream din fișiere
Import java.io.ioexception – reprezintă o clasa de excepții care sunt prinse la erori de input/output
Import java.io.outputstream – permite scrierea unui stream în fișiere
În continuare se implementează clasa publica MainActivity care extinde o activitate. De aici se declara variabilele statice publice de tip string PACKAGE_NAME și DATA_PATH care vor memora numele pachetului (com.license.radu.opticalcharacterrecognition) spre a fi folosit mai departe, respectiv locația unde se va plasa directorul cu fișiere ale aplicației. Pentru a ne asigura ca aplicația va putea sa salveze local atât fișierul .trained data cat și locația unde va fi plasata imaginea citita, am optat ca acest director sa fie poziționat în cadrul memoriei externe și va purta numele de OCR.
Pe lângă aceste variabile se mai declara variabila statica publica de tip string lang și variabila privata statica TAG și o variabila statica PHOTO_TAKEN. Variabila lang va memora limba ce va fi folosita în fișierul traineddata care pentru a ne asigura ca se returnează informații cat mai corecte va prelua valoarea eng. Variabila TAG va fi inițiată cu valoarea MainActivity.java. Aceasta variabila nu va fi folosita direct în codul aplicației, dar va fi folosita în cadrul Android Monitor pentru a putea depista o eventualele erori întâmpinate.
public class MainActivity extends Activity {
public static final String PACKAGE_NAME = "com.license.radu.opticalcharacterrecognition";
public static final String DATA_PATH = Environment.getExternalStorageDirectory().toString() + "/OCR/";
public static final String lang = "eng";
private static final String TAG = "MainActivity.java";
protected static final String PHOTO_TAKEN = "photo_taken";
Următoarea etapa este declararea elementelor de interfață ce vor fi folosite în cadrul aplicației:
protected Button _button;
protected EditText _field;
protected String _path;
protected boolean _taken;
Elementele adăugate sunt butonul de citire și text view-ul menționate la capitolul 3, un string care păstrează path-ul directorului de stocare a fișierelor aplicației și o variabilă booleana care va tine cont daca o imagine a fost capturata.
Următorul pas este acum realizarea interfeței prin intermediu metodei OnCreate care va fi afișata pe ecran. Prin intermediul comenzii savedInstanceState se salvează view-ul creat pentru a se asigura ca în momentul în care acesta iese din focus, nu se pierd informațiile.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
În continuare se va memora variabila paths având valoarea provenită din DATA_PATH la care se adaugă directorul tessdata/. Aceasta va fi directorul unde se va stoca fișierul eng.traineddata. În următoarele comenzi se va verifica daca acest director exista. Daca acesta exista, următorul set de comenzi va fi ignorat, însă daca directorul nu exista, aplicația va încerca sa îl creeze, rezultatul acestei acțiuni fiind returnat în Android Monitor cu „ERROR: Creation of directory <calea folder-ului> on sdcard failed” (în care rezultatul este negativ) sau „Created directory <calea folder-ului> on sdcared” (daca rezultatul este pozitiv).
String[] paths = new String[] { DATA_PATH, DATA_PATH + "tessdata/" };
for (String path : paths) {
File dir = new File(path);
if (!dir.exists()) {
if (!dir.mkdirs()) {
Log.v(TAG, "ERROR: Creation of directory " + path + " on sdcard failed");
return;
} else {
Log.v(TAG, "Created directory " + path + " on sdcard");
}
}
}
Mai departe se va folosi folder-ul tessdata pentru a se stoca fișierul cu dicționarul .traineddata. Prin metoda de mai jos, aplicația va verifica daca fișierul eng.traineddata se regăsește în directorul menționat mai devreme. Daca acesta exista, restul codului din metoda nu mai este aplicat, însă daca acesta nu exista, aplicația va încerca sa îl copieze din asset-urile aplicației. Prin comanda AssetManager assetManager = getAssets() se menționează ca aplicația dorește sa folosească asset-urile cu care este livrat apk-ul. Directorul de asset-uri este folosit în continuare drept input stream fiind sursa de unde se copiază fișierul .traineddata.
if (!(new File(DATA_PATH + "tessdata/" + lang + ".traineddata")).exists()) {
try {
AssetManager assetManager = getAssets();
InputStream în = assetManager.open("tessdata/eng.traineddata");
OutputStream out = new FileOutputStream(DATA_PATH + "/tessdata/eng.traineddata");
Copierea se efectuează byte cu byte, din assets în tessdata. Din moment ce aplicația nu poate ști dimensiunea informațiilor din fișierul copiat, mulțimea byte este inițiată la 1024.
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
Log.v(TAG, "Copied " + lang + " traineddata");
} catch (IOException e) {
Log.e(TAG, "Was unable to copy " + lang + " traineddata " + e.toString());
}
}
La finalul acestei metode se va returna în Android Monitor statusul operațiunii, acesta fiind Copied eng.traineddata (în cazul în care copierea s-a finalizat cu succes) sau Was unable to copy eng.traineddata (în cazul în care copierea nu s-a finalizat cu succes)
La finalul clasei onCreate se setează ce conținut va fi afișat, respectiv ce parametri și layout vor folosi, prin intermediul comenzii setContentView. Implicit trebuie menționate și elementele de UI de mai devreme, TextView-ul și butonul din main_activity.xml.
Butonul este un obiect care are două stări și anume “activ” și “inactiv”. Acesta este atribuit prin comanda findViewById obiectului button cu id-ul „set”. În continuare, acestuia ii este setat un OnClickListener care la activare va executa comenzile aflate în metoda ButtonClickHandler(). Aceasta va fi explicata imediat.
setContentView(R.layout.activity_main);
_field = (EditText) findViewById(R.id.field);
_button = (Button) findViewById(R.id.button);
_button.setOnClickListener(new ButtonClickHandler());
_path = DATA_PATH + "/ocr.jpg";
Prin clasa ButtonClickHandler se menționează ce se întâmplă la activarea butonului, și anume faptul ca se pornește camera dispozitivului, startCameraActivity().
public class ButtonClickHandler implements View.OnClickListener {
public void onClick(View view) {
Log.v(TAG, "Starting Camera app");
startCameraActivity();
}
}
În clasa startCameraActivity se specifica faptul ca se vor acorda URI al imaginii create prin captura, fișierului localizat în _path care a fost menționat mai devreme în clasa onCreate. Subclasa uri este folosita pentru a crea referințe uri. Acronimul Uri vine de la Uniform resource identifier și este un sir de caractere care este folosit pentru a identifica numele unei resurse.
La finalul acestei clase se va afișa în Android Monitor end startcameraact pentru a semnala faptul ca activitatea a fost finalizata cu succes.
protected void startCameraActivity() {
File file = new File(_path);
Uri outputFileUri = Uri.fromFile(file);
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(intent, 0);
Log.v(TAG, "end startcameraact");
În timp ce activitatea de camera este afișata, aplicația trebuie sa tina cont daca imaginea capturata de utilizator va fi într-adevăr folosita de către acesta. Acest lucru este observat prin intermediul clasei onActivityResult care returnează un mesaj de eroare în Android Monitor (User cancelled) în cazul în care utilizatorul a revocat imaginea capturata, repornind CameraActivity sau va transfera informațiile despre imagine în cazul în care utilizatorul a acceptat imaginea capturata:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "resultCode: " + resultCode);
if (resultCode == -1) {
onPhotoTaken();
} else {
Log.v(TAG, "User cancelled");
}
}
Din moment ce CameraActivity nu mai este utila în momentul de față, aceasta poate fi închisa, aplicația revenind la MainActivity prin intermediul metodei:
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean(MainActivity.PHOTO_TAKEN, _taken);
Aceasta se va asigura atât ca se restaurează activitatea salvata în onCreate cat și se asigura ca variabila PHOTO_TAKEN primește valoarea din _taken.
La următorul pas, prin intermediul clasei onRestoreInstanceState se menționează ce se va întâmplă în activitatea principala în cazul în care exista o imagine capturata și aprobata de utilizator.
Aplicația va folosi metoda BitmapFactory.Options pentru a obține o imagine bitmap bazata pe imaginea capturata de către utilizator. Aceasta imagine va fi de aproximativ patru ori mai îngustă și mai mica decât imaginea originala și va avea un număr de pixeli de 16 ori mai mic. Acest lucru este făcut pentru ca în cursul următoarelor metode, aplicația sa nu rămână în hang, sau mai rău, sa crasheze deoarece procesul de ajustarea imaginii durează prea mult.
protected void onPhotoTaken() {
_taken = true;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(_path, options);
În continuare, aplicația va calcula orientarea din care imaginea a fost capturata. Acest lucru va fi folosit ulterior pentru a se asigura ca intr-un final, în cadrul librariei tess-two se vor furniza informații care pot fi citite, având caracterele într-o poziție corecta. Altfel, prin intermediul informațiilor de orientare EXIF se poate calcula direcția în care sa se reorienteze imaginea în cazul în care este nevoie. Aici rezulta 4 cazuri:
Imaginea este în orientarea corectă, iar ajustări nu sunt necesare
Imaginea este în orientarea greșită, fiind decalata cu 90 de grade
Imaginea este în orientarea greșită, fiind decalata cu 180 de grade
Imaginea este în orientarea greșită, fiind decalata cu 270 de grade
try {
ExifInterface exif = new ExifInterface(_path);
int exifOrientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
Log.v(TAG, "Orient: " + exifOrientation);
int rotate = 0;
switch (exifOrientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
}
Log.v(TAG, "Rotation: " + rotate);
Finalul metodei EXIF va returna în Android Monitor „Rotation <rotatia necesara>”.
După ce a fost calculata orientarea necesara, este nevoie ca ajustările de orientare sa fie implementate. Pentru a face acest lucru este nevoie ca mai întâi sa se obțină informații despre înălțimea și lățimea imaginii din captura. Acest lucru este realizat prin intermediul metodei
if (rotate != 0) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Când acestea au fost obținute, se va genera o matrice pe baza lor care va conține imaginea în starea de pre rotație:
Matrix mtx = new Matrix();
mtx.preRotate(rotate);
Folosind matricea creata se poate implementa comanda de rotire a imaginii, al cărei rezultat va fi procesat pentru a extrage textul din obiectul imagine.
bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false);
În cazul în care aplicația nu poate din varii motive sa aplice comenzile de rotație, în Android Monitor se va afișa textul „Couldn’t correct Orientation”.
} catch (IOException e) {
Log.e(TAG, "Couldn't correct orientation: " + e.toString());
După cum a fost menționat mai devreme, librăria Tesseract este una dintre cele mai versatile de pe piață, însă acest lucru nu înseamnă ca nu ar trebui sa încercam sa furnizam o calitate cat mai buna a imaginilor. Un mod prin care calitatea imaginii poate fi crescuta în sistemul android este prin convertirea imaginii într-un format mai detaliat. În acest scop, aplicația va transforma imaginea creata în format ARGB_8888. În acest format de bitmap, fiecare canal de culoare, incluzând alfa este stocat pe un set de 8 biți fapt care permite o flexibilitate crescuta, cu 256 de rezultate valabile. Momentan, acesta este formatul cu cea mai buna calitate.
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Având imaginea ajustata atât din punct de vedere al orientării și convertita într-o calitate crescută, este timpul sa se folosească librăria Tesseract prin intermediul metodei:
TessBaseAPI baseApi = new TessBaseAPI();
baseApi.setDebug(true);
baseApi.init(DATA_PATH, "eng");
baseApi.setImage(bitmap);
String recognizedText = baseApi.getUTF8Text();
baseApi.end();
Log.v(TAG, "OCRED TEXT: " + recognizedText);
În cadrul motorului de recunoaștere, imaginea va trece printr-o serie de etape care o vor transforma treptat și va returna în final textul conținut în imagine, de la binarizarea adaptiva, la analiza componentelor selectate, la găsirea cuvintelor și la verificările, urmând sa fie returnat ca output către aplicație.
Prin comanda TessBaseAPI baseAPI = new TessBaseApi() se inițiază librăria și se asigura ca în procesul începe fără sa folosească orice fel de informații anterioare.
Prin comanda baseApi.setDebug(true) se asigura ca orice eroare care ar apărea în cursul rulării este captata de către Android Monitor. Debug mode-ul poate fi trecut ca fiind false când aplicația este considerata bug free.
BaseApi.setImage(bitmap) precizează care imagine se va folosi în procesul de recunoaștere. Aceasta imagine este cea rezultata în cursul metodei anterioare furnizata în format ARGB_8888.
String recognizedText = baseApi.getUTF8Text() inițiază variabila de tip string recognizedText care va stoca textul obținut în timpul recunoașterii.
Procesul de recunoaștere se finalizează prin intermediul comenzii baseApi.end() care eliberează memoria folosita de către motor, după ce textul a fost salvat în variabila recognizedText.
Când procesul este terminat, Android Monitor va returna un set de informații care conține textul obținut prin intermediul comenzii Log.v(TAG, "OCRED TEXT: " + recognizedText).
Acum, având textul din imagine, acesta trebuie returnat și afișat către utilizator. Acest lucru se va realiza prin intermediul
if ( lang.equalsIgnoreCase("eng") ) {
recognizedText = recognizedText.replaceAll("[^a-zA-Z0-9]+", " ");
}
Acest set de comenzi va asigura ca daca limba setata este eng se va face o ultima verificare care va afișa o versiune alfanumerica din care se vor reduce caracterele în plus sau care sunt provenite din diversele defecte ale obiectului fotografiat.
Curățarea textului este finalizata rulând metoda:
if ( recognizedText.length() != 0 ) {
_field.setText(_field.getText().toString().length() == 0 ? recognizedText : _field.getText() + " " + recognizedText);
_field.setSelection(_field.getText().toString().length());
}
Astfel, textul găsit în imagine este returnat în TextView-ul aflat în MainActivity. Daca în TextView nu se regăsește niciun text, rezultatul va fi afișat ca atare. Daca în schimb, în TextutView se regăsește deja un text, fie adăugat de utilizator, fie adăugat în urma unei citiri anterioare, după acest text se va adăuga un spațiu pentru a diferenția cele doua string-uri.
4.2 Probleme întâmpinate
Pe parcursul dezvoltării aplicației au fost întâmpinate o serie de probleme, cauzate de diverși factori. Printre aceste probleme, cele mai dăunătoare au fost cele care au afectat aplicația pe un plan arhitectural. Dintre acestea amintim:
Probleme în crearea/salvarea fișierelor din .png și .traineddata.
Principala problema în acest caz a fost cauzata de o schimbare în arhitectura sistemului de operare Android, survenita cu apariția versiunii 6.0 ce rulează pe API level 23. Începând cu API level 23, aplicațiile nu mai cer permisiuni din partea utilizatorului la instalare ci pe măsura ce diverse componente necesita diversele componente de sistem. Acest lucru va cauza ca aplicația sa crash-eze la acceptarea imaginii capturate, datorita inabilității de a accesa un fișier inexistent (eng.traineddata).
Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/OCR/ocr.jpg: open failed: EACCES (Permission denied)
Rezolvare: Un mod prin care aplicația poate fi făcută sa ruleze pe sisteme cu API level 23 este prin activarea directa a permisiunii de a scrie/citi conținutul utilizatorului.
Fig 4.1 Permisiunile de stocare pentru aplicație
Probleme în acuratețea imaginii obținute, cauzate de obiectul fotografiat.
Problema cauzata aici se datorează mediului fotografiat, care conține textul pe care îl căutam. După cum am menționat la capitolul 3, deși este una dintre cele mai bune librarii de recunoaștere optica a caracterelor, Tesseract nu poate sa atingă o acuratețe de 100%, mai ales în condițiile în care aplicația fotografiază un mediu mai întunecat care ar putea împiedica procesul de binarizare.
În contextul de fata imaginea fotografiata a fost a unui text găsit pe o suprafață luminoasa care la interacțiunea cu blitz-ul dispozitivului a dus la generarea unei serii de pete cu variații de alb-n-gri-negru. Acest lucru nu a permis obținerea unei valori de binarizare corecte, rezultatul fiind unul ilizibil
Fig 4.2 si 4.3 Problema cauzata de interpretarea ratei de refresh a ecranului ca zgomot care influențează procesul de binarizare
Rezolvare: Aceasta problema nu poate fi rezolvata din cod fără sa riște apariția altor potențiale probleme. Daca se implementează metode de reducere a zgomotului din fotografie, acestea risca sa cauzeze alte probleme unde elemente corecte sunt îndepărtate împreună cu zgomotul din imagini.
Probleme cauzate de rularea pe un dispozitiv cu memorie insuficienta
Aceasta problema ar putea fi întâmpinată doar în rularea pe simulator. În cazul în care aplicația încearcă sa ruleze pe un dispozitiv care nu are suficienta memorie alocata încât sa poată salva fișierul de tip .bitmap, se poate ajunge în situația în care procesul sa eșueze cu un crash. Un fișier de tipul ARGB_8888 rulat ar putea sa genereze un fișier de ~ 19 Mb care va putea cu ușurință sa supraîncarce spațiul destinat pentru aplicație în sine:
java.lang.OutofMemoryError: bitmap size exceeds VM budget
Rezolvare: După cum am menționat la început, problema poate fi întâmpinata numai pe dispozitive rulate în simulator. Date fiind dispozitivele de pe piață care în momentul de fata pot ajunge sa depășească un spațiu de stocare de 64 GB, o astfel de eroare nu mai reprezintă o problema reala în ziua de azi.
4.3 Rezultate
Mai jos se vor regăsi o serie de imagini realizate după ce aplicația a fost finalizata, împreună cu comentarii unde este cazul:
Fig 4.4 si 4.5 Citirea textului de pe un colet. O eroare a intervenit unde, datorita italicizării, un spațiu a fost perceput in mijlocul cuvântului ROMANIA
Fig 4.4 si 4.5 Citirea unui text de verificare. Textul citit este folosit deoarece conține toate literele alfabetului
Fig 4.4 si 4.5 Citirea unui text de pe un ghid de asamblare pentru un scaun de la birou de la IKEA
Concluzii
Scopul acestei lucrări este de a explora opțiunile disponibile pentru dezvoltarea unei aplicații de recunoaștere optica a caracterelor pentru platforma Android.
Deși motorul de recunoaștere suferă în fata unor probleme de procesare și preprocesare, care ii fac implementarea destul de dificila, viitorul acestei tehnologii este unul optimist.
Prototipul creat în cadrul acestei lucrări este menit sa evidențieze atât competentele librăriei Tesseract în dezvoltarea unei aplicații de recunoaștere și extragere a obiectelor de tip text din imagini, cât și ca punct de pornire pentru un produs final spre a fi publicat pe Google.
În viitor, consider necesara o serie de îmbunătățiri ce ar trebui aduse la procesorul de imagini pentru a creste precizia cu care sunt recunoscute textele. Consider ca introducerea unei metode care sa preia imaginea și sa ii aplice un filtru grayscale va putea reduce din elementele care sunt pierdute în timpul binarizării, iar algoritmi de verificare și îndreptare a problemelor de așezare și orientare în pagina pe o axa oblica vor permite ca mai multe texte vor putea fi citite și identificate corect. Un ultim adaos ce ar merita făcut este adăugarea unui pas de a face crop pe imagine înainte de a o trimite care procesare. În felul acesta, se pot elimina problemele survenite de la imagini în care se pot observa borduri întunecate care ar risca sa fie percepute ca zgomot.
În materie de alte trăsături pe care îmi doresc sa le adaug la aplicație pe lângă îmbunătățirile către motorul de recunoaștere sunt:
Posibilitatea de a face citiri și pe imagini provenite din spațiul de stocare local al dispozitivului.
Integrare cu o platforma cloud.
Posibilitatea de a citi și genera coduri QR
Posibilitatea de a distribui rezultatele citirii către alte aplicații sau către rețele de socializare
Data fiind răspândirea larga de care a dat dovada în ultimii ani platforma Android, combinata cu posibilitatea de elaborare a aplicației va garanta existența unui public.
Bibliografie
”Moore’s Law to roll on for another decade” – M. Kanellos (2003)
”How Good Can It Get? Analysing and Improving OCR Accuracy in Large Scale HistoricNewspaper Digitisation Programs” – R. Holley (2009)
CVision Tech – http://www.cvisiontech.com/reference/general-information/ocr-applications.html
”The History of OCR: Optical Character Recognition” – H.F. Schantz (2012)
”Reading Machine Spells Out Loud” – M. Mann (1949)
”OpticalCharacterRecognition(OCR)” – R Ahmad (2012)
”Tesseract: an Open-Source Optical Character Recognition Engine – A Kay (2007)
OCR Test – https://play.google.com/store/apps/details?id=edu.sfsu.cs.orange.ocr&hl=en
ABBYY text reader -https://play.google.com/store/apps/details?id=com.abbyy.mobile.bcr.lite&hl=en
Building an OCR App using the Tess Two library http://gaut.am/making-an-ocr-android-app-using-tesseract
Tess Two library – https://github.com/rmtheis/tess-two
Android Developers – https://developer.android.com/index.html
Anexe
Anexa 1 – Cod MainActivity.java
package com.license.radu.opticalcharacterrecognition;
import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import com.googlecode.tesseract.android.TessBaseAPI;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class MainActivity extends Activity {
public static final String PACKAGE_NAME = "com.license.radu.opticalcharacterrecognition";
public static final String DATA_PATH = Environment.getExternalStorageDirectory().toString() + "/OCR/";
public static final String lang = "eng";
private static final String TAG = "MainActivity.java";
protected Button _button;
protected EditText _field;
protected String _path;
protected boolean _taken;
protected static final String PHOTO_TAKEN = "photo_taken";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] paths = new String[] { DATA_PATH, DATA_PATH + "tessdata/" };
for (String path : paths) {
File dir = new File(path);
if (!dir.exists()) {
if (!dir.mkdirs()) {
Log.v(TAG, "ERROR: Creation of directory " + path + " on sdcard failed");
return;
} else {
Log.v(TAG, "Created directory " + path + " on sdcard");
}
}
}
if (!(new File(DATA_PATH + "tessdata/" + lang + ".traineddata")).exists()) {
try {
AssetManager assetManager = getAssets();
InputStream in = assetManager.open("tessdata/eng.traineddata");
OutputStream out = new FileOutputStream(DATA_PATH + "/tessdata/eng.traineddata");
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
Log.v(TAG, "Copied " + lang + " traineddata");
} catch (IOException e) {
Log.e(TAG, "Was unable to copy " + lang + " traineddata " + e.toString());
}
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_field = (EditText) findViewById(R.id.field);
_button = (Button) findViewById(R.id.button);
_button.setOnClickListener(new ButtonClickHandler());
_path = DATA_PATH + "/ocr.jpg";
}
public class ButtonClickHandler implements View.OnClickListener {
public void onClick(View view) {
Log.v(TAG, "Starting Camera app");
startCameraActivity();
}
}
protected void startCameraActivity() {
File file = new File(_path);
Uri outputFileUri = Uri.fromFile(file);
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
startActivityForResult(intent, 0);
Log.v(TAG, "end startcameraact");
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.i(TAG, "resultCode: " + resultCode);
if (resultCode == -1) {
onPhotoTaken();
} else {
Log.v(TAG, "User cancelled");
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putBoolean(MainActivity.PHOTO_TAKEN, _taken);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.i(TAG, "onRestoreInstanceState()");
if (savedInstanceState.getBoolean(MainActivity.PHOTO_TAKEN)) {
onPhotoTaken();
}
}
protected void onPhotoTaken() {
_taken = true;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(_path, options);
try {
ExifInterface exif = new ExifInterface(_path);
int exifOrientation = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
Log.v(TAG, "Orient: " + exifOrientation);
int rotate = 0;
switch (exifOrientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
}
Log.v(TAG, "Rotation: " + rotate);
if (rotate != 0) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix mtx = new Matrix();
mtx.preRotate(rotate);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false);
}
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
} catch (IOException e) {
Log.e(TAG, "Couldn't correct orientation: " + e.toString());
}
Log.v(TAG, "Before baseApi");
TessBaseAPI baseApi = new TessBaseAPI();
baseApi.setDebug(true);
baseApi.init(DATA_PATH, "eng");
baseApi.setImage(bitmap);
String recognizedText = baseApi.getUTF8Text();
baseApi.end();
Log.v(TAG, "OCRED TEXT: " + recognizedText);
if ( lang.equalsIgnoreCase("eng") ) {
recognizedText = recognizedText.replaceAll("[^a-zA-Z0-9]+", " ");
}
recognizedText = recognizedText.trim();
if ( recognizedText.length() != 0 ) {
_field.setText(_field.getText().toString().length() == 0 ? recognizedText : _field.getText() + " " + recognizedText);
_field.setSelection(_field.getText().toString().length());
}
}
}
Anexa 2 – AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.license.radu.opticalcharacterrecognition">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
</manifest>
Anexa 3 – main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:weightSum="1">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="66dp"
android:text="@string/button_text"
android:layout_gravity="center" />
<EditText
android:id="@+id/field"
android:layout_width="match_parent"
android:layout_height="60dp"
android:hint="@string/text_hint"
android:layout_gravity="center" />
</LinearLayout>
Anexa 4 – Strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello</string>
<string name="app_name">Lector</string>
<string name="button_text">Citeste</string>
<string name="text_hint">Textul citit va aparea aici</string>
</resources>
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Dezvoltarea Unei Aplicatii PE Platforma Android Pentru Extragerea Obiectelor DIN Imagini (ID: 114018)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
