Proiectare De Soft In Timp Real

  • Uploaded by: Radu
  • 0
  • 0
  • June 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Proiectare De Soft In Timp Real as PDF for free.

More details

  • Words: 12,867
  • Pages: 53
www.cartiaz.ro – Carti si articole online gratuite de la A la Z PROIECTARE DE SOFT IN TIMP REAL Bibliografie obligatorie: • Tiberiu Coroescu, Mirela-Maria Coroescu, Silvestru Lucian Constantin – “Proiectare de soft in timp real cu aplicatie in imagistica medicala”, Ed. Didactica si Pedagogiga, Bucuresti, 2006 CAP. 1. Principii si strategii ale proiectarii performante de soft Curs 1 1.1. Principii si strategii ale proiectarii de soft in timp real Structurile software in timp real nou concepute trebuie sa respecte atat caracteristicile de proiectare ale oricarui soft comercial obisnuit, cat si caracteristicile de testare, fiabilitate si timp critic impuse de utilizarea sa in timp real. Ca urmare la proiectarea de soft in timp real se vor respecta urmatoarele 8 principii: 1. Compactitatea, ce impune conceperea unui soft concis, avand o structura bine stabilita de la inceputul proiectarii sale, cu evidentierea clara a domeniului specific de utilizare. De obicei proiectarea se realizeaza de catre o echipa complexa de specialisti, caz in care este critica o comunicare excelenta intre membrii echipei de proiectare. De asemeni este necesara intelegerea si respecatrea tuturor cerintelor utilizatorilor cu integrarea lor intr-o strctura compacta de soft. 2. Expandabilitatea, ce impune ca noul soft sa fie capabil sa integreze noi trasaturi care sai extinda aria de aplicabilitate. 3. Mentenanta, sau intretinerea sigura, ce impune claritate in stilul de scriere al softului care sa permita intretinerea sigura si corectarea cu usurinta a eventualelor defecte (“bugs”) descoperite ulterior. 4. Comprehensiunea, sau intelegerea cu usurinta a logicii de realizare si a bibliotecilor soft specializate integrate. In acest fel potentialii utilizatori vor putea sa-si construiasca cu usurinta propriile aplicatii si chiar propriile sisteme de dezvoltare sofware. 5. Stabilitatea, ce impune conceperea unui soft capabil sa ruleze pe o perioada de timp extinsa fara avarii, fara pierderi ale memoriei si fara anomalii inexplicabile. 6. Autotestarea, ce impune ca softul conceput sa contina subrutine de testare automata a parametrilor, subrutine activate atat la initializare, cat si periodic in timpul utilizarii sale in timp real. 7. Fiabilitate critica, ce impune ca noul soft sa fie extrem de fiabil in conditii dificile de functionare in timp real, fiabilitae verificata prin simularea defectiunilor posibile si prin rularea de teste functionale riguroase inainte de lansarea softului. 8. Feed-back critic, ce impune o viteza critica de reactie la aparitia solicitarilor in timp real. Strategia de proiectare pentru softul in timp real, bazata pe ciclul de dezvoltare al noului produs software, respecta urmatoarele reguli: a) se stabileste structura de baza sau linia principala de proiectare ce va fi respectata in mod obligatoriu de toti cei implicati in procesul de proiectare; b) se stabileste un cadru de testare unitar , cu o structura cat mai simpla, care sa fie respectat de toti proiectantii de module soft in timp real; c) se construiesc prototipuri soft foarte simple care sunt rulate rapid, asa incat se pot corecta imediat; d) se parcurg toate testele folosind instrumentele soft, fie cele existente in bibliotecile soft fie cele nou proiectate. 1

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Principiile si strategiile de proiectarea de soft in timp real vor fi implementate pentru o aplicatie de imagistica digitala, respectiv procesarea performanta a imaginilor digitale. Se vor concepe, testa si implementa modulele soft in cod sursa folosind limbajul, tehnicile si bibliotecile C++ pentru generarea unor imagini concentrate (“thumbnail”) ale imaginilor cadru digitale clasice (“image framework”), asa dupa cum se vede in fig. 1.1.

Fig. 1.1. Dezvoltarea aplicatiilor de imagine concentrata “thumbnail” Curs 2 2.1. Concepte de baza privind procesarea imaginilor Procesarea imaginilor reprezinta o aplicatie soft care primeste la intrare o imagine, realizeaza apoi o serie de manipulari ale acelei imagini, iar in final produce o imagine rezultanta care poate diferi sau nu de imaginea originala. Imaginile utilizate la procesarea imaginilor pot fi de diferite tipuri si anume: imagini alb-negru (gri) de 8 sau 32 biti si imagini color. O imagine alb-negru de 8 biti consta din pixeli (unitati elementare ale imaginii) avand diferite nivele de gri dar fara sa contina culoare. Un pixel contine informatia video pentru un singur punct al imaginii si are o valoare discreta cuprinsa intre 0 (negru) si 255 (alb). Valorile cuprinse intre 0 si 255 sunt nivele variabile de gri. Se specifica marimea imaginii prin latime (“width” – pe axa x) si prin inaltime (“height” – pe axa y), iar originea imaginii(0,0) prin coltul din stanga sus, asa dupa cum este ilustrat in fig. 2.1. Fig. 2.1.. Proprietatile imaginilor alb-negru In cadrul prototipului soft initial se va reprezenta o imagine alb-negru ca o matrice bidimensionala de pixeli, iar fiecare pixel ca o cantitate anonima de 8 biti (“ unsigned char “). Imaginile color pot fi reprezentate in multe moduri, dar cele mai folosite sunt RBG si HIS. Imaginea color RGB este o imagine de 24 biti in care fiecare pixel consta din 8 biti de rosu (“Red), 8 biti de verde (“Green”) si 8 biti de albastru (“Blue”), fiecare cu o valoare intre 0 (lipsa culoare) si 255 (culoare pura). Prin combinarea luminii acestor trei culori primare se pot produce toate culorile detectabile de catre om. Imaginea color HIS (“Hue – Saturation – Intensity”) foloseste 3 proprietati ale culorii: nuanta-saturatie-intensitate. Acest spatiu de culoare mimeaza modul in care ochiul uman percepe culoarea. Nuanta culorii reprezinta reprezinta locul ocupat de aceasta in portiunea vizibila a spectrului radiatiei electromagnetice. Nuanta se poate reprezenta ca un unghi pe un cerc al culorilor, asa dupa cum se prezinta in fig. 2.2. Fig. 2.2.. Nuanta reprezentata pe cercul culorilor

Saturatie culorii este determinata de cat de mult este mixata culoarea cu gri si alb. O culoare complet saturata nu contine gri si alb, ci doar culoarea in sine. Pe masura ce griul si albul sunt mixate in culoare, aceasta devine mai putin saturata. De exemplu, culoarea unei lamai este un galben complet saturat, pa cand culoarea unei banane este un galben putin saturat. Intensitatea culorii mai este numita si luminanta in anumite programe grafice si reprezinta stralucirea culorii. Deoarece reprezentarea HIS nu este destul de rapida pentru functionarea in timp real vom 2

www.cartiaz.ro – Carti si articole online gratuite de la A la Z folosi in proiectarea noului soft reprezentarea culorii in spatiul RGB. Ca si la imaginile pe scala de gri, marimea unei imagini color folosind spatiul RGB reprezinta latimea si inaltimea sa in pixeli, iar originea (0,0) pentru axele x,y este coltul din stanga sus, dupa cum se vede in fig. 2.3 unde s-a reprezentat culoarea galben pal. Fig.2.3. Proprietatile imaginii color folosind spatiul de culoare RGB

Curs 3 3.1. Proiectarea clasei soft a imaginilor normale Image Class Clasa soft a imaginilor normale Image Class este de tip apImage si are, din punct de vedere al proiectarii, urmatoarele 5 proprietati: 1. Este o matrice bi-dimensionala de pixeli cu nuante de gri, iar fiecare pixel poate fi exprimat ca o cantitate anonima de 8 biti (“unsigned char”). Marimea unei imagini este specificata prin latimea si inaltimea sa; 2. Permite accesul aleator la citirea / scrierea pixelilor din imagine. Fiecare pixel din imagine este descris ca locatie prin coordonatele sale (x,y). Se specifica originea imaginii de coordonate (0,0) ca fiind situata in coltul din stanga sus, iar pixelul de coordonate (width-1, height-1) este in coltul din dreapta jos; 3. Foloseste o schema simpla de alocare a memoriei pentru datele imaginii. Memoria pentru o imagine a carei dimensiune x este “width” iar dimensiune y este “height” va fi alocata astfel: unsigned char* pixels = new unsigned char [width * height]; 4. Adresa oricarui pixel (x,y) din imagine va fi: unsigned char* address = pixels + y*width + x; 5. Genereaza exceptia/obiectia C++ “rangeError” la orice referire de pixeli care nu exista in interiorul imaginii. Definirea clasei soft de aplicatie pentru imaginile normale “apImage”, cu respectarea proprietatilor anterioare, se face in cadrul fisierului header pentru imagine”_ image_h_” astfel: #ifndef _image_h_ #define _image_h_ class apImage { public: apImage (); apImage (int width, int height); ~apImage (); apImage (const apImage& src); apImage& operator= (const apImage& src); void swap (apImage& src); void setPixel (int x, int y, unsigned char pixel); unsigned char getPixel (int x, int y) const; bool isValid () const { return pixels_ != 0;} 3

www.cartiaz.ro – Carti si articole online gratuite de la A la Z int width () const { return width_;} int height () const { return height_;} class rangeError {}; private: void init (); void cleanup (); int width_; int height_; unsigned char* pixels_;

// Image width // Image height // Image data

}; #endif

// _image_h_

Curs 4 4.1. Implementarea clasei soft a imaginilor normale Image Class Implementarea clasei apImage este acum simplu de realizat prin urmatoarea succesiune de functii: pentru alocarea si desalocarea memoriei, pentru constructori si distructori, pentru operatorul de alocare si interpretorul de copiere, ca si pentru citirea si inscrierea pixelilor imaginii. •

Alocarea si desalocarea memoriei se va realiza prin functiile init() si cleanup() in loc sa duplicam codul de fiecare data cand datele imaginii sunt alocate sau sterse. Functia init() foloseste operatorul new pentru alocarea memoriei de stocare a datelor imaginii avand dimensiunea width * height. Functia cleanup() este functia ortogonala de stergere a memoriei alocate. Ele sunt prezentate in programul urmator: void apImage::cleanup () { delete [] pixels_; width_ = 0; height_ = 0; pixels_ = 0; } void apImage::init () { if (width_ > 0 && height_ > 0) pixels_ = new unsigned char [width_ * height_]; }

4

www.cartiaz.ro – Carti si articole online gratuite de la A la Z •

Constructorii si distructoriii se realizeaza simplu prin folosirea acelorasi functii init() si cleanup() dupa cum se arata in programul urmator:

apImage::apImage () : width_ (0), height_ (0), pixels_ (0) {} apImage::apImage (int width, int height) : width_ (width), height_ (height), pixels_ (0) { init (); } apImage::~apImage () { cleanup (); } Constructorul implicit pentru apImage creeaza o imagine nula, adica o imagine care nu are alocata memorie si prezinta inaltime si latime zero. Imaginile nule sunt totusi obiecte foarte utile si necesare deoarece unele operatii de procesare a imaginilor pot avea in mod justificat un efect nul. De exemplu, rutinele de procesare a imaginilor opereaza adesea pe un set particular de pixeli prezent in imagini multiple. Daca nu exista o suprapunere a pixelilor din toate imaginile multiple, atunci se revine din rutina cu o imagine nula. O imagine nula este generata de asemeni in cazul cand apare o eroare in timpul operatiei de procesare a imaginii. Functia isValid() testeaza daca apare o imagine nula, asa dupa cum se prezinta in programul urmator: apImage input; … apImage output = input.arbitraryAlgorithm (args); if (!output.isValid()) { // Error processing. arbitraryAlgorithm failed. } •

Operatorul de alocare si interpretorul de copiere trebuie sa fie definit de proiectant pentru ca cel generat de catre compilator in mod automat realizeaza o copie a clasei de date, care copiaza intr-un mod incorect pointerul de date al imaginii “pixels_”. Se pot folosi functiile memcpy si std::copy() din biblioteca standard a limbajului C pentru cresterea vitezei softului de multiplicare a datelor imaginii astfel:

apImage::apImage (const apImage& src) : width_ (0), height_ (0), pixels_ (0) { if (src.isValid()) { // Recreate the information in the source image width_ = src.width (); height_ = src.height (); init (); memcpy (pixels_, src.pixels_, width_ * height_); 5

www.cartiaz.ro – Carti si articole online gratuite de la A la Z } } apImage& apImage::operator= (const apImage& src) { if (&src != this) { // Delete any existing data and recreate the source image cleanup (); width_ = src.width (); height_ = src.height (); init (); memcpy (pixels_, src.pixels_, width_ * height_); } return *this; } Se constata ca desi operatorul de alocare si interpretorul de copiere implementat este rapid, el foloseste o portiune redundanta de cod. Pentru eliminarea redundantei se foloseste tehnica Sutter care defineste functia swap() prin care se comuta (“switch”) datele a doua imagini obiect de tip apImage dupa cum se prezinta in implementarea urmatoare: template void swap (T& a, T& b) { T copy(a); a = b; b = copy; } void apImage::swap (apImage& src) { ::swap (width_, src.width_); ::swap (height_, src.height_); ::swap (pixels_, src.pixels_); } apImage& apImage::operator= (const apImage& src) { apImage temp (src); swap (temp); return *this; } Functia sablon “swap<>” schimba pur si simplu datele a doua obiecte de acelasi tip. Ea ne permite sa implementam o metoda swap() pentru clasa apImage care sa schimbe toate datele unui 6

www.cartiaz.ro – Carti si articole online gratuite de la A la Z obiect cu cu toate datele unui alt obiect de tip apImage. Operatorul de alocare foloseste constructorul (interpretorul) de copiere pentru a crea o copie a “src” , care este apoi schimbata cu datele existente ale obiectului. Cand operatorul de alocare revine, obiectul temporar apImage este distrus in mod automat. Aceasta abordare este deosebit de utila deoarece ea garanteaza faptul ca la aparitia oricarei erori, obiectul nu va fi parasit intr-o stare partial construita. • Scrierea si citirea pixelilor imaginii se realizeaza prin functii specifice care completeaza implementarea clasei soft a imaginilor normale apImage. Ambele functii vor genera un domeniu de eroare prin “rangeError” fie in cazul in care coordonatele furnizate nu specifica un pixel din cadrul imaginii, fie in cazul cand imaginea este nula, ca in implementarea urmatoare: void apImage::setPixel (int x, int y, unsigned char pixel) { if (x < 0 || y < 0 || x >= width_ || y >= height_ || !isValid()) throw rangeError (); // Image data is stored a row at a time. unsigned char* p = pixels_ + y*width_ + x; *p = pixel; } unsigned char apImage::getPixel (int x, int y) const { if (x < 0 || y < 0 || x >= width_ || y >= height_ || !isValid()) throw rangeError (); // Image data is stored a row at a time. unsigned char* p = pixels_ + y*width_ + x; return *p; } Curs 5 5.1. Proiectarea clasei soft a imaginilor concentrate Thumbnail Class Clasa soft a imaginilor concentrate Thumbnail Class este de tip “apThumbNail” si are ca scop crearea de imagini concentrate prin manipularea fisierelor I/O si cu utilizarea unor algoritmi de tip thumbnail. Aceasta clasa soft are din punct de vedere al proiectarii urmatoarele 4 proprietati: 1. Citeste imaginea de intrare dintr-un fisier video; 2. Proceseaza imaginea normala si o transforma intr-o imagine concentrata de tip “thumbnail” pe baza unui factor de reducere dat; 3. Genereaza functia de excludere C++ de forma “invalid” in cazul aparitiei oricarei erori pe durata procesarii. Astfel daca prin clasa imaginilor normale s-a lansat o eroare de tip “rangeError” , clasa thumbnail o captureaza si lanseaza in locul ei noua exceptie de tip “invalid”; 4. Inscrie imaginea thumbnail intr-un fisier. 7

www.cartiaz.ro – Carti si articole online gratuite de la A la Z Proiectarea clasei soft a imaginilor concentrate se realizeaza pe baza algoritmului thumbnail. Conform acestui algoritm, fiecare pixel din imaginea concentrata thumbnail se determina calculand valoarea medie a unui numar de pixeli din imaginea de intrare. Valoarea pixelului din imaginea concentratata thumbnail , valoare notata prin T (x0,y0), se calculeaza cu formula din fig.5.1: Fig. 5.1. Procesarea valorii pixelilor din imaginea concentrata thumbnail Prin “factor” s-a notat valoarea de concentratre / reducere dorita. Fiecare pixel P din imaginea originala este redus la un punct corespondent T in imaginea concentrata thumbnail, prin calcularea folosind formula 5.1 a valorii medii a pixelilor din vecinatate redusi cu factorul de reducere. Pentru a simplifica si mai mult softul de aplicatie, vom rotunji valoarea initiala a pixelilor astfel incat prin impartirea cu factorul de reducere sa rezulte o valoare intreaga a pixelului thumbnail. De exemplu, daca imaginea originala are marimea de 640x480 pixeli, iar factorul de reducere este 6, imaginea redusa thumbnail ar trebui sa aibe marimea (640/6)x(480/6) pixeli, adica ar fi (106,67x80) pixeli. Pentru a evita valoarea zecimala, vom ignora ultimii 4 pixeli din fiecare linie a imaginii originale, obtinand o imagine concentrata thumbnail (636/6)x(480/6) adica (106x80) pixeli. Acum suntem pregatiti sa definim clasa imaginilor concentrate thumbnail apThumbNail, care genereaza imaginile concentrate thumbnail prin procesarea imaginilor normale de intrare. La definirea acestei clase trebuie analizat daca pentru aplicatie este necesara definirea unui operator nou de copiere / alocare. Deoarece in practica proiectarii de software in timp real abordarea generala este de a nu defini un operator propriu, complet nou, de copiere / alocare vom accepta ca nu este necesar. Ca urmare vom include in mod explicit un comentariu pentru utilizatorii acestui soft prin care sa facem cunoscut faptul ca operatorul implicit de copiere / alocare este OK, deci nu am proiectat unul nou si aceasta alegere a fost deliberata. Ca si in cazul clasei imaginilor normale apImage, si clasa imaginilor concentrate apThumbNail este definita in modul cel mai simplu si mai eficient prin fisierul header “apThumbNail.h” ca mai jos: #ifndef _thumbnail_h_ #define _thumbnail_h_ #include "image.h" class apThumbNail { public: apThumbNail (); ~apThumbNail (); // The default copy constructor and assignment operator is ok void createThumbNail (const char* inputFile, const char* outputFile, int factor); class invalid {}; // Exception class private: void readImage (const char* inputFile); void writeImage (const char* outputFile) const; 8

www.cartiaz.ro – Carti si articole online gratuite de la A la Z unsigned char averagePixels (int x0, int y0, int factor); apImage image_; // input image apImage thumbnail_; // thumbnail image }; #endif // _thumbnail_h_

Curs 6 6.1. Implementarea clasei soft a imaginilor concentrate Thumbnail Class Implementarea clasei imaginilor concentrate Thumbnail Class este si aici simplu de realizat prin urmatoarea succesiune de functii: pentru constructori si distructori, pentru citirea si inscrierea imaginilor initiale, ca si pentru crearea imaginilor concentrate thumbnail. • Constructorii si distructorii se realizeaza in cazul imaginilor concentrate thumbnail mult mai simplu decat in cazul imaginilor normale folosind functiile apThumbNail() si ~apThumbNail() astfel: apThumbNail::apThumbNail () {} apThumbNail::~apThumbNail () {} Desi atat constructorul cat si distructorul sunt functii nule, definirea lor sub aceasta forma are totusi un scop. O greseala obisnuita in timpul procesului de dezvoltare de soft este adaugarea unui nou membru de date la un obiect si apoi uitarea initializarii sale ulterioare in cadrul constructorului. Prin definirea initiala a constructorului este mai putin probabil sa se uite initializarea noilor membri de date. Acest mod de implementare poate sa uimeasca prin faptul ca functiile anterioare nu au fost plasate in fisierul header, cum se obisnuieste, ci in fisierul sursa. Pastrarea lor in programul sursa la dezvoltarea de soft in timp real este insa foarte potrivita deoarece atat constructorul cat si distructorul sunt supuse modificarilor frecvente pe durata dezvoltarii si intretinerii acestui tip de soft. Plasarea lor in header poate duce astfel la cresterea considerabila a necesarului de memorie si deci la scaderea vitezei, ceea ce in timp real este inacceptabil. • Citirea si inscrierea imaginilor initiale se realizeaza in cazul imaginilor concentrate thumbnail prin implementarea functiilor readImage() si writeImage() . Aceste functii sunt destinate pentru citirea unei imagini dintr-un fisier si apoi convertirea ei intr-o clasa de tip apImage, respectiv pentru inscrierea unei imagini apImage intr-un fisier. Exista numeroase formate de fisiere destinate stocarii datelor imaginilor. Vom simula aceste doua functii pentru a verifica rapid daca softul proiectat lucreaza corect, inainte de a-l testa asupra modului in care suporta diferite formate de fisiere. Functia readImage() creeaza clasa apImage pe care o doteaza cu pixelii imaginii initiale, iar functia writeImage() afiseaza pur si simplu clasa apImage. De asemeni functia readImage() demonstreaza modul in care se captureaza eroarea de tip rangeError generata de apImage, pe care o converteste intr-o eroare de tip invalid. Implementarea functiilor readImage() si writeImage() este prezentata in programul urmator: 9

www.cartiaz.ro – Carti si articole online gratuite de la A la Z void apThumbNail::readImage (const char* /*inputFile*/) { // Create a synthetic 64x64 image. image_ = apImage (64, 64); try { for (int y=0; y
Crearea imaginilor concentrate thumbnail se realizeaza in cazul imaginilor concentrate thumbnail prin functia createThumbNail(). Aceasta preia numele unui fisier de intrare, numele fisierului thumbnail ce urmeaza sa fie creat la iesire, precum si raportul de concentrare / reducere dorit. Odata ce imaginea de intrare este citita iar imaginea thumbnail este alocata, functia createThumbNail() baleiaza fiecare pixel al imaginii de intrare pe care-l proceseaza in pixel al imaginii thumbnail, proces prezentat in programul urmator:

void apThumbNail::createThumbNail (const char* inputFile, const char* outputFile, int factor) { // Validate the arguments if (inputFile == 0 || outputFile == 0 || factor <= 1) 10

www.cartiaz.ro – Carti si articole online gratuite de la A la Z throw invalid (); // Read the source image readImage (inputFile); if (!image_.isValid()) throw invalid (); // Create our internal thumbnail image thumbnail_ = apImage (image_.width() / factor, image_.height() / factor); // Turn any rangeErrors from apImage into our invalid error unsigned char pixel; try { for (int y=0; y
www.cartiaz.ro – Carti si articole online gratuite de la A la Z throw invalid (); } // This cast is very safe return static_cast(sum / (factor * factor)); } Desi apThumbNail este o clasa simpla, ea poate totusi crea confuzii, deoarece contine doua imagini cu sisteme de coordonate diferite. Astfel, imaginea de intrare are dimensiunile width x height, in timp ce imaginea concentrata thumbnail are dimensiunile width/factor x height/factor. Pentru a evita aparitia confuziilor, intotdeauna coordonatele folosite de functia averagePixels() sunt toate in termenii imaginii de intrare image. De asemenea, coordonatele folosite de functia createThumbNail() sunt intotdeauna in termeni ai imaginii de iesire concentrata thumbnail, asa dupa cum a fost prezentat in programul de la crearea imaginilor concentrate thumbnail. Curs 7 7.1.Principii de alocare a memoriei Cel mai simplu mod de alocare a memoriei in cazul procesarii imaginilor consta in folosirea operatorilor new si delete, respectiv a functiilor init() si cleanup() pentru a aloca si a elibera memoria pentru pixelii imaginii din clasa apImage prin apImage::init() si apImage::cleanup(). Acest mecanism simplu este totusi foarte ineficient deoarece cade rapid la orice incercare de extindere. Managementul alocarii memoriei este o problema critica pentru o imagine complet functionala si de aceea la proiectarea de soft in timp real trebuie sa fie tratata foarte serios. Procesarea imaginilor necesita un volum mare de spatiu de memorie pentru a pastra datele pixelilor. S-a constatat ca este foarte ineficient sa se copieze direct aceste imagini, atat in ceea ce priveste volumul memoriei de stocare folosit cat si al timpului de procesare si manipulare. In cazul unui numar foarte mare de imagini stocate, se poate forte usor depasi capacitatea memoriei, iar zona de memorare devine fragmentata daca nu exista un bloc de memorie de rezerva destul de mare dupa alocare. De aceea la proiectarea de soft trebuie avut in vedere sa se evite duplicarea imaginilor, operatie care se justifica doar in cazul fortuit cand retinerea unei copii este critica, asa dupa cum ar fi cazul pastrarii imaginii originale si a imaginii filtrate rezultate. Pentru a ilustra ineficienta ce poate aparea la manipularea imaginilor vom prezenta un exemplu de cod simplu, prin care vom incerca sa adunam impreuna doua imagini si anume: apImage a (...); apImage b (...); apImage c = a + b; Codul aloca mai intai memorie pentru stocarea imaginii “a”, apoi pentru stocarea imaginii “b”, in continuare pentru stocarea imaginii temporare “(a+b)”, iar in final aloca memorie pentru imaginea rezultanta “c”. Acest exemplu simplu ilustreaza atat volumul mare al memoriei necesare alocate, cat si timpul excesiv necesar pentru copierea tuturor datelor pixelilor. Pentru a elimina aceste inconveniente, in loc de a proiecta un obiect soft care sa lucreze numai pentru imagine, vom crea un obiect generic util pentru orice aplicatie care necesita alocarea si managementul unui bloc masiv de memorie. Cerintele impuse de proiectare pentru obiectul generic de alocare a memoriei sunt: 12

www.cartiaz.ro – Carti si articole online gratuite de la A la Z • •

Sa aloce memorie si in afara blocului masiv de memorie necesar; Sa foloseasca o contorizare de referinta care sa permita atat distribuirea memoriei, cat si stergerea automata a memoriei atunci cand nu mai este necesara; • Sa foloseasca blocarea si deblocarea ca mod de management a obiectelor in cazul aplicatiilor de tip “multithreading”; • Sa nu se realizeze nici o initializare a memoriei dupa alocare, acesta urmand sa fie realizata de utilizator, daca este cazul; • Sa foloseasca sabloane, asa incat unitatea de alocare sa fie arbitrara; • Sa suporte arii simple matriciale ca si accesul direct la memorie; • Sa foloseasca biblioteca de sabloane standard STL (“Standard Template Library”); • Sa alinieze inceputul memoriei la o limita specifica, pentru compatibilitate cu compilatoarele existente care de obicei controleaza ordonarea datelor din memorie. Biblioteca STL este intotdeauna o resursa importanta pentru solutiile de proiectare, de unde se pot folosi instrumente de management a memoriei cum ar fi: std::vector, std::list sau std::string. Fiecare prezinta propriul sau avantaj, dar niciunul din aceste clase de sabloane nu ofera un numarator de referinta. Acesta este foarte necesar pentru proiectarea de soft in timp real, in special pentru realizarea obiectului soft generic de alocare a memoriei, deoarece permite diferitelor obiecte sa-si imparta aceeasi informatie. Figura 7.1 indica modul in care trei obiecte isi impart datele imaginii dintr-un acelasi bloc de memorie.

Fig. 7.1. Memorie distribuita la mai multe obiecte In fig. 7.1, obiectul continand datele imaginii este notat “Object Z” si contine de asemeni numaratorul de referinta “3”, care indica cat de multe obiecte isi impart datele, aceste obiecte fiind “Objects A, B, C”. Consideram urmatorul exemplu: apImage image2 = image1 Daca in acest caz se foloseste un numarator de referinta, atunci obiectul “image2” va imparti aceeasi memorie de stocare ca si obiectul “image1”. Se impune desrierea succinta a functionarii unui astfel de numaratoir de referinta. Un obiect de alocare a memoriei aloca memoria pentru o imagine. Atunci cand imaginile ulterioare necesita sa imparta acelasi spatiu de memorie, obiectul de alocare a memoriei incrementeaza o variabila, numita numarator de referinta, pentru a pastra inregistrarea imaginilor care au impartit acel spatiu de memorie. Apoi acest numarator va returna un indicator (“ pointer “) spre obiectul de alocare a memoriei. Atunci cand una din aceste imagini este stearsa, numaratorul de 13

www.cartiaz.ro – Carti si articole online gratuite de la A la Z referinta este decrementat, iar atunci cand el ajunge la zero, memoria folosita pentru stocare este stearsa. Modul de lucru al obiectului de alocare a memoriei apAlloc<> este evidentiat in urmatorul exemplu: apAlloc array1 (100); int i; for (i=0; i<100; i++) array1 [i] = i; apAlloc array2 = array1; for (i=0; i<100; i++) array1[i] = i*2; Odata ce obiectul de alocare a memoriei apAlloc<> a fost creat, el este folosit mai mult ca si un pointer oarecare. In acest exemplu s-a creat si completat cu date obiectul array1, iar dupa alocarea acestui obiect la obiectul array2, codul modifica continutul lui array1. Acum array2 are acelasi conyinut ca si array1. Obiectul de alocare a memoriei foloseste sabloane pentru implementare, deci este nevoie de o metoda de convertire a claselor in sablone. Consideram urmatoarea clasa imagine simpla pe care dorim s-o convertim intr-o clasa sablon: class apImageTest { public: apImageTest (int width, int height, char* pixels); char* getPixel (int x, int y); void setPixel (int x, int y, char* pixel); private: char* pixels_; int width_, height_; }; Conversia este simpla si consta in substituirea numelui de tip T cu referirea la char asa dupa cum se indica mai jos: template class apImageTest { public: apImageTest (int width, int height, T* pixels); T* getPixel (int x, int y); void setPixel (int x, int y, T* pixel); private: T* pixels_; int width_, height_; };

14

www.cartiaz.ro – Carti si articole online gratuite de la A la Z Pentru a folosi acest obiect, vom inlocui referirea la apImageTest prin apImageTest. Dupa definirea versiunii generice se poate implementa specializarea pentru cazul imaginii nedefinite unsigned char astfel: template<> class apImageTest { public: apImageTest (int width, int height, unsigned char* pixels); unsigned char* getPixel (int x, int y); void setPixel (int x, int y, unsigned char* pixel); private: unsigned char* pixels_; int width_, height_; }; Putem trece acum la definirea fiecarei functii de membru specializat, chiar si pentru cazul functiei unui singur membru specializat. Obtinem o implementare foarte generica pentru constructorul apImageTest<> si anume: template apImageTest::apImageTest (int width, int height, T* pixels) : width_ (width), height_ (height), pixels_ (0) { pixels_ = new T [width_ * height_]; T* p = pixels_; for (int y=0; y::apImageTest (int width, int height, unsigned char* pixels) : width_ (width), height_ (height), pixels_ (0) { pixels_ = new unsigned char [width_ * height_]; memcpy (pixels_, pixels, width_ * height_); } Se poate folosi cu incredere functia memcpy() pentru a initializa datele pixelilor din aplicatia de proiectare a softului de procesare a imaginii in timp real.

15

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Curs 8 8.1. Clase ierarhice pentru obiectul alocator de memorie In cadrul diagramelor de reprezentare a claselor se foloseste un set de notatii simple prezentate in fig. 8.1.

Fig. 8.1. Notatii folosite in cadrul diagramelor de reprezentare a claselor Structura ierarhica a claselor pentru obiectul alocator de memorie este prezentata in fig. 8.2 si consta din trei clase care folosesc toate un sablon T (Template). Clasa de baza este 16

www.cartiaz.ro – Carti si articole online gratuite de la A la Z apAllocatorBase_<>, din care se obtine clasa derivata apAllocator_<> iar clasa obiect propriuzisa este apAlloc<> care foloseste clasa derivata ca unul din argumentele sale „A”.

0100090000037800000002001c00000000000400000003010800050000000b020000000005000000 0c022d01e004040000002e0118001c000000fb021000070000000000bc02000000ee0102022253797 374656d0001e0040000461100008047110004ee8339807e1d000c020000040000002d010000040000 00020101001c000000fb02ceff0000000000009001000000ee0440001254696d6573204e657720526f 6d616e0000000000000000000000000000000000040000002d010100050000000902000000020d00 0000320a2d0000000100040000000000df042c0120d51600040000002d010000030000000000

Fig. 8.2. Structura ierarhica a claselor pentru obiectul alocator de memorie S-a adaugat caracterul de subliniere „ _ „ la numele clasei de baza si a celei derivate pentru a indica faptul ca ele sunt clase interne folosite doar de catre interfata de programare a aplicatiilor API („Application Programming Interface”), dar care nu pot fi apelate niciodata direct de catre utilizatori. Clasa de baza apAllocatorBase_<> realizeaza managementul memoriei principale si initializeaza variabilele obiect. Clasa derivata apAllocator_<> provine din clasa de baza si realizeaza managementul alocarii si dezalocarii memoriei din blocul masiv de memorie dedicata imaginilor („heap”). Clasa obiect propriuzisa dedicata alocarii memoriei apAlloc<> reprezinta de fapt o interfata simpla pe care o folosesc aplicatiile pentru managementul memoriei din afara blocului masiv de memorie dedicata si foloseste chiar clasa derivata ca unul din parametrii de alocare a memoriei. Clasa de baza apAllocatorBase_<> prevede accesul la pointerele de linie si de contorizare pentru realizarea stocarii informatiilor in memoria principala si foloseste un singur parametru sablon T prin care specifica unitatea de memorie ce urmeaza sa fie alocata. Structura completa de implemetare a acestei clase este urmatoarea: template class apAllocatorBase_ { public: apAllocatorBase_ (unsigned int n, unsigned int align) : pRaw_ (0), pData_ (0), ref_ (0), size_ (n), align_ (align) {} // Clase derivate de alocare a memoriei; detaliile de stocare sunt in clasa de baza 17

www.cartiaz.ro – Carti si articole online gratuite de la A la Z virtual ~apAllocatorBase_ () {} // Clase derivate care vor dezaloca memoria operator T* () { return pData_;} operator const T* () const { return pData_;} // Convesia la pointerul tipului de memorie alocata unsigned int size () const { return size_;} // Numar de elemente unsigned int ref () const { return ref_;} // Numar de referiri unsigned int align () const { return align_;} // Aliniament void addRef () { ref_++; } void subRef () { --ref_; if (ref_ == 0) delete this; } // Incrementarea si decrementarea contorului de referiri protected: virtual void allocate () = 0; virtual void deallocate () = 0; // Folosit pentru alocarea/dezalocarea memoriei. T* alignPointer (void* raw); // Alinierea pointerului specific pentru a corespunde aliniamentului ales apAllocatorBase_ (const apAllocatorBase_& src); apAllocatorBase_& operator= (const apAllocatorBase_& src); // Nu este permisa copierea sau alocarea char* pRaw_; // Pointerul liniei alocate T* pData_; // Pointerul aliniat memoriei specifice unsigned int size_; // Numarul de elemente alocate unsigned int ref_; // Numaratorul de referiri unsigned int align_; // Alinierea modulului de memorie }; Clasa derivata apAllocator_<> care deriva din clasa de baza si realizeaza managementul alocarii si dezalocarii memoriei din blocul masiv de memorie dedicata imaginilor („heap”) are urmatoarea structura de implementare: template class apAllocator_ : public apAllocatorBase_ { public: 18

www.cartiaz.ro – Carti si articole online gratuite de la A la Z explicit apAllocator_ (unsigned int n, unsigned int align = 0) : apAllocatorBase_ (n, align) { allocate (); addRef (); } virtual ~apAllocator_ () { deallocate() private: virtual void allocate () ; // Aloca memoria necesara avand marimea „size_” elemente de tip T cu // aliniamentul specificat de „align_”. Prin 0 si 1 nu se specifica un aliniament, // prin 2 = aliniamentul cuvantului, prin 4 = alignmentul pe 4-byte, ... Acest // aliniament trebuie sa fie o putere a lui 2. virtual void deallocate (); apAllocator_ (const apAllocator_& src); apAllocator_& operator= (const apAllocator_& src); // Nu este permisa copierea sau alocarea. }; Clasa obiect propriuzisa dedicata alocarii memoriei apAlloc<> pe care o folosesc in mod direct aplicatiile pentru alocarea si managementul memoriei, este implementatat in urmatorul mod: template > class apAlloc { public: static apAlloc& gNull (); // Se revine cu acest obiect pentru orice alocare nula // De fapt se aloca 1 byte pentru a valida toate functiile. apAlloc (); // Alocare nula. Se revine cu pointerul la memoria gNull(). explicit apAlloc (unsigned int size, unsigned int align= ~apAlloc (); // Aloca baitii specificati, cu aliniamentul corect. // Prin 0 si 1 nu se specifica un aliniament, prin 2 = aliniament pe cuvant, // prin 4 = aliniament pe 4-byte. Trebuie sa fie o putere a lui 2. apAlloc (const apAlloc& src); apAlloc& operator= (const apAlloc& src); // Este nevoie de un constructor de copiere si operator de alocare propriu. 19

www.cartiaz.ro – Carti si articole online gratuite de la A la Z unsigned int size () const { return pMem_->size ();} unsigned int ref () const { return pMem_->ref ();} bool isNull () const { return (pMem_ == gNull() const T* data () const { return *pMem_;} T* data () { return *pMem_;} // Acces la inceputul zonei de memorie. Folosire totusi cu economie. const T& operator[] (unsigned int index) const; T& operator[] (unsigned int index); // Acceseaza un element specific. Genereaza eroarea STL „range_error” // daca indexul nu este valid. virtual A* clone (); // Dubleaza memoria pentru apAllocator. void duplicate (); // Intrerupe orice contor de referire si forteaza ca objectul // sa aibă propria sa copie. protected: A* pMem_; // Pointer spre memoria ce a fost allocata. static apAlloc* sNull_; // Obiectul nul };

Curs 9 9.1. Realizarea prototipului 1 pentru obiectele imagine simple Strategia de realizare a prototipurilor pentru domeniul procesarii imaginilor, care permite investigarea a trei aspecte diferite ale procesarii, este prezentata in fig. 9.1 si este finalizata prin trei tipuri specifice de prototipuri notate prin 1, 2 si 3. In cazul prototipului 1 sunt cercetate elementele comune ale obiectelor imagine simple, avand diferite tipuri de pixeli (8 biti, respectiv 32 biti) pentru a permite proiectarea unui soft mai curat. In cazul prototipului 2 sunt explorate obiectele imagine sablonate „templated” pentru a gasi cel mai bun mod de a proiecta un soft performant care sa foloseasca similaritatile dintre imagini avand diferite sisteme de reprezentare. In cazul prototipului 3 este investigata fezabilitatea separarii componentelor de stocare a imaginii de obiectele imagine.

20

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

0100090000037800000002001c00000000000400000003010800050000000b020000000005000000 0c022d01e004040000002e0118001c000000fb021000070000000000bc02000000ee0102022253797 374656d0001e0040000461100008047110004ee8339807e1d000c020000040000002d010000040000 00020101001c000000fb02ceff0000000000009001000000ee0440001254696d6573204e657720526f 6d616e0000000000000000000000000000000000040000002d010100050000000902000000020d00 0000320a2d0000000100040000000000df042c0120d51600040000002d010000030000000000 Fig. 9.1. Strategia de realizare a prototipurilor pentru procesarea imaginilor Strategia de implementare a prototipului 1 pentru explorarea imaginilor monocrome de 8 biti si 32 biti este prezentata in fig. 9.2. Se tine cont de faptul ca o imagine de 8 biti este reprezentata printr-un caracter nedefinit „unsigned char”, pe cand imaginea de 32 biti este reprezentata printr-un intreg nedefinit”unsigned int”.

0100090000037800000002001c00000000000400000003010800050000000b020000000005000000 0c022d01e004040000002e0118001c000000fb021000070000000000bc02000000ee0102022253797 374656d0001e0040000461100008047110004ee8339807e1d000c020000040000002d010000040000 00020101001c000000fb02ceff0000000000009001000000ee0440001254696d6573204e657720526f 6d616e0000000000000000000000000000000000040000002d010100050000000902000000020d00 0000320a2d0000000100040000000000df042c0120d51600040000002d010000030000000000 21

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Fig. 9.2. Strategia de implementare a prototipului 1 de procesare a imaginilor Programul de implementare al apImage8 este urmatorul: typedef unsigned char Pel8; class apImage8 { public: apImage8 (); apImage8 (int width, int height); // Creaza o imagine nula, sau una avand dimensiunea specificata virtual ~apImage8 (); int width () const { return width_;} int height () const { return height_;} const Pel8* pixels () const { return pixels_.data();} Pel8* pixels () { return pixels_.data();} // Revine cu un pointer de inceput al datelor pixelilor. Pel8 getPixel (int x, int y) const; void setPixel (int x, int y, Pel8 pixel); // Preia/seteaza un singur pixel // Proceseaza imaginea virtual apImage8 thumbnail (int reduction) const; // Constructorul de copiere si operatorul de alocare sunt ok protected: apAlloc pixels_; // Datele pixelilor int width_; // Dimensiunile imaginii int height_; }; Programul de implementare al apImage32 este urmatorul: typedef unsigned int Pel32; class apImage32 { public: apImage32 (); apImage32 (int width, int height); // Creaza o imagine nula, sau una avand dimensiunea specificata virtual ~apImage32 (); int width () const { return width_;} 22

www.cartiaz.ro – Carti si articole online gratuite de la A la Z int height () const { return height_;} const Pel32* pixels () const { return pixels_.data();} Pel32* pixels () { return pixels_.data();} // Revine cu un pointer de inceput al datelor pixelilor. Pel32 getPixel (int x, int y) const; void setPixel (int x, int y, Pel32 pixel); // Preia/seteaza un singur pixel // Proceseaza imaginea virtual apImage32 thumbnail (int reduction) const; // Constructorul de copiere si operatorul de alocare sunt ok protected: apAlloc pixels_; // Datele pixelilor int width_; // Dimensiunile imaginii int height_; }; Curs 10 10.1. Realizarea prototipului 2 pentru obiectele imagine sablonate ( templated ) Obiectele imagine de diferite tipuri sunt de fapt foarte similare, iar modul de evidentiere a acestor similaritati se realizeaza prin folosirea sabloanelor, cu scopul de a simplifica proiectarea softului si maximizarea cantitatii de cod reutilizabil. La realizarea prototipului 2 se adauga softului urmatoarele noi trasaturi: • Se folosesc sabloane, adica se rescrie clasa imaginilor apImage pentru a prelua parametrul sablon T care reprezinta tipul pixelului; • Se introduce expresia idiom „clasa de manevrare/procesare” („handle class idiom”), asa incat mai multe obiecte de manevrat apImage<> pot sa imparta acelasi obiect reprezentativ apImageRep<>; • Se verifica daca softul proiectat lucreaza si cu imagini de un tip mai complex, cum ar fi imaginile RGB. Implementarea softului din prototipul 2, proiectat pentru obiectele imagine sablonate, este indicata in fig. 10.1.

0100090000037800000002001c00000000000400000003010800050000000b020000000005000000 0c022d01e004040000002e0118001c000000fb021000070000000000bc02000000ee0102022253797 374656d0001e0040000461100008047110004ee8339807e1d000c020000040000002d010000040000 00020101001c000000fb02ceff0000000000009001000000ee0440001254696d6573204e657720526f 23

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Fig. 10.1. Proiectarea softului pentru obiectele imagine sablonate Idiomul clasei de manevrare utilizeaza o clasa reprezentativa care contine datele si realizeaza toate operatiile, dar si o clasa de manevrare care este de fapt un pointer spre clasa reprezentativa. In cadrul prototipului 2, clasa reprezentativa este apImageRep, iar clasa de manevra este apImage, ca pointer spre clasa reprezentativa. Convertirea obiectului imagine apImage<> intr-un obiect sablon se face prin urmatorul soft: template class apImage { public: apImage (); apImage (unsigned int width, unsigned int height); ~apImage (); const T* pixels () const; T* pixels (); T getPixel (unsigned int x, unsigned int y) const; void setPixel (unsigned int x, unsigned int y, T pixel); apImage thumbnail (int reduction) const; protected: apAlloc pixels_; unsigned int width_; unsigned int height_; }; Clasa de manevra apImage din cadrul prototipului 2 are doua argumente sablon. Primul argument sablon este T, care reprezinta tipul pixelului din imagine, iar al doilea este E si reprezinta tipul pixelului intern ce urmeaza sa fie folosit in timpul computatiei. De exemplu, clasa apImage descrie o imagine cu pixeli de 8 biti, dar utilizeaza pentru computatiile interne pixeli de 32 biti. Implementarea clasei de manevra apImage ca un pointer spre clasa reprezentativa apImageRep este prezentata in programul urmator: template class apImageRep; // Declaratia initiala. template class apImage { public: friend class apImageRep; apImage (); // O imagine nula, utila pentru alocarile ulterioare. apImage (unsigned int width, unsigned int height); 24

www.cartiaz.ro – Carti si articole online gratuite de la A la Z ~apImage () { image_->subRef ();} apImage (const apImage& src); apImage& operator= (const apImage& src); // Este nevoie de propriul constructor de copiere si operator de alocare. const apImageRep* operator -> () const { return image_;} apImageRep* operator -> () { return image_;} // Permite accesul la obiectul reprezentativ. protected: apImage (apImageRep* rep); // Construieste o imagine din obiectul reprezentativ. apImageRep* image_; // Datele imaginii curente. }; Pentru a verifica daca softul proiectat lucreaza si cu imagini de un tip mai complex, cum ar fi imaginile RGB, este nevoie sa definim tipul special de imagine color RGBPe132, care este identic cu tipul RGB dar contine, pentru definirea culorilor, trei valori de 32 biti, in loc de trei valori de 8 biti. Implementarea softului de testare a functionarii prototipului 2 cu imagini RGB complexe este prezentata in programul urmator: // Tipul datelor noastre color de baza (format 8:8:8) struct RGB { Pel8 red; Pel8 green; Pel8 blue; RGB (Pel8 b=0) : red (b), green (b), blue (b) {} }; // Definitia interna in timpul computatiei (format 32:32:32) struct RGBPel32 { Pel32 red; Pel32 green; Pel32 blue; RGBPel32 (Pel32 l=0) : red (l), green (l), blue (l) {} }; RGBPel32& operator += (RGBPel32& s1, const RGB& s2) { s1.red += s2.red; s1.green += s2.green; s1.blue += s2.blue; return s1; } 25

www.cartiaz.ro – Carti si articole online gratuite de la A la Z RGB operator/ (const RGBPel32& s1, int den) { RGB div; div.red = s1.red / den; div.green = s1.green / den; div.blue = s1.blue / den; return div; } apImage p (32, 32); // Initializeaza imaginea cu cateva date. RGB pel; pel.red = pel.green = pel.blue = 0; for (y=0; yheight(); y++) for (x=0; xwidth(); x++) { p->setPixel (x, y, pel); pel.red++; pel.green++; pel.blue++; } apImage thumbnail = p->thumbnail (2);

Curs 11 11.1. Realizarea prototipului 3 de separare a stocarii informatiei de obiectele imagine Scopul prototipului 3 este de a separa stocarea imaginii de procesarea imaginii. Pentru a realiza acest lucru vom crea o clasa reprezentativa, apStorageRep, care sa incapsuleze stocarea imaginii, dar vom defini totodata pe apImage<> ca fiind obiectul care sa realizeze procesarea imaginii. Cele doua elemente le vom conecta folosind o clasa de manevrare, apImageStorage, rezultand structura de proiectare a softului pentru prototipul 3 din fig. 11.1.

26

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Fig. 11.1. Structura de proiectare a separarii stocarii informatiei de obiectele imagine Deoarece limbajul C++ nu suporta doua clase denumite la fel si care sa faca acelasi lucru, adica apImageStorage si apImageStorage<>, s-a redenumit clasa apImageStorage<> drept apImageStorageTmpl<>, prin adaugarea sufixului Tmp1 pentru a marca foarte clar faptul ca este o clasa diferita de tip sablon („Templated”). Implementarea clasei apStorageRep va contine atat definitii generice pentru stocarea imaginii, cat si functii specifice de manevrare a acestora, prezentate in programul urmator: class apStorageRep { public: static apStorageRep* gNull (); // Representarea stocarii unei imagini nule. apStorageRep (); apStorageRep (unsigned int width, unsigned int height, unsigned int bytesPerPixel); virtual ~apStorageRep (); const unsigned char* base unsigned char* base // Acces la baza memoriei.

() const { return storage_.data();} () { return storage_.data();}

unsigned int width () unsigned int height () unsigned int bytesPerPixel () { return bytesPerPixel_;} unsigned int ref ()

const { return width_;} const { return height_;} const const { return ref_;}

void addRef () { ref_++;} void subRef () { if (--ref_ == 0) delete this;} // Incrementeaza sau decrementeaza numaratorul de referinta. // Constructorul de copiere si operatorul de alocare este ok. protected: apAlloc storage_; // Stocarea pixelului. unsigned int bytesPerPixel_; // Bytes per pixel unsigned int width_; 27

www.cartiaz.ro – Carti si articole online gratuite de la A la Z unsigned int height_; unsigned int ref_; // Numaratorul de referinta curent. static apStorageRep* sNull_; }; Implementarea clasei apImageStorage va contine apImageStorageTmpl<> fiind prezentata in programul urmator:

si

clasa

derivata

din

ea

class apImageStorage { public: apImageStorage (); // Stocarea unei imagini nule. apImageStorage (apStorageRep* rep); virtual ~apImageStorage (); apImageStorage (const apImageStorage& src); apImageStorage& operator= (const apImageStorage& src); // Avem nevoie de propriul constructor de copiere si operator de alocare. const apStorageRep* operator -> () const { return storage_;} apStorageRep* operator -> () { return storage_;} protected: apStorageRep* storage_; }; template class apImageStorageTmpl : public apImageStorage { public: apImageStorageTmpl () {} apImageStorageTmpl (unsigned int width, unsigned int height) : apImageStorage (new apStorageRepTmpl (width, height)) {} virtual ~apImageStorageTmpl () {} const apStorageRepTmpl* operator -> () const { return static_cast*> (storage_);} apStorageRepTmpl* operator -> () { return static_cast*> (storage_);} }; Implementarea finala a obiectului apImage, responsabil pentru toate rutinele de procesare a imaginii va contine si sablonul thumbnail() pentru imaginile concentrate, asa dupa cum se vede in programul urmator: 28

www.cartiaz.ro – Carti si articole online gratuite de la A la Z template class apImage { public: apImage (); apImage (unsigned int width, unsigned int height) : pixels_ (width, height) {} ~apImage () {} unsigned int width () const { return pixels_->width();} unsigned int height () const { return pixels_->height();} const T* pixels () const { return pixels_->base();} T* pixels () { return pixels_->base();} const T& getPixel (unsigned int x, unsigned int y) const; void setPixel (unsigned int x, unsigned int y, const T& pixel); // Operatii asupra imaginii. apImage thumbnail (unsigned int reduction) const; // Constructorul de copiere si operatorul de alocare implicit este ok. protected: apImage (apImageStorageTmpl& storage); // Construieste o imagine din informatia stocata in memorie. apImageStorageTmpl pixels_; // Datele imaginii curente. }; template apImage apImage::thumbnail (unsigned int reduction) const { apImage output(width()/reduction, height()/reduction); for (unsigned int ty=0; ty
www.cartiaz.ro – Carti si articole online gratuite de la A la Z sa suporte imagini pe scala gri de 8 biti, nu trebuie sa cada atunci cand se definesc noi imagini de 32 biti pe scala gri. Asigurarea compatibilitatii si a stabilitatii in timp a unui soft nou proiectat se poate obtine prin integrarea in structura sa a unor componente de soft reutilizabile. Prin conceptul de reutilizabil se intelege nu numai un soft care sa poata fi folosit la proiecte sau platforme multiple, ci mai ales un soft care sa serveasca unor scopuri multiple in aceeasi aplicatie. Pentru a evidentia mai bine acest concept, vom analiza o componenta soft reutilizabila bine cunoscuta si bine proiectata folosind limbajul C++, componenta integrata ca obiect sir in cadrul bibliotecii de sabloane standard STL. Clasa sirurilor standard std::string este un pachet soft proiectat elegant, care contine aproape orice este necesar, desi unii utilizatori pot spune ca nu are toate facilitatile de care au ei nevoie, pe cand altii apreciaza ca este prea stufoasa si are prea multe functiuni. Pentru procesarea imaginilor este deosebit de utila o componenta a clasei sirurilor standard si anume obiectul sirurilor binare apBString(). Acesta este foarte necesar pentru colectarea, manipulare si procesarea unor mari siruri de date binare, asa cum apar in cazul imagisticii medicale. O analiza mai profunda a obiectului sirurilor binare arata ca acest obiect realizeaza de fapt managementul unor date marcate („tagged data”). Orice articol (data) scris pentru aceste siruri consta din doua parti: o prima parte este un marcaj, care specifica tipul de data scris, iar a doua parte este chiar data propriuzisa. O linie din sirul de date binare poate fi decodata cu dificultate, in special atunci cand formatul sau este modificat in timp. Folosirea metodei de marcare a datelor usureaza interpretarea lor si permite oricui sa citesca un sir de date, chiar si atunci cand are un inteles necunoscut. Campul de marcare a datelor precede data propriuzisa si are marimea de 1 byte. Implementarea pentru obiectul sirurilor binare apBString() este prezentata mai jos: typedef unsigned char typedef unsigned char typedef unsigned char typedef unsigned char class apBString { public: apBString (); ~apBString ();

Pel8; Pel16; Pel32; Pel32s;

apBString apBString& operator=

// sir de 1-byte // sir de 2-bytes // sir de 4-bytes nedefiniti („unsigned”) // sir de 4-bytes definiti („signed”)

(const apBString& src); (const apBString& src);

size_t size () const { return string_.size();} const void* base () const { return string_.c_str();} // Revine cu pointerul si marimea datelor. void rewind () { offset_ = 0;} // Reseteaza pointerul nostru de iesire pentru starea de inceput. bool eof () const { return offset_ >= string_.size();} // Revine cu adevarat („true”) daca sirul este la sfarsitul sau. 30

www.cartiaz.ro – Carti si articole online gratuite de la A la Z bool match () const { return match_;} // Revine cu adevarat („true”) daca toate extragerile au ca rezultat concordanta dintre // tipul de date solicitate si tipul de date stocate. const std::string& str () const { return string_; } // Acces la sirul nostru de date. // Operatori de introducere / inserare. apBString& operator<< (Pel8 b); apBString& operator<< (Pel16 w); apBString& operator<< (Pel32s l); apBString& operator<< (Pel32 l); apBString& operator<< (float f); apBString& operator<< (double d); apBString& operator<< (const std::string& s); apBString& operator<< (const apBString& bstr); void append (const void* data, unsigned int size); // Operatori de extragere. apBString& operator>> (Pel8& b); apBString& operator>> (Pel16& w); apBString& operator>> (Pel32s& l); apBString& operator>> (Pel32& l); apBString& operator>> (float& f); apBString& operator>> (double& d); apBString& operator>> (std::string& s); apBString& operator>> (apBString& bstr); bool fetch (const void*& data, unsigned int& size); std::string dump (); // Umplere cu date de tip ASCII, incepand de la offsetul curent. private: std::string string_; unsigned int offset_; bool match_; enum eTypes {eNone=0, ePel8=1, ePel16=2, ePel32s=3, ePel32=4, eFloat=5, eDouble=6, eString=7, eData=8, eBstr=9}; // Tipurile de date suportate. Aceste valori nu pot fi schimbate niciodata dar // pot fi adaugate altele in plus. 31

www.cartiaz.ro – Carti si articole online gratuite de la A la Z apBString (const void* data, unsigned int size); void add (eTypes type, const void* data, unsigned int size); // Adauga datele speciicate la memoria noastra tampon („ buffer”). const void* extract (eTypes& type); // Revine cu un pointer la urmatorul tip de date, dar mai revine si cu tipul sau. // Revine cu null daca se incearca citirea dincolo de capatul sirului. Pel8 readPel8 (const void* p); Pel16 readPel16 (const void* p); Pel32s readPel32s (const void* p); Pel32 readPel32 (const void* p); float readFloat (const void* p); double readDouble (const void* p); std::string readString (const void* p); // Citeste o anumita cantitate din sir. std::string dumpBString (unsigned int indent); // Umple cu text continutul de la un singur sir BString. }; Sirul binar analizat este stocat ca un obiect std::string la care vom adauga date sub forma unor variabile de tip Pel16 avand dimensiunea de 2 bytes, prin proiectarea urmatorului soft: apBString& apBString::operator<< (Pel16 w) { add (ePel16, &w, sizeof (w)); return *this; } void apBString::add (eTypes type, const void* data, unsigned int size) { // Adauga tipul. Pel8 t = static_cast(type); string_.append (reinterpret_cast(&t), sizeof (Pel8)); // Adauga datele. string_.append (reinterpret_cast (data), size); }

32

www.cartiaz.ro – Carti si articole online gratuite de la A la Z Curs 13 13.1. Proiectarea modulelor de cod de depanare directa prin flux generalizat. Obiectul sursa Modulele de cod de depanare se proiecteaza conform schemei structurale din fig. 13.1, incepand cu obiectul sursa, adica fluxul de depanare generalizat cdebug, apoi continuand cu obiectele destinatie, adica obiectele controlerului de iesire de depanare apDebug denumite „sinks”.

Fig. 13.1. Schema structurala a modulelor de depanare directa prin flux generalizat Codul de depanare se adauga unei aplicatii de obicei prin scrierea informatiei intr-un flux standard, cum ar fi std::cout sau std::cerr. Optimizarea procesului de depanare se obtine insa prin crearea unui flux complet nou al carui scop este doar depanarea, dar care foloseste fluxul existent std::cout pe care-l redirectioneaza spre depanare directa prin urmatoarea secventa de cod: #include #include { std::cout << "Aceasta secventa ar trebui sa fie tiparita la consola" << std::endl; std::ofstream file ("redirect.txt"); std::streambuf* oldbuf = std::cout.rdbuf (); std::cout.rdbuf (file.rdbuf());

// Salvare.

std::cout << " Aceasta secventa ar trebui sa fie tiparita in fisier " << std::endl; std::cout.rdbuf (oldbuf); // Restaurare. std::cout << "Aceasta secventa ar trebui sa fie tiparita la consola" << std::endl; } 33

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

{ cdebug << "Aceasta linie apartine de sink-ul null " << std::endl; debugstream.sink (apDebugSinkConsole::sOnly); cdebug << " Aceasta linie apartine de std::cout" << std::endl; apDebugSinkConsole::sOnly.showHeader (true); cdebug << " Apartine de asemenea de std::cout, dar cu marcaj in timp" << std::endl; apDebugSinkFile::sOnly.setFile ("test.txt"); debugstream.sink (apDebugSinkFile::sOnly); cdebug << " Aceasta linie apartine de test.txt" << std::endl; apDebugSinkFile::sOnly.showHeader (true); cdebug << " Apartine de asemenea de test.txt dar cu marcaj in timp " << std::endl; };

Curs 14 14.1. Proiectarea modulelor de cod de depanare directa prin flux generalizat. Obiectele destinatie Obiectele destinatie in cazul depanarii directe prin flux generalizat sunt obiecte de tip „sink” care sa pastreze informatiile din fluxul de depanare. Clasa de baza a acestor obiecte sink este apDebugSink si defineste interfata soft de baza pe care orice obiect sink derivat va trebui sa o implementeze, ea avand urmatoarea structura: class apDebugSink { public: apDebugSink (); virtual void write (const std::string& str) = 0; virtual void write (int c) = 0; // Inscrie un sir sau un caracter la sink-ul de depanare. virtual void flush () {} // Completeaza cu orice informatie posibila pentru acest tip de sink. virtual std::string header () { return standardHeader();} // In mod implicit se va emite header-ul standard daca headerele sunt activate. 34

www.cartiaz.ro – Carti si articole online gratuite de la A la Z void showHeader (bool state) { enableHeader_ = state;} // Marcheaza starea in care se afla header-ul. protected: std::string standardHeader (); bool enableHeader_; // Logic „true” pentru header daca buffer-ul este umplut. } std::string apDebugSink::standardHeader () { std::string header; // Genereaza timpul curent. time_t now = time(0); header += ctime (&now); header.erase (header.length()-1, 1); // Elimina noua linie inscrisa. header += ": "; return header; }; Pentru inchiderea fluxului de depanare se foloseste sink-ul nul apDebugSinkNull, care reprezinta si sink-ul implicit atunci cand se construieste fluxul de depanare cdebug, el fiind proiectat in urmatoarea secventa: class apDebugSinkNull : public apDebugSink { public: static apDebugSinkNull sOnly; virtual void write (const std::string& /*str*/) {} virtual void write (int /*c*/) {} private: apDebugSinkNull (); }; Pentru lucrul la consola se foloseste sink-ul apDebugSinkConsole, prin care se inscrie un flux de caractere la std::cout, el avand urmatoarea structura: class apDebugSinkConsole : public apDebugSink { public: static apDebugSinkConsole sOnly; 35

www.cartiaz.ro – Carti si articole online gratuite de la A la Z virtual void write (const std::string& str); virtual void write (int c); virtual void flush (); protected: virtual void display (const std::string& str); // Iesirea fluxului, care poate fi ocolita de clasele derivate. apDebugSinkConsole (); virtual ~apDebugSinkConsole (); std::string buffer_; }; Pentru lucrul cu fisierele se foloseste sink-ul apDebugSinkFile, prin care se inscrie un flux de date intr-un fisier specific, el avand urmatoarea structura: class apDebugSinkFile : public apDebugSinkConsole { public: static apDebugSinkFile sOnly; void setFile (const std::string& file); // Inscrie / Schimba numele fisierului. Fluxul este incarcat inainte ca // numele fisierului sa fie schimbat. private: virtual void display (const std::string& str); apDebugSinkFile (); virtual ~apDebugSinkFile (); std::string file_; }; Pentru vizualizarea la depanare a fluxurilor de date din ferestrele windows, chiar si atunci cand se proceseaza in paralel un alt flux, se foloseste sink-ul apDebugSinkWindows, avand urmatoarea structura: class apDebugSinkWindows : public apDebugSinkConsole { public: static apDebugSinkWindows sOnly; virtual void display (const std::string& str); }; apDebugSinkWindows apDebugSinkWindows::sOnly = apDebugSinkWindows (); 36

www.cartiaz.ro – Carti si articole online gratuite de la A la Z void apDebugSinkWindows::display (const std::string& str) { OutputDebugString (str.c_str()); };

Curs 15 15.1. Proiectarea modulelor de cod de depanare indirecta prin accesarea registrelor obiect. Strategia de proiectare. Depanarea permite monitorizarea modului in care lucreaza o anumita aplicatie. Depanarea indirecta este mult mai complicata in cazul in care se face de la distanta, permitand accesul indirect la un obiect pur si simplu printr-un registru obiect. Acest registru special poate fi apoi folosit pentru a proiecta module de depanare performante care sa ruleze fie local pe acelasi computer, fie prin comanda la distanta pe un alt computer. Structura unui registru obiect este prezentata in fig. 15.1.

Fig. 15.1. Structura registrului obiect Registrul obiect consta din doua parti. Prima parte contine tipurile de obiecte care sunt inregistrate, avand cate o intrare pe fiecare tip de obiect. Parte a doua contine exemplele caz curente ale fiecarui tip de obiect. Strategia de proiectare se bazeaza pe utilizarea unor clase specifice grupate conform schemei din fig. 15.2.

37

www.cartiaz.ro – Carti si articole online gratuite de la A la Z

Fig. 15.2. Strategia de proiectare a registrului obiect Clasele specifice grupate pentru a lucra impreuna sunt: • • • • •

apObjectMgr, care realizeaza managementul listei tuturor tipurilor de obiecte folosite de interfata de depanare; apObjectInfoBase, care reprezinta clasa de baza din care deriva obiectele ce pastreaza inregistrarea informatiilor despre desfasurarea procesului de depanare; apObjectInfo, care pastreaza inregistrarea tuturor exemplelor caz de obiecte de un anumit tip; apObject, care reprezinta clasa de baza din care deriva obiectele ce au capacitatea de depanare de la distanta; user object, reprezentand un obiect care deriva (trebuie sa fie extras) din apObject, daca utilizatorul doreste ca acesta sa fie disponibil pentru depanare la distanta.

Strategie generala de proiectare a registrului obiect implica realizarea bazei de date apObjectInfoBase prin divizarea fiecarui obiect intr-un obiect obisnuit ne-sablonat si un obiect special sablonat, folosind un obiect std::map pentru a pastra o lista a tuturor tranzitiilor unui anumit obiect, asa dupa cum se prezinta in urmatoarea structura de cod: class apObjectInfoBase { public: typedef std::map INSTANCEMAP; apObjectInfoBase () : debug_ (0) {} int debug () { return debug_;} void debug (int d) { debug_ = d;} bool isDebug (int level) { return debug_ >= level;} void addInstance (void* obj) { mapping_[obj] = 1;} 38

www.cartiaz.ro – Carti si articole online gratuite de la A la Z void removeInstance (void* obj) { mapping_.erase (obj);} // Adauga / Elimina un obiect din lista curenta. virtual std::string process (const std::string& command) = 0; // Commanda procesorul. virtual std::string dump () = 0; // Produce o lista a obiectelor controlate. protected: int debug_; INSTANCEMAP mapping_; };

// Nivelul de depanare pentru acest obiect. // Lista tuturor obiectelor curente.

Curs 16 16.1. Proiectarea modulelor de cod de depanare indirecta prin accesarea registrelor obiect. Implementare. Implementarea clasei apObjectInfoBase se face apObjectInfo, rezultand urmatoarea structura de cod:

prin

sablonare

template class apObjectInfo : public apObjectInfoBase { public: static apObjectInfo& gOnly (); //singleton object const std::string& name () const { return name_;} // Return our object name. This might be as simple // as what typeid() returns, but it also can be // modified as necessary. virtual std::string process (const std::string& command); // Process the debugging command and return results. // Strings are used to keep the interface generic. // The base class version ignores everything virtual std::string dump (); // Produce a space separated list of object instances private: 39

folosind

clasa

www.cartiaz.ro – Carti si articole online gratuite de la A la Z static apObjectInfo* sOnly_; std::string name_; apObjectInfo (); }; template std::string apObjectInfo::dump () { std::string str; char buffer[16]; INSTANCEMAP::iterator i; for (i=mapping_.begin(); i != mapping_.end(); i++) { sprintf (buffer, " %d", i->first); str += buffer; } return str; }; Implementarea clasei apObjectMgr se face pe baza clasei implementate anterior apObjectInfoBase rezultand urmatoarea structura de cod: class apObjectMgr { public: typedef std::map<std::string, apObjectInfoBase*> OBJMAP; static apObjectMgr& gOnly (); std::string dump (); // Descarcarea textului tuturor obiectelor in uz. void debugMessage (const std::string& header, const std::string& msg); // Genereaza un mesaj de depanare pentru „cdebug”. void add (const std::string& name, apObjectInfoBase* obj); // Adauga un obiect la lista existenta. apObjectInfoBase* find (const std::string& name); // Revine fie cu un pointer la un anumit „apObjectInfoBase”, fie cu un 0. std::string process (const std::string& command); // Transmite comanda la toate obiectele existente. 40

www.cartiaz.ro – Carti si articole online gratuite de la A la Z private: static apObjectMgr* sOnly_; apObjectMgr (); OBJMAP mapping_; // Lista tuturor clasele controlate in acest moment. } apObjectInfoBase* apObjectMgr::find (const std::string& name) { OBJMAP::iterator i = mapping_.find (name); if (i == mapping_.end()) return 0; return i->second; } std::string apObjectMgr::process (const std::string& command) { std::string result; OBJMAP::iterator i; for (i=mapping_.begin(); i != mapping_.end(); ++i) { result += i->second->process (command); result += " "; } return result; }; Curs 17 17.1. Proiectarea modulelor de multiprocesare cu integrarea si sincronizarea traseelor de procesare („threads”). Proiectarea modulelor soft de multiprocesare se face prin integrarea facilitatilor de procesare multipla folosind traseele multiple de procesare („threads”). Pentru crearea si integrarea traseelor de procesare multiple se foloseste clasa apThread iar pentru sincronizarea lor se foloseste clasa apLock. Clasa apThread se implementeaza prin urmatoarea structura de cod: class apThread { public: apThread () : threadid_ (-1) {} ~apThread () {if (threadid_ != -1) stop();} int threadid () const { return threadid_;} bool start () { threadid_ = _beginthreadex (0, 0, thread_, this, CREATE_SUSPENDED, (unsigned int*) &threadid_); if (threadid_ != 0) 41

www.cartiaz.ro – Carti si articole online gratuite de la A la Z ResumeThread ((HANDLE)threadid_); return (threadid_ != 0); } // Start the thread running bool stop () { TerminateThread ((HANDLE) threadid_, -1); return true; } // Stop the thread. bool wait (unsigned int seconds = 0) { DWORD wait = seconds * 1000; if (wait == 0) wait = INFINITE; DWORD status = WaitForSingleObject ((HANDLE) threadid_, wait); return (status != WAIT_TIMEOUT); } // Wait for thread to complete void sleep (unsigned int msec) { Sleep (msec);} // Sleep for the specified amount of time. protected: int threadid_; static unsigned int __stdcall thread_ (void* obj) { // Call the overriden thread function apThread* t = reinterpret_cast(obj); t->thread (); return 0; } virtual void thread () { _endthreadex (0); CloseHandle ((HANDLE) threadid_); } // Thread function, Override this in derived classes. }; Clasa apThread se implementeaza prin urmatoarea structura de cod: class apLock 42

www.cartiaz.ro – Carti si articole online gratuite de la A la Z { public: apLock () { InitializeCriticalSection (&lock_); } ~apLock () { DeleteCriticalSection (&lock_);} apLock (const apLock&) { InitializeCriticalSection (&lock_);} apLock& operator= (const apLock&) {} // Get the lock bool lock () const { EnterCriticalSection (&lock_); return true;} // Release the lock bool unlock () const { LeaveCriticalSection (&lock_); return true;} private: mutable CRITICAL_SECTION lock_; };

Curs 18 18.1. Finalizarea implementarii componentelor imaginii. Proiectarea finala a componentelor imaginii implica cele trei aspecte ale manipularii imaginii si anume: coordonateleimaginii, stocarea imaginii si definirea tipului de pixeli. Coordonatele imaginii sunt descrise prin puncte si obiecte rectangulare. Punctul reprezinta o pereche de tipul (x, y), care specifica coordonatele intregi ale unui pixel, si se implementeaza prin clasa apPoint in urmatoarea structura de cod sursa: class apPoint { public: apPoint () : x_ (0), y_ (0) {} apPoint (std::pair p) : x_ (p.first), y_ (p.second) {} apPoint (int x, int y) : x_ (x), y_ (y) {} int x () const { return x_;} int y () const { return y_;} 43

www.cartiaz.ro – Carti si articole online gratuite de la A la Z std::pair point () const { return std::pair(x_, y_);} bool operator == (const apPoint& p) const { return x() == p.x() && y() == p.y();} apPoint& operator += (const apPoint& p) { x_ += p.x(); y_ += p.y(); return *this;} private: int x_, y_; }; Obiectele rectangulare se folosesc pentru a defini marginile unei imagini, se reprezinta printr-o succesiune de puncte avand latimea width si inaltimea hight, considerate marimi intregi nespecificate „unsigned int”, si se implementeaza prin clasa apRect in urmatoarea structura de cod sursa: class apRect { public: apRect (); apRect (apPoint ul, unsigned int width, unsigned int height); apRect (apPoint ul, apPoint lr); apRect (int x0, int y0, unsigned int width, unsigned int height); const apPoint& ul () const { return ul_;} apPoint lr () const; int x0 () const { return ul_.x();} int y0 () const { return ul_.y();} int x1 () const { return lr().x();} int y1 () const { return lr().y();} unsigned int width () const { return width_;} unsigned int height () const { return height_;} bool isNull () const { return width_ == 0 || height_ == 0;} bool operator == (const apRect& r) const; bool operator != (const apRect& r) const { return !operator== (r);} bool within (const apPoint& p) const; apRect intersect (const apRect& r) const; void expand (int x, int y); private: apPoint ul_; unsigned int width_; unsigned int height_; };

44

www.cartiaz.ro – Carti si articole online gratuite de la A la Z Proiectarea finala a stocarii imaginii se realizeaza prin conceperea si implementarea clasei de baza a stocarii imaginii apImageStorageBase, avand urmatoarea strctura de cod sursa: class apImageStorageBase { public: apImageStorageBase (); apImageStorageBase (const apRect& boundary); virtual ~apImageStorageBase (); const apRect& boundary () const { return boundary_;} int x0 () const { return boundary_.x0();} int y0 () const { return boundary_.y0();} int x1 () const { return boundary_.x1();} int y1 () const { return boundary_.y1();} unsigned int width () const { return boundary_.width();} unsigned int height () const { return boundary_.height();} protected: apRect boundary_; }; Proiectarea finala a tipului de pixeli implementati in imaginea finala se realizeaza printr-o abordare originala. In locul definirii unei structuri separate pentru fiecare tip de imagine color RGB, s-a definit un sablon general apRGBTmpl<>, la care parametrul sablon este marimea componentei de rosu, verde si albastru „RGB”. Sablonul contine pe langa operatorii de baza si functiile de conversie a pixelilor color in pixeli de tip monocrom, rezultand urmatoarea structura de cod sursa:

template class apRGBTmpl { public: T red; T green; T blue; apRGBTmpl () : red(0), green(0), blue(0) {} explicit apRGBTmpl (T v) : red(v), green(v), blue(v) {} apRGBTmpl (T r, T g, T b) : red(r), green(g), blue(b) {} apRGBTmpl (const apRGBTmpl& s) apRGBTmpl& operator= (const apRGBTmpl& src) 45

www.cartiaz.ro – Carti si articole online gratuite de la A la Z template apRGBTmpl (const apRGBTmpl& s) template apRGBTmpl& operator= (const apRGBTmpl& src) apRGBTmpl& operator= (const T& c) operator T () const // Conversia la imaginea monocroma. apRGBTmpl& operator+= (const apRGBTmpl& s) apRGBTmpl& operator-= (const apRGBTmpl& s) apRGBTmpl& operator*= (const apRGBTmpl& s) apRGBTmpl& operator/= (const apRGBTmpl& s) apRGBTmpl& operator+= (const T& s) apRGBTmpl& operator-= (const T& s) apRGBTmpl& operator*= (const T& s) apRGBTmpl& operator/= (const T& s) };

Curs 19 19.1. Finalizarea implementarii clasei sablon API pentru imagine. In loc de a considera clasa imaginii ca un simplu obiect, vom extinde conceptul si vom implementa in final o clasa sablon pentru imagine sub forma unei aplicatii de tip API („Application Programming Interface”), folosind tipul pixelului T si obiectul de stocare S, rezultand clasa sablon apImage, avand urmatoarea structura de cod sursa: template > class apImage { public: typedef typename S::row_iterator row_iterator; 46

www.cartiaz.ro – Carti si articole online gratuite de la A la Z typedef typename S::iterator iterator; static apImage sNull; apImage () {} apImage (const apRect& boundary, apRectImageStorage::eAlignment align = apRectImageStorage::eNoAlign) : storage_ (boundary, align) {} apImage (S storage) : storage_ (storage) {} virtual ~apImage () {} apImage (const apImage& src); apImage& operator= (const apImage& src); bool lockImage () const { return storage_.lock ();} bool unlockImage () const { return storage_.unlock ();} bool lockState () const { return storage_.lockState ();} bool unlockState () const { return storage_.unlockState ();} bool isNull () const { return storage_.isNull();} int ref () const { return storage_.ref();} int xoffset () const { return storage_.xoffset();} int yoffset () const { return storage_.yoffset();} unsigned int bytesPerPixel () const { return storage_.bytesPerPixel();} unsigned int rowSpacing () const { return storage_.rowSpacing();} apRectImageStorage::eAlignment alignment () const { return storage_.alignment();} S& storage () { return storage_;} const apRect& boundary () const { return storage_.boundary();} int x0 () const { return storage_.x0();} int y0 () const { return storage_.y0();} int x1 () const { return storage_.x1();} int y1 () const { return storage_.y1();} unsigned int width () const { return storage_.width();} unsigned int height () const { return storage_.height();} const T* base () const ; const T* rowAddress (int y) const ; T* rowAddress (int y) ; const T& getPixel (int x, int y) const ; const T& getPixel (const apPoint& point) const ; void setPixel (int x, int y, const T& pixel ; void setPixel (const apPoint& point, const T& pixel) ; void setRow (int y, const T& pixel) ; void setCol (int x, const T& pixel) ; 47

www.cartiaz.ro – Carti si articole online gratuite de la A la Z row_iterator row_begin () { return storage_.row_begin();} const row_iterator row_begin () const { return storage_.row_begin();} row_iterator row_end () { return storage_.row_end();} const row_iterator row_end () const { return storage_.row_end();} iterator begin () { return storage_.begin();} const iterator begin () const { return storage_.begin();} iterator end () { return storage_.end();} const iterator end () const { return storage_.end();} bool window (const apRect& window) { return storage_.window (window);} void trim (); void rebase (); apRectImageStorage::eAlignment bestAlignment () const; apImage align (apRectImageStorage::eAlignment align = apRectImageStorage::eNoAlign); apImage duplicate (apRectImageStorage::eAlignment align = apRectImageStorage::eNoAlign) const; void set (const T& value) { ::set (*this, value);} void add (const T& value); void sub (const T& value); void mul (const T& value); void div (const T& value); void scale (float scaling); bool isIdentical (const apImage& src) const; bool operator== (const apImage& src) const; bool operator!= (const apImage& src) const protected: S storage_; // Image storage };

Curs 20 20.1. Finalizarea implementarii functiilor imagine globale concentrate „thumbnail”. Implementarea functiilor imagine globale concentrate de tip „thumbnail” se realizeaza dupa filtrarea imaginii pentru eliminarea zgomotelor, folosind operatia de convolutie, prin care valoarea unui pixel din imaginea filtrata concentrata se obtine facand suma ponderata a valorilor pixelilor din imediata vecinatate. Matricea de ponderi folosita la operatia de insumare ponderata se numeste „kernel”. S-a conceput un sablon generic de convolutie care este apoi particularizat pentru filtre de tip Laplacian si Gaussian, sablonul avand urmatoarea structura de cod: 48

www.cartiaz.ro – Carti si articole online gratuite de la A la Z template void ap_convolve_generic (const R&, const apImage& src1, const char* kernel, unsigned int size, int divisor, apImage& dst1) { typename apImage::row_iterator i1 = src1.row_begin(); typename apImage::row_iterator i2 = dst1.row_begin(); typename apImage::row_iterator i1k; unsigned int h = src1.height() - (size-1); unsigned int w = src1.width() - (size-1); const T1* pk; const char* k; T2* p2; R sum; unsigned int x, y, xk, yk; for (y=0; yp; for (x=0; x<w; x++) { sum = 0; i1k = i1; k = kernel; for (yk=0; yk<size; yk++) { pk = i1k->p + x; for (xk=0; xk<size; xk++) sum += static_cast(*pk++) * (*k++); i1k++; } if (divisor == 1) *p2++ = apLimit (sum); else { sum /= divisor; *p2++ = apLimit (sum); } } } }; In final imaginea globala concentrata „thumbnail” se realizeaza prin sablonul functiei thumbnail(), avand urmatoarea structura de cod: template apImage thumbnail (const apImage& src1, unsigned int factor) { apImageLocker srcLocking (src1); 49

www.cartiaz.ro – Carti si articole online gratuite de la A la Z apImage dst; if (src1.isNull()) return dst; apRect boundary (src1.x0(), src1.y0(), src1.width()/factor, src1.height()/factor); dst = apImage (boundary, src1.alignment()); typename apImage::row_iterator s; typename apImage::row_iterator d; typename apImage::row_iterator s1; unsigned int w = dst.width (); const T1* sp; T1* dp; R sum; // Iteratia asupra pixelilor destinatie for (d=dst.row_begin(), s=src1.row_begin(); d != dst.row_end(); d++, s+=factor) { dp = d->p; for (unsigned int x=0; x<w; x++) { sum = 0; s1 = s; for (unsigned int dy=0; dyp + x*factor; for (unsigned int dx=0; dx (sum / (factor*factor)); } } return dst; }

Curs 21 21.1. Finalizarea implementarii interfetelor video de interconectare cu bibliotecile soft existente. Structurile software moderne se folosesc de bibliotecile soft existente, atat pentru a creste viteza de proiectare, cat si pentru a minimiza cheltuielile de intretinere si timpul de depanare. In 50

www.cartiaz.ro – Carti si articole online gratuite de la A la Z practica actuala de proiectare de soft in timp real, aplicatiile se proiecteaza intotdeauna cu interfete specifice prin care sa se delege responsabilitatea subrutinelor deja existente din bibliotecile soft. Se foloseste termenul de delegare „delegate” pentru a indica aceasta delegare de responsabilitati. Clasa de baza pentru delegarea bibliotecilor soft existente din domeniul video este apImageIOBase si este implementata prin urmatoarea structura de cod sursa: class apImageIOBase { public: virtual apDelegateInfo info (const std::string& filename) = 0; template void read (const std::string& filename, apImage& image) { if (typeid(apImage) == typeid(image)) image = readRGB (filename); else if (typeid(apImage) == typeid(image)) image = readPel8 (filename); else copy (readRGB (filename), image); } template void write (const std::string& filename, apImage& image, const apDelegateParams& params = sNoParams) { if (typeid(apImage) == typeid(image)) write (filename, image.storage(), params); else if (typeid(apImage) == typeid(image)) write (filename, image.storage(), params); else { apImage rgb = image; write (filename, rgb.storage(), params); } } virtual apImage readRGB (const std::string& filename) = 0; virtual apImage readPel8 (const std::string& filename) = 0; virtual bool write (const std::string& filename, const apRectImageStorage& pixels, const apDelegateParams& params) = 0; protected: apImageIOBase (); virtual ~apImageIOBase (); static apDelegateParams sNoParams; }; Pastrarea evidentei formatelor de fisiere video disponibile se face printr-o lista a fisierelor incarcate prin clasa apImageIODelegateList, implementata prin urmatoarea structura de cod sursa: class apImageIODelegateList 51

www.cartiaz.ro – Carti si articole online gratuite de la A la Z { public: static apImageIODelegateList& gOnly (); apImageIOBase* getDelegate (const std::string& type); void setDelegate (const std::string& type, apImageIOBase* object); apImageIOBase* findDelegate (const std::string& filename); private: typedef std::map<std::string, apImageIOBase*> map; typedef map::iterator iterator; map map_; // Maparea tipului de fisier pentru delegatul care-l utilizeaza; static apImageIODelegateList* sOnly_; apImageIODelegateList (); }; Exista o multitudine de formate pentru stocarea imaginilor video, incluzand JPEG, GIF, PNG sau TIFF. Dintre acestea formatul JPEG (Joint Photographic Expert’s Group) este cel mai uzual format de fisiere video, iar in cazul sau delegarea responsabilitatilor de procesare se face prin clasa apJPEG avand urmatoarea structura de cod: class apJPEG : public apImageIOBase { public: static apJPEG& gOnly (); virtual apDelegateInfo info (const std::string& filename); virtual apImage readRGB (const std::string& filename); virtual apImage readPel8 (const std::string& filename); virtual bool write (const std::string& filename, const apRectImageStorage& pixels, const apDelegateParams& params = sNoParams); private: static apJPEG* sOnly_; apJPEG (); ~apJPEG (); };

Formatul TIFF (Tag Image File Format) este un alt tip foarte utilizat de format al fisierelor de 52

www.cartiaz.ro – Carti si articole online gratuite de la A la Z stocare a imaginilor, obtinute in special in urma procesului de scanare, imaginile putand fi atat color, cat si monocrome. In cazul formatelor TIFF delegarea responsabilitatilor de procesare se face prin clasa apTIFF avand urmatoarea structura de cod: class apTIFF : public apImageIOBase { public: static apTIFF& gOnly (); virtual apDelegateInfo info (const std::string& filename); virtual apImage readRGB (const std::string& filename); virtual apImage readPel8 (const std::string& filename); virtual bool write (const std::string& filename, const apRectImageStorage& pixels, const apDelegateParams& params = sNoParams); private: static apTIFF* sOnly_; apTIFF (); ~apTIFF (); };

53

Related Documents

Experimente In Timp
November 2019 8
Calatoria In Timp
November 2019 17
Tema De Proiectare
June 2020 4

More Documents from ""