This is an old revision of the document!
Actualizări:
Scopul temei:
Procesarea de imagini se refera la aplicarea unor algoritmi specifici pe continutul unei imagini pentru a obtine anumite efecte (blur, sharpening, etc.) sau rezultate (face detection/recognition, etc.). In aceasta tema vom lucra cu unul dintre cele mai simple formate de imagini si anume formatul BMP.
O imagine BMP are următoarea structură:
Header-ele pe care le puteți folosi în implementare se află în scheletul de cod asociat temei.
Veti avea de rezolvat 5 task-uri obligatorii si un task bonus.
Acest task presupune transformarea unei imagini color intr-o imagine alb-negru.
Procedeul prin care o imagine color se transforma intr-o imagine alb-negru este urmatorul: fiecare pixel din imaginea color, fie acesta (R, G, B), va deveni (X, X, X) unde X = [ (R + G + B) / 3] unde prin [] se intelege parte intreaga (inferioara). De exemplu pixelul (3, 2, 2) va deveni (2, 2, 2), iar pixelul (10, 20, 30) va deveni (20, 20, 20).
Daca numele imaginii este <nume>.bmp atunci imaginea alb-negru se va scrie in fisierul <nume>_black_white.bmp (de exemplu, daca prima linie din fisierul input.txt este image.bmp atunci imaginea alb negru se va scrie in fisierul image_black_white.bmp).
Cu siguranta vi s-a intamplat cel putin o data sa nu puteti posta o poza pe o retea de socializare (ex. Instagram) din motiv ca poza respectiva nu este patratica si aceasta sa fie decupata, pierzand informatii utile din imaginea initiala. Ne dorim sa implementam functionalitatea unei aplicatii care rezolva aceasta problema, operatia asociata mai fiind numita de no-crop. Acest task presupune bordarea unei imagini in scopul obtinerii variantei patratice a acesteia.
Procedeul prin care se transforma o imagine oarecare intr-o imagine patratica este:
Sa presupunem ca avem urmatoarea imagine:
(1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1)
Observam ca inaltimea este egala cu 2, iar latimea este egala cu 5. Prin urmare, inaltimea imaginii trebuie completata astfel incat aceasta sa devina egala cu latimea (5). Diferenta intre cele doua dimensiuni este 3, asa ca imaginea rezultat va arata astfel:
(255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (1 1 1) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255) (255 255 255)
Daca numele imaginii este <nume>.bmp atunci imaginea rezultata patratica se va scrie in fisierul <nume>_nocrop.bmp (de exemplu, daca prima linie din fisierul de input este image.bmp atunci imaginea rezultata se va scrie in fisierul image_nocrop.bmp).
Exemplu concret: Daca imaginea initiala arata astfel:
Atunci noua imagine ar trebui sa arate astfel:
In cadrul acestui task va trebui sa aplicati anumite filtre pe o imagine. Un filtru convolutional este o matrice care este aplicata fiecarui pixel din imagine pentru a obtine noul pixel. In continuare o sa vedem ce inseamna sa aplici o matrice (filtru de 3×3) unui pixel. Fie $(B_{22},G_{22},R_{22})$ un pixel din imagine care are urmatorii vecini:
$$ \begin{bmatrix} (B_{11},G_{11},R_{11}) & (B_{12},G_{12},R_{12}) & (B_{13},G_{13},R_{13}) \\ (B_{21},G_{21},R_{21}) & (B_{22},G_{22},R_{22}) & (B_{23},G_{23},R_{23}) \\ (B_{31},G_{31},R_{31}) & (B_{32},G_{32},R_{32}) & (B_{33},G_{33},R_{33}) \end{bmatrix} $$
si fie filtrul:
$$ A = \begin{bmatrix} a_{11} & a_{12} & a_{13} \\ a_{21} & a_{22} & a_{23} \\ a_{31} & a_{32} & a_{33} \end{bmatrix} $$
Atunci, in imaginea rezultata in urma aplicarii filtrului A, pixelul $(B_{22},G_{22},R_{22})$ va fi inlocuit cu $(B'_{22},G'_{22},R'_{22})$ unde:
$$ B'_{22} = \sum_{i=1}^3 \sum_{j=1}^3 B_{ij}a_{ij} $$ $$ G'_{22} = \sum_{i=1}^3 \sum_{j=1}^3 G_{ij}a_{ij} $$ $$ R'_{22} = \sum_{i=1}^3 \sum_{j=1}^3 R_{ij}a_{ij} $$
Sa presupunem ca avem urmatoarea imagine:
(2 2 2) (3 3 3) (0 0 0) (0 0 0) (10 10 10) (1 1 1) (0 0 0) (0 0 0) (0 0 0) (0 0 0) (50 50 5) (0 0 0) (0 0 0) (0 0 0) (240 0 240) (0 0 0)
Pentru imaginea de mai sus, fisierul BMP ar arata astfel (ignorand cele 2 headere):
(0 0 0) (0 0 0) (240 0 240) (0 0 0) No padding (0 0 0) (0 0 0) (50 50 5) (0 0 0) No padding (10 10 10) (1 1 1) (0 0 0) (0 0 0) No padding (2 2 2) (3 3 3) (0 0 0) (0 0 0) No padding
Pe imaginea de mai sus dorim sa aplicam urmatorul filtru:
$$ \begin{bmatrix}0 & 1 & 0\\1 & 1 & 1\\0 & 1 & 0 \end{bmatrix} $$
Imaginea rezultata va fi:
(15 15 15) (6 6 6) (3 3 3) (0 0 0) (13 13 13) (14 14 14) (51 51 6) (0 0 0) (10 10 10) (51 51 6) (255 50 245) (50 50 5) (0 0 0) (240 0 240) (255 50 245) (240 0 240)
Pentru rezolvarea acestui task va trebui sa aplicati un filtru asupra imaginii citite la taskurile anterioare.
Daca numele imaginii este <nume>.bmp atunci imaginea rezultata dupa aplicarea filtrului se va scrie in fisierul <nume>_filter.bmp (de exemplu, daca prima linie din fisierul input.txt este image.bmp atunci imaginea rezultata se va scrie in fisierul image_filter.bmp).
Exemplu concret: Daca imaginea initiala arata astfel:
Iar filtrul pe care dorim sa il aplicam este:
$$ \begin{bmatrix}0 & 1 & 0\\1 & -4 & 1\\0 & 1 & 0 \end{bmatrix} $$
Atunci noua imagine ar trebui sa arate astfel:
In cadrul acestui task va trebui sa aplicati anumite filtre de pooling pe o imagine, si anume Max Pooling Layer respectiv Min Pooling Layer. Un filtru de pooling este o operatie care este aplicata fiecarui pixel din imagine pentru a obtine un nou pixel. In continuare o sa vedem ce inseamna sa aplici o operatie de min/max pooling unui pixel. Fie $(B_{22},G_{22},R_{22})$ un pixel din imagine care are urmatorii vecini:
$$ \begin{bmatrix} (B_{11},G_{11},R_{11}) & (B_{12},G_{12},R_{12}) & (B_{13},G_{13},R_{13}) \\ (B_{21},G_{21},R_{21}) & (B_{22},G_{22},R_{22}) & (B_{23},G_{23},R_{23}) \\ (B_{31},G_{31},R_{31}) & (B_{32},G_{32},R_{32}) & (B_{33},G_{33},R_{33}) \end{bmatrix} $$
Atunci, in imaginea rezultata in urma aplicarii filtrului de pooling max de dimensiune 3, pixelul $(R_{22},G_{22},B_{22})$ va fi inlocuit cu $(B'_{22},G'_{22},R'_{22})$ unde:
$$ B'_{22} = \max(B_{ij}a_{ij}),\ \ \ i=1..3,\ j=1..3 $$ $$ G'_{22} = \max(G_{ij}a_{ij}),\ \ \ i=1..3,\ j=1..3 $$ $$ R'_{22} = \max(R_{ij}a_{ij}),\ \ \ i=1..3,\ j=1..3 $$
Analog se aplica si pentru filtrul de pooling min.
Sa presupunem ca avem urmatoarea imagine:
(2 2 2) (3 3 3) (0 0 0) (0 0 0) (10 10 10) (1 1 1) (7 7 7) (0 0 0) (12 12 12) (0 0 0) (50 50 5) (0 0 0) (0 0 0) (0 0 0) (240 0 240) (0 0 0)
Pentru imaginea de mai sus, fisierul BMP ar arata astfel (ignorand cele 2 headere):
(0 0 0) (0 0 0) (240 0 240) (0 0 0) No padding (12 12 12) (0 0 0) (50 50 5) (0 0 0) No padding (10 10 10) (1 1 1) (7 7 7) (0 0 0) No padding (2 2 2) (3 3 3) (0 0 0) (0 0 0) No padding
Pe imaginea de mai sus dorim sa aplicam filtrul max de pooling de dimensiune 3:
Imaginea rezultata va fi:
(12 12 12) (240 50 240) (240 50 240) (240 50 240) (12 12 12) (240 50 240) (250 50 240) (240 50 240) (12 12 12) (50 50 12) (50 50 7) (50 50 7) (10 10 10) (10 10 10) (7 7 7) (7 7 7)
Pentru rezolvarea acestui task va trebui sa aplicati un filtru de pooling (diferentiat prin m pentru filtru de min, respectiv M pentru filtru de max) asupra imaginii citite la taskurile anterioare.
Daca numele imaginii este <nume>.bmp atunci imaginea rezultata dupa aplicarea filtrului se va scrie in fisierul <nume>_pooling.bmp (de exemplu, daca prima linie din fisierul input.txt este image.bmp atunci imaginea rezultata se va scrie in fisierul image_pooling.bmp).
Exemplu concret: Daca imaginea initiala arata astfel:
Iar filtrul pe care vrem sa il aplicam este unul de max pooling de dimensiune 5, imaginea rezultata va fi:
In cadrul acestui task, ne propunem sa grupam (clusterizam) pixelii din cadrul imaginii noastre in zone asemanatoare de pixeli cu scopul de a evidentia aceste zone vizual sau pentru a transforma imaginea initiala intr-o imagine cat mai artificiala/simplista (o sa vedem in exemplul concret cum arata o imagine clusterizata).
Pentru a stii cum incadram doi pixeli oarecere intr-o zona, operatia de clustering va primi ca input o valoare de threshold (prag) si o va folosi dupa cum este descris si in algoritmul de mai jos asociat operatiei.
Algoritmul de clustering pe care o sa il implementati (si descrie formal ceea ce a fost descris mai sus) este urmatorul:
1) Se alege un pixel din imagine care nu a fost inclus in nicio zona. Fie acest pixel $ (R, G, B) $. Daca toti pixelii au fost inclusi intr-o zona atunci algoritmul s-a terminat;
2) Pornind de la acest pixel se detemina zona de pixeli care satisface simultan urmatoarele conditii:
3) Se determina suma valorilor pixelilor din zona curenta cat si numarul acestora (pentru a aproxima culoarea pe care o sa o aiba zona curenta).
$$ N = numarul\ de\ pixeli\ din\ zona\ curenta $$ $$ sum_{R} = \sum_{i=1}^N R_{i} $$ $$ sum_{G} = \sum_{i=1}^N G_{i} $$ $$ sum_{B} = \sum_{i=1}^N B_{i} $$ $ (R_{i}, G_{i}, B_{i}) $ reprezinta valoarea pixelului i din zona curenta;
4) Fiecare pixel $ (R',G',B') $ din zona determinata va avea ca valoare noua media aritmetica a valorilor pixelilor din zona (culoarea zonei), adica $ (sum_{R} / N, sum_{G} / N, sum_{B} / N) $;
5) Dupa deteminarea noii zone se reia algoritmul incepand cu pasul 1).
Sa presupunem ca avem urmatoarea imagine (rasucita deja si in ordinea $ (R, G, B) $) si threshold = 10:
(10 10 10) (12 10 13) (10 10 10) (30 30 30) (30 29 31) (10 10 10) (12 15 10) (10 10 10) (30 28 30) (22 20 23) (11 11 11) (11 12 11) (10 10 10) (30 33 30) (22 21 23) (12 12 12) (12 10 12) (10 10 10) (30 30 31) (22 20 23)
Initial niciun pixel nu este inclus in vreo zona. Se alege pixelul din coltul stanga-sus de valoare (10 10 10) deoarece acesta nu se afla deja in nicio zona. Se determina zona maximala din care acesta face parte:
(10 10 10) (12 10 13) (10 10 10) (30 30 30) (30 29 31) (10 10 10) (12 15 10) (10 10 10) (30 28 30) (22 20 23) (11 11 11) (11 12 11) (10 10 10) (30 33 30) (22 21 23) (12 12 12) (12 10 12) (10 10 10) (30 30 31) (22 20 23)
Calculam numarul de elemente si sumele asociate zonei curente:
$ N = 12 $
$ sum_{R} = 130 $, $ sum_{G} = 130 $, $ sum_{B} = 129 $
Prin urmare, fiecare pixel din zona rosie va fi inlocuit cu $ (sum_{R} / N, sum_{G} / N, sum_{B} / N) $, deci $ (10, 10, 10) $.
(10 10 10) (10 10 10) (10 10 10) (30 30 30) (30 29 31) (10 10 10) (10 10 10) (10 10 10) (30 28 30) (22 20 23) (10 10 10) (10 10 10) (10 10 10) (30 33 30) (22 21 23) (10 10 10) (10 10 10) (10 10 10) (30 30 31) (22 20 23)
Alegem din nou un pixel care nu a fost inclus intr-o zona conform algoritmului de selectie. Acest pixel este cel de pe linia 1 si coloana 4 (indexarea incepe la 1) de valoare (30 30 30). Se determina zona maximala din care acesta face parte (colorata cu verde):
(10 10 10) (10 10 10) (10 10 10) (30 30 30) (30 29 31) (10 10 10) (10 10 10) (10 10 10) (30 28 30) (22 20 23) (10 10 10) (10 10 10) (10 10 10) (30 33 30) (22 21 23) (10 10 10) (10 10 10) (10 10 10) (30 30 31) (22 20 23)
Calculam numarul de elemente si sumele asociate zonei curente:
$ N = 5 $
$ sum_{R} = 150 $, $ sum_{G} = 150 $, $ sum_{B} = 152 $
Prin urmare, fiecare pixel din zona verde va fi inlocuit cu $ (sum_{R} / N, sum_{G} / N, sum_{B} / N) $, deci $ (30, 30, 30) $.
(10 10 10) (10 10 10) (10 10 10) (30 30 30) (30 30 30) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 21 23) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23)
Alegem din nou un pixel care nu a fost inclus intr-o zona conform algoritmului de selectie. Acest pixel este cel de pe linia 2 si coloana 5 (indexarea incepe la 1) de valoare (12 10 13). Se determina zona maximala din care acesta face parte (colorata cu albastru):
(10 10 10) (10 10 10) (10 10 10) (30 30 30) (30 30 30) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 21 23) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23)
Calculam numarul de elemente si sumele asociate zonei curente:
$ N = 3 $
$ sum_{R} = 66 $, $ sum_{G} = 61 $, $ sum_{B} = 69 $
Prin urmare, fiecare pixel din zona albastra va fi inlocuit cu $ (sum_{R} / N, sum_{G} / N, sum_{B} / N) $, deci $ (22, 20, 23) $.
(10 10 10) (10 10 10) (10 10 10) (30 30 30) (30 30 30) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23) (10 10 10) (10 10 10) (10 10 10) (30 30 30) (22 20 23)
Am determinat astfel 3 zone similare si putem spune ca am clusterizat imaginea. Aceasta imagine este cea care va fi afisata in fisierul .bmp corespunzator acestui task.
Pentru rezolvarea acestui task va trebui sa aplicati operatia de clusterizare asupra imaginii citite la taskurile anterioare.
Daca numele imaginii este <nume>.bmp atunci imaginea rezultata dupa aplicarea filtrului se va scrie in fisierul <nume>_clustered.bmp (de exemplu, daca prima linie din fisierul input.txt este image.bmp atunci imaginea rezultata se va scrie in fisierul image_clustered.bmp).
Exemplu concret: Daca imaginea initiala arata astfel:
Si vrem sa realizam o operatie de clusterizare avand valoarea de threshold egala cu 100, imaginea rezultata va fi:
Intrucat unul din scopurile principale ale acestei teme este alocarea dinamica a memoriei, pentru a rezolva acest task trebuie sa nu aveti nicio eroare sau leak de memorie la rularea utilitarului valgrind pe aceasta.
Utilitarul se va rula folosind urmatoarea comanda:
valgrind --tool=memcheck --leak-check=full --error-exitcode=1 ./bmp
make build
trebuie sa fie neaparat bmp!
Fisierul de intrare, in care se afla informatiile despre fiecare task, se numeste input.txt. Acesta are urmatoarea structura:
Format filtru:
N VAL_11 VAL_12 ... VAL_1N VAL_21 VAL_22 ... VAL_2N ........................ VAL_N1 VAL_N2 ... VAL_NN
Format filtru pooling:
m/M N
Exemplu
test1.bmp ./input/filters/filter1.txt ./input/pooling/pooling1.txt ./input/clustering/cluster1.txt
input
din checker.
Dupa cum a fost descris deja, fiecare task are ca scop producerea unei noi imagini, dupa aplicarea unei operatii de procesare pe imaginea initiala. Prin urmare, programul vostru va trebui sa citeasca imaginea de input si sa produca 5 imagini noi, fiecare avand numele specificat in cadrul task-ului corespunzator.
Bineinteles, puteti sa sariti peste implementarea unui task sau sa implementati selectiv un anumit numar de taskuri, singura restrictie pe care o aveti este sa nu primiti erori de tipul Segmentation fault, deoarece acestea vor invalida testarea temei.
Resursele pentru tema se pot descarca de aici. Sunt prezente:
Tema va fi trimisa folosind v2.vmchecker, cursul Programarea Calculatoarelor, tema Image Processing.
Punctajul:
Lista nu este exhaustivă. Se pot aplica chiar depunctări mai mari în cazuri excepționale.