Termen de predare:
Pentru fiecare zi (24 de ore) de întârziere, se vor scădea 10 puncte din nota acordată, până la atingerea deadline-ului hard.
Dacă aveți nelămuriri, puteți să ne contactați pe forumul dedicat temei de casă nr. 1 sau pe canalul Temei 1.
La orice întrebare vom răspunde în maxim 24 de ore.
Nu se acceptă întrebări în ultimele 24 de ore înainte de deadline.
La această temă ne propunem să construim un program de procesare de imagini. Vom reprezenta o imagine sub forma unei matrici de pixeli NxM (N linii, M coloane), unde pentru fiecare pixel avem 3 valori: R (Red), G (Green), B (Blue) reprezentând cele 3 componente de culoare. Astfel, o imagine va fi o matrice NxMx3 (spre exemplu, m[5][100][0]
este componenta Red a pixelului de pe linia 5, coloana 100 din imaginea m). Fiecare din cele 3 componente de culoare poate avea doar valori întregi între 0 și 255 inclusiv. Spre exemplu, un pixel cu valorile (0,0,0)
este negru; unul cu valorile (255,255,255)
este alb; unul cu valorile (255,255,0)
este galben etc.
Pentru această temă vom considera că o imagine are originea coordonatelor în colțul stânga-sus. De exemplu, rândul 3 reprezintă al treilea rând de pixeli ai imaginii numărat de sus în jos; coloana 3 reprezintă a treia coloană de pixeli numărată de la stânga la dreapta. W (Width) înseamnă dimensiunea imaginii pe orizontală (numărul de coloane); H (Height) înseamnă dimensiunea imaginii pe verticală (numărul de linii). La funcțiile care primesc ca parametru o pereche de coordonate (x,y)
, coordonata x
este pe orizontală (între 0 și M) iar coordonata y
este pe verticală (între 0 și N).
Pentru această temă trebuie să porniți de la scheletul de cod de aici: tema1_schelet.zip.
În scheletul de cod veți găsi următoarele:
main.c
și respectiv interactive.c
imageprocessing.c
în care voi va trebui să completați implementările funcțiilor pentru taskurile 1-6interactive.c
în care voi va trebui să completați implementarea programului pentru taskul 7main.c
care nu trebuie trimis în arhiva cu tema, scopul lui este să vă ofere un exemplu pentru rularea funcțiilor din celelalte fișiere (ca de exemplu, funcțiile read_from_bmp
și write_to_bmp
).bmp.c
ce conține implementările funcțiilor read_from_bmp
și write_to_bmp
ce trebuie folosite în temă.
imageprocessing.c
primesc ca parametru o imagine care se presupune că a fost deja alocată (dinamic) și returnează imaginea modificată. Dacă aplicați procesările direct pe imaginea primită ca parametru, este suficient să o returnați tot pe ea. Însă dacă alocați o nouă matrice și aplicați procesările pe matricea nou creată, imaginea originală primită ca parametru trebuie dezalocată în aceeași funcție! Atenție: pentru funcțiile de procesare care necesită schimbarea dimensiunii imaginii (e.g. crop, extend), este obligatoriu ca la finalul funcției să obținem o imagine cu exact atâta memorie alocată cât este necesar (e.g. în urma aplicării unui crop
de dimensiune 100×100 pe o imagine 800×600 trebuie să obținem o matrice alocată 100×100).
Implementați în fișierul imageprocessing.c
funcția:
int ***flip_horizontal(int ***image, int N, int M)
Funcția trebuie să returneze imaginea obținută prin oglindirea pe orizontală a imaginii primite ca parametru.
Exemplu. Dacă imaginea originală avea pixelii: $$ A = \begin{bmatrix} p_{11} & p_{12} & p_{13} \\ p_{21} & p_{22} & p_{23} \\ p_{31} & p_{32} & p_{33} \end{bmatrix} $$ atunci, după oglindire, trebuie să aibă: $$ A = \begin{bmatrix} p_{13} & p_{12} & p_{11} \\ p_{23} & p_{22} & p_{21} \\ p_{33} & p_{32} & p_{31} \end{bmatrix} $$
Implementați în fișierul imageprocessing.c
funcția:
int ***rotate_left(int ***image, int N, int M)
Funcția trebuie să returneze imaginea obținută prin rotirea cu 90 de grade la stânga a imaginii primite ca parametru.
Exemplu. Dacă imaginea originală avea pixelii: $$ A = \begin{bmatrix} p_{11} & p_{12} & p_{13} \\ p_{21} & p_{22} & p_{23} \\ p_{31} & p_{32} & p_{33} \end{bmatrix} $$ atunci, după rotire la stânga, trebuie să aibă: $$ A = \begin{bmatrix} p_{13} & p_{23} & p_{33} \\ p_{12} & p_{22} & p_{32} \\ p_{11} & p_{21} & p_{31} \end{bmatrix} $$
Implementați în fișierul imageprocessing.c
funcția:
int ***crop(int ***image, int N, int M, int x, int y, int h, int w)
Funcția trebuie să returneze imaginea obținută prin crop (sub-matrice) care începe (adică colțul stânga-sus al cropului) la coordonatele (x,y), de dimensiune (h,w) (adică sub-matricea rezultată trebuie să aibă h linii, w coloane).
Exemplu. Dacă imaginea originală avea pixelii: $$ A = \begin{bmatrix} p_{11} & p_{12} & p_{13} & p_{14} \\ p_{21} & p_{22} & p_{23} & p_{24} \\ p_{31} & p_{32} & p_{33} & p_{34} \end{bmatrix} $$ atunci, după aplicarea unui crop cu x=2, y=1, h=2, w=2, trebuie să obținem: $$ A = \begin{bmatrix} p_{23} & p_{24} \\ p_{33} & p_{34} \end{bmatrix} $$
Implementați în fișierul imageprocessing.c
funcția:
int ***extend(int ***image, int N, int M, int rows, int cols, int new_R, int new_G, int new_B)
Funcția trebuie să aplice opusul operației de crop, adică să extindă imaginea cu rows
linii atât deasupra cât și dedesubt, și cu cols
coloane atât la stânga cât și la dreapta. Pixelii nou-creați trebuie să aibă toți aceeași culoare, dată de parametrii new_R
, new_G
, new_B
.
Exemplu. Dacă imaginea originală avea pixelii:
$$ A = \begin{bmatrix}
p_{11} & p_{12} \\
p_{21} & p_{22}
\end{bmatrix} $$
atunci, după aplicarea unui extend cu rows=1, cols=2, imaginea devine:
$$ A = \begin{bmatrix}
p_{n} & p_{n} & p_{n} & p_{n} & p_{n} & p_{n} \\
p_{n} & p_{n} & p_{11} & p_{12} & p_{n} & p_{n} \\
p_{n} & p_{n} & p_{21} & p_{22} & p_{n} & p_{n} \\
p_{n} & p_{n} & p_{n} & p_{n} & p_{n} & p_{n}
\end{bmatrix} $$
unde p_{n}
reprezintă pixeli cu valorile culorilor (new_R, new_G, new_B)
.
Implementați în fișierul imageprocessing.c
funcția:
int ***paste(int ***image_dst, int N_dst, int M_dst, int *** image_src, int N_src, int M_src, int x, int y)
Funcția trebuie să facă copy-paste de la imaginea sursă peste imaginea destinație, începând de la coordonatele (x,y) ale imaginii destinație. Dacă imaginea sursă este mai mare decât spațiul disponibil (adică ar depăși marginile imaginii destinație), atunci pixelii care depășesc trebuie ignorați. Atenție: imaginea sursă nu trebuie modificată și nici dezalocată în această funcție! Funcția trebuie să returneze pointer la imaginea destinație.
Exemplu. Dacă imaginea destinație avea pixelii: $$ A = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} $$ și imaginea sursă este: $$ B = \begin{bmatrix} b_{11} & b_{12} & b_{13} \\ b_{21} & b_{22} & b_{23} \\ b_{31} & b_{32} & b_{33} \end{bmatrix} $$ atunci, după aplicarea paste la coordonatele x=1, y=1, trebuie să obținem: $$ A = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & b_{11} & b_{12} \\ a_{31} & b_{21} & b_{22} \end{bmatrix} $$
Implementați în fișierul imageprocessing.c
funcția:
int ***apply_filter(int ***image, int N, int M, float **filter, int filter_size)
Funcția trebuie să modifice imaginea prin aplicarea filtrului primit ca parametru pe toți pixelii imaginii. Un filtru este o matrice bidimensională de dimensiune (filter_size x filter_size) care definește modul în care fiecare pixel este modificat în funcție de valoarea sa și de valorile vecinilor lui. În practică, filtrele sunt folosite pentru tot felul de procesări pe imagini: blur, sharpen, emboss, sobel, edge detection etc.
De exemplu: fie $(R_{22},G_{22},B_{22})$ un pixel din imagine care are următorii vecini:
$$ \begin{bmatrix} (R_{11},G_{11},B_{11}) & (R_{12},G_{12},B_{12}) & (R_{13},G_{13},B_{13}) \\ (R_{21},G_{21},B_{21}) & (R_{22},G_{22},B_{22}) & (R_{23},G_{23},B_{23}) \\ (R_{31},G_{31},B_{31}) & (R_{32},G_{32},B_{32}) & (R_{33},G_{33},B_{33}) \end{bmatrix} $$
și fie filtrul de dimensiune 3 (atentie - valorile din filtru sunt numere (float), nu pixeli!):
$$ F = \begin{bmatrix} f_{11} & f_{12} & f_{13} \\ f_{21} & f_{22} & f_{23} \\ f_{31} & f_{32} & f_{33} \end{bmatrix} $$
Atunci, în imaginea rezultată in urma aplicării filtrului A, pixelul $(R_{22},G_{22},B_{22})$ va fi înlocuit cu $(R'_{22},G'_{22},B'_{22})$ unde:
$$ R'_{22} = \sum_{i=1}^3 \sum_{j=1}^3 R_{ij}f_{ij} $$ $$ G'_{22} = \sum_{i=1}^3 \sum_{j=1}^3 G_{ij}f_{ij} $$ $$ B'_{22} = \sum_{i=1}^3 \sum_{j=1}^3 B_{ij}f_{ij} $$
Se garantează că filtrele primite ca parametru vor avea mereu dimensiune impară.
Scrieți un program (în fișierul interactive.c
) care execută procesări pe imagini în mod interactiv. Programul trebuie să funcționeze astfel:
read_from_bmp
și write_to_bmp
puse la dispoziție în scheletul de cod.
Comenzile care trebuie implementate sunt următoarele:
Nume comandă | Parametri | Descriere |
---|---|---|
e | - | Exit - închide programul |
l | N M path | Load - alocă și încarcă imaginea de dimensiune NxM aflată la calea path |
s | index path | Save - salvează imaginea de pe indexul index la calea specificată prin path |
ah | index | Apply Horizontal Flip - aplică operația de flip pe orizontală imaginii de la indexul index |
ar | index | Apply Rotate - aplică operația de rotație la stânga imaginii de la indexul index |
ac | index x y w h | Apply Crop - aplică operația de crop cu parametrii dați imaginii de la indexul index |
ae | index rows cols R G B | Apply Extend - aplică operația de extend cu parametrii dați imaginii de la indexul index |
ap | index_dst index_src x y | Apply Paste - aplică operația de paste cu parametrii dați imaginii de la indexul index_dst |
cf | size [list of values] | Create filter - alocă și crează un filtru de dimensiune size cu valorile date de lista de valori (exemplu: dacă se creează un filtru de dimensiune 3, atunci după size vor urma 9 valori |
af | index_img index_filter | Apply filter - aplică filtrul de pe indexul index_filter pe imaginea de pe indexul index_img |
df | index_filter | Delete filter - șterge și dezalocă filtrul de pe indexul index_filter |
di | index_img | Delete image - șterge și dezalocă imaginea de pe indexul index_img |
l 768 1024 ./cat.bmp l 768 1024 ./dog.bmp ah 0 ac 1 300 300 200 100 cf 3 0 1 0 1 1 1 0 1 0 af 1 0 ap 0 1 300 300 s 0 ./output.bmp df 0 di 0 di 0 e
char path[100]
)scanf(”%s”, path)
)
Pentru acest task, trebuie să aveți punctajul maxim pe taskul 7 și să nu aveți memory leaks la verificarea folosind utilitarul valgrind pe acesta.
Utilitarul se va rula folosind urmatoarea comanda:
valgrind --tool=memcheck --leak-check=full --error-exitcode=1 ./interactive
La fel ca la tema 0, există o depunctare de până la -20p pentru coding style. Checkerul verifică coding style-ul în mod automat.
Pentru a vă ajuta în dezvoltarea temei, arhiva tema1_checker.zip conține o copie a checkerului.
Pentru a instala dependențele necesare verificării pentru coding style utilizați scriptul install-linters.sh:
./install-linters.sh
Pentru a rula checkerul local, copiați toate fișierele checkerului în același director în care aveți codul sursă, apoi folosiți comanda:
./check.sh
Tema va fi trimisă folosind Moodle, cursul Programarea Calculatoarelor (CB & CD), activitatea “Tema 1”.
Toate temele sunt testate în mod automat pe Moodle.
Arhiva temei se va încărca folosind formularul de submisie (butonul Add submission.
Rezultatele vor fi disponibile în secțiunea Feedback - nota apare la linia Grade, iar outputul checkerului și erorile apar la sectiunea Feedback comments. Dacă apare un buton albastru în formă de plus, trebuie să dați click pe el pentru a afișa întregul output al checkerului.
Citiți cu atenție informațiile afișate în Feedback pentru a vă asigura că tema a fost rulată cu succes; o eroare comună este dată de faptul că conținutul arhivei nu respectă structura dorită (ex. fișierele sunt într-un alt director).
Punctajul final al temei este afișat la linia Grade și la finalul outputului din checker.
Conținutul arhivei trebuie să fie următorul:
Lista nu este exhaustivă.