#313 Ciocea Bogdan-Andrei

	Pentru abordarea acestei teme, am considerat ca ar fi decent sa fac o 
structura care sa retina datele unei imagini, adica tipul (avand doua 
variabile de tip char, prima care sa retina litera “P” si a doua sa retina 
cifra corespunzatoare tipului imaginii), dimensiunile, valoarea maxima pe 
care o poate avea un pixel si matricea de date unde se gasesc valorile 
pixelilor. In loc sa fac o singura matrice pentru toate tipurile de 
imagine, am ales sa le fac structurat: o singura matrice pentru imaginea
de tip PGM si trei matrici pentru imaginea de tip PPM (o matrice are 
valorile culorii rosu, alta pentru verde si a treia pentru albastru).

	O prima functie in acest program este functia “init()”, care 
initializeaza valorile datelor unei imagini care nu a fost incarcata 
inca. Aceasta functie a fost facuta ca sa nu avem valori neinitializate.

	A doua functie din program este “eliberare_matrice()”, care elibereaza 
datele dintr-o imagine, adica dealoca spatiul stocat in matricile alocate, 
adica daca imaginea este de tip grayscale si, normal, i s-a alocat memorie,
atunci se va elibera memorie din acea matrice. Daca imaginea ese de tip 
PPM, atunci se vor elibera cele trei matrici alocate.

	A treia functie este “skip_comments()”, care ne ajuta sa sarim peste 
comentarii din imagine in cazul in care exista.

	Functia “find_max()” este o functie care gaseste elementul cu valoarea
maxima dintr-o imagine sau dintr-un set de trei matrici. Daca imaginea 
este de tip PGM, atunci se va gasi maximul din acea matrice. Daca matricea 
este de tip PPM, atunci o sa cautam trei valori maxime, fiecare avand o 
matrice la care se refera, apoi comparam maximele. Aceasta functie este 
facuta sa actualizeze valoarea maxima dintr-un pixel dupa o operatie 
(de exemplu cand vrem sa taiem o imagine dupa o selectie folosind functia 
“CROP()”, trebuie sa actualizam valoarea numarului maxim, ca altfel nu se 
va salva corect o imagine).

	Pentru a modulariza codul si a nu va “copy-paste” de cateva ori, am 
decis sa fac o functie care aloca memorie unei matrici. Aceasta se numeste 
“alocare_matrice()”, care are drept parametrii numarul de linii si numarul 
de coloane. Aceasta functie returneaza o matrice caruia i s-a alocat 
dinamic memorie. De asemenea, in aceasta functie exista si programarea 
defensive, astfel, daca se depaseste memoria sa se dealoce memoria si sa 
nu intoarca nimic.

	Functia “alocareppm()” practic foloseste functia “alocare_matrice()” 
pentru cele trei matrici care contin valorile culorilor.

	Functia “alocarepgm()” aloca memoria unei matrici, adica cea a unei 
imagini de tip PGM.

	Functia “citire_binar()” citeste datele dintr-o matrice, fie de tip 
PGM, fie PPM. Adica aceasta functie deschide imaginea cu scopul de a 
citi datele in format binar. Se citesc datele referitor la tipul imaginii 
(dar in mod fictiv, deoarece tipul de imagine se citeste in functia 
“LOAD()”), la numarul de coloane si de linii, si apoi se verifica tipul 
de imagine. Verificam daca tipul de imagine este PPM sau PGM si se aloca 
memorie pentru matricele corespunzatoare. Se citesc matricele din imagine 
cu functia “fread()”. Totodata, la aceasta functie se sar si peste 
comentarii daca exista.

	Functia “citire_ascii()” face exact acelasi lucru ca si functia 
explicate mai sus, dar se citesc datele sub forma de intregi, nu caractere.
Avem functia “LOAD()”, in care sunt introduse aceste doua functii. Inainte 
de toate avem variabla pointer “*OK”, care verifica daca a fost inainte 
introdusa o imagine. Daca da, atunci trebuie sa dealocam matricele alocate 
si sa initializam structura din nou si valoarea pointerului la OK se va 
face 0. Apoi o sa deschidem fisierul pe care vrem sa il incarcam in 
structura doar pentru a-l citi. Citim tipul imaginii si sarim peste 
posibilele comentarii. Apoi o sa inchidem fisierul si o sa citim fisierul 
dupa tipul de imagine inregistrat in structura. Dupa aceea OK o sa aiba 
valoarea 1 ce indica faptul ca fisierul a fost incarcat cu succes.

	Functia “SELECT_ALL()” este cu scopul de a seta coordonatele de 
selectie in coltul din stanga sus si in coltul din dreapta jos. 
Inainte de a se face aceasta operatie, se verifica daca exista un fisier 
incarcat.

	Functia “SELECT()” practic seteaza doua coordonate de selectie. Acestea
se vor citi in main(), si se vor verifica daca sunt valide in aceasta 
functie. Intai verificam daca exista un fisier deja incarcat, pentru ca 
operatia nu ar mai avea loc daca nu s-a incarcat nicio imagine. Apoi o sa 
verificam daca coordonatele sunt valide, adica daca acestea macar nu ies 
din matrice si nici daca sunt egale fiecare. Dupa treaba aceasta, se 
verifica daca avem ordinea coordonatelor incorecta, adica daca, din 
greseala, am introdus intai coordonata din dreapta jos si apoi coordonata 
din stanga sus, ca apoi, daca este adevarat acest caz, sa rectificam 
aceasta greseala si sa inversam coordonatele ele intre ele. Dupa aceea, 
variabilele care reprezinta coordonatele din structura va lua valorile 
acelor coordonate.

	Functia “HISTOGRAM()” face histograma unei imagini de tip PGM. Intai de
toate se verifica daca exista o imagine incarcata, ca de nu, o sa se arate 
mesajul “No image loaded”. Apoi se vor citi coordonatele si se va verifica 
daca nu este trecuta una dintre ele sau ambele. Apoi se verifica daca cumva
tipul imaginii este color. Dupa se va verifica daca numarul de binuri este 
valid. Dupa aceea se vor face efectiv operatiile descrise in fisierul temei
de casa. Adica facem un vector care sa retina frecventa fiecarui pixel in 
imagine, ca dupa sa facem maximul de frecventa in binuri si dupa aceea sa 
calculam numarul de stelute si sa le afisam cum ni s-a cerut.

	Functia “EXIT()”  dealoca resursele matricelor alocate in cazul in care
exista o imagine incarcata. Daca nu, atunci se va afisa mesajul “No image 
loaded”. Tin sa precizez ca dupa aplicarea acestei functii se va iesi din 
program, indiferent de faptul ca imaginea a fost incarcata sau nu.

	Functia “CROP_PGM()”  se taie imaginea de tip PGM dupa o selectie 
anume. Aceasta functie practic face o matrice auxiliara cu datele matricei 
taiate. Noi vom actualiza numarul de linii si numarul de coloane. Apoi o sa
eliberam matricea alocata imaginii din structura. Actualizam dimensiunile 
imaginii din structura si apoi alocam memorie pentru matricea din imagine 
cu dimensiunile noi ale imaginii. Pe urma se copiaza efectiv valorile 
matricei auxiliare in matricea din structura. Dupa se va dealoca memoria 
pentru matricea auxiliara, iar selectia va fi setata drept toata imaginea.

	Functia “free_matrici_color()” dealoca memoria pentru trei mattrici cu 
“height” linii.

	Functia “CROP_PPM()” face acelasi lucru ca si functia “CROP_PGM()”, 
singura diferenta ar fii ca in loc de o matrice auxiliara sunt trei, 
pentru fiecare culoare (R, G, B).

	Functia “CROP()” imbina cele doua functii de crop de mai sus, 
verificand tipul imaginii, dar inainte de aceasta intructiune, verifica 
daca imaginea este incarcata. Daca operatia s-a finalizat cu success, se 
va afisa mesajul “Image cropped”.

	Functiile “SAVE_ASCII()” si “SAVE_BINARY()” sunt practic in oglinda cu 
functiile “citire_ascii()” si “citire_binar()”, adica pe cand functiile de 
citire citesc datele, functiile de salvare afiseaza in alt fisier imaginea.
Adica, daca la functia de citire in ascii avem functia “fscanf()”, la 
salvarea elementelor sub formatul ASCII avem “fprintf()” si la citirea 
elementelor in format binar avem “fread()”, la salvarea elementelor in 
format binar avem functia “fwrite()”, care scrie intr-un fisier matricea 
sub format binar. Aceste doua functii verifica inainte de toate daca 
imaginea a fost inainte incarcata. Daca acest lucru se respecta, atunci se 
va deschide fisierul sub format binar sau ascii (depinde de caz). In ambele
functii se verifica inainte de scrierea elementelor din matrice tipul 
matricei, adica daca noi vrem sa transformam o matrice din format binar in 
format ascii, ii schimbam tipul si vice versa.

	Functia “EQUALIZE()” face ce se cere din enuntul problemei, adica sa 
egalizeze pixelii. Acest algoritm merge in felul urmator: verifica daca 
exista o imagine incarcata, daca intr-adevar este incarcata o matrice, 
atunci ii vom verifica tipul, deoarece noi dorim sa egalizam o imagine de 
tip PGM (grayscale) si vom afisa mesajul “Black and white image needed” 
daca imaginea este color. Se face un vector de frecventa care sa ia 
frecventa fiecarui pixel din imagine. Apoi, fiecare pixel va lua valoarea 
data de formula scrisa in enuntul temei: se face o variabila care sa retina
valoarea ariei imaginii, avem o suma de frecvente si avem calculul 
propriu-zis. Se rotunjeste rezultatul si se verifica daca valoarea apartine
intervalului [0, 255]. Dupa aceea, valoarea pixelului va lua valoarea 
variabilei “rez” si se va afisa mesajul “Equalize done”.

	Functiile “inmultire_matrici()” si “inmultire_matrici_n()” sunt legate 
intre ele, deoarece ambele au fost create ca sa faca operatia (babeste) a 
inmultirilor fiecarui pixel cu o valoare dintr-o matrice data. Singura 
chestie care difera una fata de cealalta este ca functia 
“inmultire_matrici_n()” merge doar pe functiile de “BLUR” si 
“GAUSSIAN_BLUR” din comanda “APPLY”.  Functia “inmultire_matrici_n()” 
imparte rezultatul la un n dat (depinde de functie). Aceste doua functii 
folosesc o matrice in care fiecare element reprezinta un produs de doua 
elemente din doua matrici. Exista o matrice auxiliara cu elementele 
matricei corespunzatoare pentru a nu deranja ceilalti pixeli cu valoarea 
actualizata al pixelului modificat. De asemenea mai avem si matricea “mat” 
care reprezinta matricea specifica fiecarei operatii din comanda “APPLY”, 
adica poate reprezenta fiecare matrice din enuntul temei.

	Functia “apply_inmultire()” practice face operatiile de “APPLY” pentru 
“SHARPEN” si “EDGE”. Aceasta functie merge doar pe o selectie anume, fie ca
e una mica, fie ca este pe toata imaginea. Avem pentru fiecare culoare cate
o matrice de dimensiuni 3 x 3 in care se vor face inmultirile si apoi se 
vor aduna valorile lor. Se verifica daca selectia cumva nu este pe ramura, 
adica pe margini, deoarece nu are ce calcule sa se faca cu pixelii din 
marginile imaginii, deoarece acei pixeli nu au 8 vecini, deci se vor 
ajusta. Dupa aceea se aloca dinamic in memorie o matrice de “image->height”
linii si “image->width” coloane si se copiaza elementele dintr-o matrice de
culoare in una din matricile auxiliare (adica pentru fiecare culoare exista
o matrice auxilara si se face operatiile pe fiecare pixel separate) ca, pe 
urma sa se dealoce memoria pentru matricele noi alocate ca sa nu avem 
memory leak-uri (scuzati rom-engleza…nu ma depunctati va rog). Aceasta 
functie este folosita doar in operatiile de “EDGE” si de “SHARPEN”.
Pentru functia “apply_inmultire_blur()” se face acelasi lucru ca in functia
de mai sus, doar ca se cere sa se imparta rezultatul sumei produselor la un
anumit numar, deci aceasta functie va fi folosita in functiile de “BLUR” si
de “GAUSSIAN_BLUR”.

	Urmatoarele patru functii de mai jos au acelasi stil, doar ca difera 
matricele. Acestea sunt functiile in care se aplica un efect pe o imagine. 
Acestea au structura asa: se verifica daca imaginea a fost incarcata in 
memorie, daca nu atunci se va afisa mesajul “No image loaded”. Daca 
imaginea este incarcata si este de tip PGM, atunci se va afisa mesajul 
“Easy, Charlie Chaplin”. Daca imaginea exista si este de tip color, atunci 
vom avea o matrice de dimensiune 3 x 3 si apoi vom apela functia pentru 
inmultirea elementelor din matrici. La final se va afisa un mesaj de 
incheiere cu success al functiei.

	In functia “main()” se apeleaza toate functiile, se declara structura 
imagine, se initializeaza structura cu functia “init()”, se declara comanda
“line”, “*tok” pe care il vom folosi atunci cand o sa ne dorim sa spargem 
un sir in cuvinte si o variabila “OK”, care ne arata daca o imagine a fost 
incarcata in memorie sau nu. Apoi, cu ajutorul unui “while”, vom apela 
functiile. Daca ce scriem nu se regaseste printre cele din functia 
“main()”, atunci se va afisa mesajul “Invalid command”.