Tema 1
.În cadrul acestei teme veți lucra cu fișiere imagine, mai exact cu formatul PPM din pachetul Netpbm. Le veți primi sub formă de listă de charactere.
Numele arhivelor trebuie sa fie de forma <Nume>_<Prenume>_<Grupa>_T1.zip (daca aveti mai multe prenume sau nume, le puteti separa prin '-')
Primele 3 linii vor fi mereu aproape la fel, formatul P3 pe prima linie, lungimea și înălțimea pe a doua, culoarea maximă pe a treia, care va fi mereu 255. Apoi fiecare pixel va fi pe o linie, mai intâi valoarea de roșu (de la 0 la 255) apoi verde și albastru, urmat direct de un \n.
Pentru a reprezenta imaginea, se va folosi tipul Image definit mai jos:
type Image = List[List[Pixel]] type GrayscaleImage = List[List[Double]]
unde Pixel
este o clasă in care se rețin valorile celor 3 culori (roșu, verde, albastru) sub forma de Integer. Clasa Pixel se găsește in folderul util din schelet.
Se dau două imagini de aceeași lungime. Să se alăture vertical astfel încât prima imagine să fie sus și a doua jos.
Se va completa pentru asta funcția
def verticalConcat(image1: Image, image2: Image): Image = ???
Se dau două imagini de aceeași înălțime. Să se alăture orizontal astfel încât prima imagine să fie în stânga și a doua în dreapta.
Se va completa pentru asta funcția
def horizontalConcat(image1: Image, image2: Image): Image = ???
La această cerință se va implementa o rotație în sens trigonometric cu multipli de 90 de grade (90, 180, …).
Se va completa pentru asta funcția
def rotate(image: Image, degrees: Integer): Image = ???
O operatie care apare destul de des in procesarea imaginilor este detectia obiectelor, iar o parte importanta din acest proces complex este detectia frontierelor (marginilor) obiectelor. Un algoritm simplu pentru detectia frontierelor este detectorul/operatorul Sobel.
Acesta se bazeaza pe conceptul de convolutie, care este folosit extrem de des in prelucrarea imaginilor. Convolutia reprezinta o transformare a imaginii in care fiecare pixel din imaginea noua este obtinut prin insumarea valorilor ponderate ale unui pixel si ale vecinilor lui. Ponderile cu care se inmultesc fiecare dintre vecinii pixelului sunt reprezentate de obicei ca o matrice numita nucleu sau kernel de convolutie. Variind kernelul, putem obtine diverse efecte asupra imaginii.
Presupunem ca vrem sa aplicam detectia frontierelor pe aceasta imagine:
Pași detectorului Sobel sunt următorii:
1. Se va aplica funcția grayscale
def toGrayScale(pixel: Pixel) : Double = ???
din Util pe fiecare pixel (detectorul functioneaza doar pe imagini alb-negru)
2. Se va aplica un blur gaussian prin convolutie cu kernelul:
(acest pas nu face parte din detector propriu-zis, dar are rolul de a elimina zgomotul din imagine, care ar fi foarte vizibil in rezultat)
3. Se vor aplica apoi, prin convolutie, kernelurile Gx și Gy pentru a genera Mx și My
(acestea detecteaza variatii bruste ale intensitatii pe orizontala si, respectiv, pe veriticala)
Pentru pasii 2 si 3, se va implementa functia
def applyConvolution(image: GrayscaleImage, kernel: GrayscaleImage) : GrayscaleImage = ???
4. Se vor combina Mx și My, adunându-se (element cu element) fiecare pixel în modul (astfel, obtinem o aproximare destul de buna a schimbarii de intensitate in jurul fiecarui pixel)
5. Se va pune un prag (threshold) T, astfel încât fiecare pixel cu o valoare mai mica decat T va fi negru (rgb 0 0 0) și fiecare pixel deasupra T va fi alb (rgb 255 255 255)
Se va completa pentru asta funcția
def edgeDetection(image: Image, threshold : Double): Image = ???
getNeighbors
din fisierul Util.scala
Triunghiul lui Pascal este o reprezentare 2-dimensionala a unor valori care apar in foarte multe aplicatii de combinatorica dar si algebra. Triunghiul lui Pascal cu 5 linii este ilustrat mai jos:
1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 ...
l
din triunghiul lui Pascal este urmatoarea: primul element $ l_0$ (si ultimul) este intotdeauna 1; n
si coloana k
este $ C_n^k$ (combinari de $ n$ luate cate $ k$ ), si atunci relatia de mai sus devine: $ C_n^k = C_{n-1}^k + C_{n-1}^{k-1} $. Preferam in general sa implementam calculul $ C_n^k$ folosind aceasta relatie de recurenta, pentru ca este mai eficientă din punct de vedere al complexității temporale față de formula cu factoriale.n
este numărul liniei ( $ n \ge 0 $).M
pentru a stabili numarul de culori folosite in colorare. O culoare este, de fapt, un Pixel. Colorarea inseamna ca fiecarei valori din triunghiul modulo al lui Pascal i se asociaza un Pixel cu anumite valori (rosu, verde, albastru), valori ce urmeaza regula prezentata in functia pickColor
. Tot ce este deasupra diagonalei principale va rămâne negru și nu se va aplica funcția!def pickColor(i: Integer) : Pixel = { if (i == 0) Pixel(255, 0, 0) else if (i == 1) Pixel(0, 0, 255) else if (i == 2) Pixel(0, 255, 0) else if (i == 3) Pixel(255, 255, 255) else Pixel(0, 0, 0) }
def moduloPascal(M: Integer, // nr. de culori pickColor: Integer => Pixel, // functia de colorare size: Integer // dimensiunea triunghiului (nr de linii) ): Image = ???