Sunteți pe pagina 1din 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 sa-
i 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 bi-
dimensionala 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_; // Image width


int height_; // Image height
unsigned char* pixels_; // 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<class T> 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<image_.height(); y++)
for (int x=0; x<image_.width(); x++)
image_.setPixel (x, y, (unsigned char)(y % 255));
}
catch (apImage::rangeError) {
throw invalid ();
}
}

void apThumbNail::writeImage (const char* /*outputFile*/) const


{
apImage output;
output = thumbnail_;
// Save formatting state of stream
std::ios_base::fmtflags flags = std::cout.flags (std::cout.hex);
int width = std::cout.width (2);

for (int y=0; y<output.height(); y++) {


for (int x=0; x<output.width(); x++)
std::cout << (int) output.getPixel (x, y) << " ";
std::cout << std::endl;
}
std::cout.flags (flags);
std::cout.width (width);
}

• 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<thumbnail_.height(); y++) {
for (int x=0; x<thumbnail_.width(); x++) {
// Convert to image_ coordinates to find the average
pixel = averagePixels (x*factor, y*factor, factor);
thumbnail_.setPixel (x, y, pixel);
}
}
}
catch (apImage::rangeError) {
throw invalid ();
}
writeImage (outputFile);
}

Functia createThumbNail() apeleaza functia averagePixels() pentru a calcula media


pixelilor din imaginea de intrare, care sunt necesari pentru calculul unui singur pixel din
imaginea concentrata thumbnail. De fapt, pixelii dintr-un mic subset al imaginii initiale
image de forma factor x factors sunt insumati si li se face media dupa cum urmeaza:

unsigned char apThumbNail::averagePixels (int x0, int y0,


int factor)
{
int sum = 0;

// Average factor x factor pixels in the input image


try {
for (int y=0; y<factor; y++) {
for (int x=0; x<factor; x++)
sum += image_.getPixel (x+x0, y+y0);
}
}
catch (apImage::rangeError) {
11
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

throw invalid ();


}

// This cast is very safe


return static_cast<unsigned char>(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<int> array1 (100);


int i;
for (i=0; i<100; i++)
array1 [i] = i;
apAlloc<int> 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 T> 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<char>. Dupa definirea versiunii generice se poate implementa specializarea
pentru cazul imaginii nedefinite unsigned char astfel:

template<> class apImageTest<unsigned char>


{
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<class T>
apImageTest<T>::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<height; y++) {
for (int x=0; x<width; x++)
*p++ = *pixels++; // use assignment to copy pixels
}
}

Aceasta definitie foloseste alocarea pentru a copia fiecare pixel din zona de memorie data, intr-una
controlata de clasa. Se poate acum defini si implementa o specializare in care T este un caracter
nespecificat unsigned char:

apImageTest<unsigned char>::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 T> 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 T> class apAllocator_ : public apAllocatorBase_<T>


{
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_<T> (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 T, class A = apAllocator_<T> >


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<Pel8> 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<Pel32> 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<T,E>, iar clasa de
manevra este apImage<T,E>, ca pointer spre clasa reprezentativa.
Convertirea obiectului imagine apImage<> intr-un obiect sablon se face prin urmatorul soft:
template<class T> 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<T> thumbnail (int reduction) const;
protected:
apAlloc<T> pixels_;
unsigned int width_;
unsigned int height_;
};
Clasa de manevra apImage<T,E> 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<unsigned char, unsigned int> descrie o imagine cu pixeli de 8 biti, dar utilizeaza pentru
computatiile interne pixeli de 32 biti.
Implementarea clasei de manevra apImage<T,E> ca un pointer spre clasa reprezentativa
apImageRep<T, E> este prezentata in programul urmator:

template<class T, class E> class apImageRep; // Declaratia initiala.

template<class T, class E> class apImage


{
public:
friend class apImageRep<T, E>;

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<T, E>* operator -> () const { return image_;}


apImageRep<T, E>* operator -> () { return image_;}
// Permite accesul la obiectul reprezentativ.

protected:
apImage (apImageRep<T, E>* rep);
// Construieste o imagine din obiectul reprezentativ.

apImageRep<T, E>* 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<RGB, RGBPel32> p (32, 32);

// Initializeaza imaginea cu cateva date.


RGB pel;
pel.red = pel.green = pel.blue = 0;
for (y=0; y<p->height(); y++)
for (x=0; x<p->width(); x++) {
p->setPixel (x, y, pel);
pel.red++;
pel.green++;
pel.blue++;
}
apImage<RGB, RGBPel32> 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 () const { return storage_.data();}


unsigned char* base () { return storage_.data();}
// Acces la baza memoriei.

unsigned int width () const { return width_;}


unsigned int height () const { return height_;}
unsigned int bytesPerPixel () const
{ return bytesPerPixel_;}
unsigned int ref () 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<unsigned char> 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 si clasa derivata din ea


apImageStorageTmpl<> fiind prezentata in programul urmator:

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 T>
class apImageStorageTmpl : public apImageStorage
{
public:
apImageStorageTmpl () {}
apImageStorageTmpl (unsigned int width, unsigned int height)
: apImageStorage (new apStorageRepTmpl<T> (width, height))
{}

virtual ~apImageStorageTmpl () {}

const apStorageRepTmpl<T>* operator -> () const


{ return static_cast<apStorageRepTmpl<T>*> (storage_);}
apStorageRepTmpl<T>* operator -> ()
{ return static_cast<apStorageRepTmpl<T>*> (storage_);}
};

Implementarea finala a obiectului apImage<T, E>, 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 T, class E> 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<T, E> thumbnail (unsigned int reduction) const;
// Constructorul de copiere si operatorul de alocare implicit este ok.
protected:
apImage (apImageStorageTmpl<T>& storage);
// Construieste o imagine din informatia stocata in memorie.
apImageStorageTmpl<T> pixels_; // Datele imaginii curente.
};

template<class T, class E>


apImage<T,E> apImage<T,E>::thumbnail (unsigned int reduction) const
{
apImage<T,E> output(width()/reduction, height()/reduction);
for (unsigned int ty=0; ty<output.height(); ty++) {
for (unsigned int tx=0; tx<output.width(); tx++) {
E sum = 0;
for (unsigned int y=0; y<reduction; y++) {
for (unsigned int x=0; x<reduction; x++)
sum += getPixel (tx*reduction+x, ty*reduction+y);
}
output.setPixel (tx, ty, sum / (reduction*reduction));
}
}
return output;
}
Curs 12
12.1. Folosirea codurilor reutilizabile
Orice soft evolueaza prin adaugarea de noi trasaturi la subrutinele initiale sau prin
conceperea de noi subrutine aparute ca fiind necesare pe masura utilizarii sale. Aceasta face ca insasi
scopul original al softului, impus in faza de proiectare, sa se schimbe in timp. Un soft bine proiectat
ar trebui sa permita schimbari ulterioare in structura sa de proiectare fara sa afecteze insa
functionarea sistemului existent. De exemplu, pentru cazul procesarii imaginii, un sistem proiectat
29
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 Pel8; // sir de 1-byte


typedef unsigned char Pel16; // sir de 2-bytes
typedef unsigned char Pel32; // sir de 4-bytes nedefiniti („unsigned”)
typedef unsigned char Pel32s; // sir de 4-bytes definiti („signed”)
class apBString
{
public:
apBString ();
~apBString ();

apBString (const apBString& src);


apBString& operator= (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<Pel8>(type);
string_.append (reinterpret_cast<char*>(&t), sizeof (Pel8));

// Adauga datele.
string_.append (reinterpret_cast<const char*> (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 <iostream>
#include <fstream>
{
std::cout << "Aceasta secventa ar trebui sa fie tiparita la consola" << std::endl;

std::ofstream file ("redirect.txt");


std::streambuf* oldbuf = std::cout.rdbuf (); // Salvare.
std::cout.rdbuf (file.rdbuf());

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<T>, care pastreaza inregistrarea tuturor exemplelor caz de obiecte de un
anumit tip;
• apObject<T>, 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<T>,
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<void*, char> 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_; // Nivelul de depanare pentru acest obiect.
INSTANCEMAP mapping_; // 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 prin sablonare folosind clasa


apObjectInfo<T>, rezultand urmatoarea structura de cod:

template <class T>


class apObjectInfo : public apObjectInfoBase
{
public:
static apObjectInfo<T>& 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
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

static apObjectInfo<T>* sOnly_;


std::string name_;

apObjectInfo ();
};

template <class T>


std::string apObjectInfo<T>::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<apThread*>(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<int, int> 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<int, int> point () const


{ return std::pair<int, int>(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 T> 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<T>& operator= (const apRGBTmpl& src)

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

template<class T1> apRGBTmpl (const apRGBTmpl<T1>& s)


template<class T1> apRGBTmpl<T>& operator= (const apRGBTmpl<T1>&
src)

apRGBTmpl& operator= (const T& c)

operator T () const
// Conversia la imaginea monocroma.

apRGBTmpl<T>& operator+= (const apRGBTmpl<T>& s)


apRGBTmpl<T>& operator-= (const apRGBTmpl<T>& s)
apRGBTmpl<T>& operator*= (const apRGBTmpl<T>& s)
apRGBTmpl<T>& operator/= (const apRGBTmpl<T>& s)

apRGBTmpl<T>& operator+= (const T& s)


apRGBTmpl<T>& operator-= (const T& s)
apRGBTmpl<T>& operator*= (const T& s)
apRGBTmpl<T>& 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<T, S>, avand urmatoarea structura de cod sursa:

template<class T, class S=apImageStorage<T> >


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<T,S> align (apRectImageStorage::eAlignment align =


apRectImageStorage::eNoAlign);

apImage<T,S> 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<class R, class T1, class T2, class S1, class S2>


void ap_convolve_generic (const R&, const apImage<T1,S1>& src1,
const char* kernel, unsigned int size, int divisor,
apImage<T2,S2>& dst1)
{
typename apImage<T1,S1>::row_iterator i1 = src1.row_begin();
typename apImage<T2,S2>::row_iterator i2 = dst1.row_begin();
typename apImage<T1,S1>::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; y<h; y++, i1++, i2++) {


p2 = i2->p;
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<R>(*pk++) * (*k++);
i1k++;
}
if (divisor == 1)
*p2++ = apLimit<T2> (sum);
else {
sum /= divisor;
*p2++ = apLimit<T2> (sum);
}
}
}
};

In final imaginea globala concentrata „thumbnail” se realizeaza prin sablonul functiei


thumbnail(), avand urmatoarea structura de cod:

template<class R, class T1, class S1>


apImage<T1,S1> thumbnail (const apImage<T1,S1>& src1, unsigned int factor)
{
apImageLocker<T1,S1> srcLocking (src1);
49
www.cartiaz.ro – Carti si articole online gratuite de la A la Z

apImage<T1,S1> dst;

if (src1.isNull())
return dst;

apRect boundary (src1.x0(), src1.y0(),


src1.width()/factor, src1.height()/factor);
dst = apImage<T1,S1> (boundary, src1.alignment());

typename apImage<T1,S1>::row_iterator s;
typename apImage<T1,S1>::row_iterator d;
typename apImage<T1,S1>::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; dy<factor; dy++, s1++) {
sp = s1->p + x*factor;
for (unsigned int dx=0; dx<factor; dx++)
sum += *sp++;
}
*dp++ = apLimit<T1> (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<class T, class S>
void read (const std::string& filename, apImage<T,S>& image)
{
if (typeid(apImage<apRGB>) == typeid(image))
image = readRGB (filename);
else if (typeid(apImage<Pel8>) == typeid(image))
image = readPel8 (filename);
else
copy (readRGB (filename), image);
}
template<class T, class S>
void write (const std::string& filename, apImage<T,S>& image,
const apDelegateParams& params = sNoParams)
{
if (typeid(apImage<apRGB>) == typeid(image))
write (filename, image.storage(), params);
else if (typeid(apImage<Pel8>) == typeid(image))
write (filename, image.storage(), params);
else {
apImage<apRGB> rgb = image;
write (filename, rgb.storage(), params);
}
}
virtual apImage<apRGB> readRGB (const std::string& filename) = 0;
virtual apImage<Pel8> 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<apRGB> readRGB (const std::string& filename);
virtual apImage<Pel8> 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<apRGB> readRGB (const std::string& filename);
virtual apImage<Pel8> 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