Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:2023:hw1 [2023/03/15 15:34] alexandra.udrescu01 |
pp:2023:hw1 [2023/03/19 12:06] (current) alexandra.udrescu01 |
||
---|---|---|---|
Line 5: | Line 5: | ||
**DEADLINE 7 aprilie ** | **DEADLINE 7 aprilie ** | ||
- | * Temele trebuie submise pe curs.upb.ro | + | * Temele trebuie submise pe curs.upb.ro, in assignment-ul numit ''Tema 1''. |
- | * Temele ce nu sunt acceptate de validatorul de arhive **NU** vor fi puncate | + | * Temele ce nu sunt acceptate de validatorul de arhive **NU** vor fi puncate. |
- | * Va sugeram ca dupa ce ati incarcat o arhiva, sa o descarcati si sa o testati cu validatorul de arhive | + | * Va sugeram ca dupa ce ati incarcat o arhiva, sa o descarcati si sa o testati cu validatorul de arhive. |
</note> | </note> | ||
Line 13: | Line 13: | ||
<note warning> | <note warning> | ||
- | **Folosiți un stil de programare funcțional. NU aveți voie cu**: | + | **Folosiți un stil de programare funcțional. NU se vor accepta:** |
- | + | * **Efecte laterale** (de exemplu modificarea parametrilor dați ca input la funcție) | |
- | - **Efecte laterale** (de exemplu modificarea parametrilor dați ca input la funcție) | + | * **var** (**val** este ok!) |
- | + | ||
- | - **var** (**val** este ok!) | + | |
</note> | </note> | ||
<note>Scheletul se poate găsi la: {{:pp:2023:tema1_2023_pp-skel.zip|}}</note> | <note>Scheletul se poate găsi la: {{:pp:2023:tema1_2023_pp-skel.zip|}}</note> | ||
+ | <note> | ||
+ | Validatorul de arhive se poate găsi la: {{:pp:2023:archive_validator_hw1.zip|}} | ||
+ | \\ | ||
+ | Formatul arhivelor este: | ||
+ | * util: Util.scala, Pixel.scala | ||
+ | * Solution.scala | ||
+ | * ID.txt - acest fisier va contine o singura linie, formata din ID-ul unic al fiecarui student | ||
+ | Numele arhivelor trebuie sa fie de forma **<Nume>_<Prenume>_<Grupa>_T1.zip** (daca aveti mai multe prenume sau nume, le puteti separa prin '-') | ||
+ | |||
+ | </note> | ||
<hidden Exemplu fișier PPM (cu explicații)> | <hidden Exemplu fișier PPM (cu explicații)> | ||
Line 47: | Line 55: | ||
<note tip>Pentru a vedea ușor imaginea generată de voi, puteți folosi [[https://0xc0de.fr/webppm/]] </note> | <note tip>Pentru a vedea ușor imaginea generată de voi, puteți folosi [[https://0xc0de.fr/webppm/]] </note> | ||
- | Pentru a reprezenta imaginea, se va folosi tipul Image definit mai jos: | + | **Pentru a reprezenta imaginea, se va folosi tipul Image definit mai jos:** |
<code scala> | <code scala> | ||
type Image = List[List[Pixel]] | type Image = List[List[Pixel]] | ||
type GrayscaleImage = List[List[Double]] | type GrayscaleImage = List[List[Double]] | ||
</code> | </code> | ||
- | unde Pixel este o clasă in care se rețin valorile celor 3 culori (roșu, verde, albastru) sub forma de Integer. Clasa Pixel ce se găsește in folderul util din schelet. | + | **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.** |
====1. Alăturare de imagini pe verticală (10p)==== | ====1. Alăturare de imagini pe verticală (10p)==== | ||
Line 126: | Line 134: | ||
Se va completa pentru asta funcția | Se va completa pentru asta funcția | ||
<code scala> | <code scala> | ||
- | def rotate_a(image: Image, degrees: Integer): Image = ??? | + | def rotate(image: Image, degrees: Integer): Image = ??? |
</code> | </code> | ||
Line 133: | Line 141: | ||
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 [[https://en.wikipedia.org/wiki/Sobel_operator|detectorul/operatorul Sobel]]. | 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 [[https://en.wikipedia.org/wiki/Sobel_operator|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. | + | 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. |
{{ :pp:2023:convolution.gif?500 |}} | {{ :pp:2023:convolution.gif?500 |}} | ||
Line 163: | Line 171: | ||
<hidden Cod Scala pentru Kernel> | <hidden Cod Scala pentru Kernel> | ||
<code scala> | <code scala> | ||
- | val gaussianBlurKernel: GrayscaleImage = GrayscaleImage( | + | val gaussianBlurKernel: GrayscaleImage = List[List[Double]]( |
List( 1, 4, 7, 4, 1), | List( 1, 4, 7, 4, 1), | ||
List( 4,16,26,16, 4), | List( 4,16,26,16, 4), | ||
Line 175: | Line 183: | ||
<hidden imagine blurred> {{ :pp:2023:hw1_blurred.jpg?300 |}} </hidden> | <hidden imagine blurred> {{ :pp:2023:hw1_blurred.jpg?300 |}} </hidden> | ||
- | 3. Se vor aplica apoi kernelurile Gx și Gy pentru a genera Mx și My | + | 3. Se vor aplica apoi, prin convolutie, kernelurile Gx și Gy pentru a genera Mx și My |
<hidden Gx><code scala> | <hidden Gx><code scala> | ||
val Gx : GrayscaleImage = List( | val Gx : GrayscaleImage = List( | ||
Line 221: | Line 229: | ||
def edgeDetection(image: Image, threshold : Double): Image = ??? | def edgeDetection(image: Image, threshold : Double): Image = ??? | ||
</code> | </code> | ||
+ | |||
+ | <note tip>**Hint:** la acest exercitiu este indicat sa folositi functia ''getNeighbors'' din fisierul Util.scala</note> | ||
====5. Triunghi pascal cu resturi - colorare (40p)==== | ====5. Triunghi pascal cu resturi - colorare (40p)==== | ||
- | Se calculează [[https://en.wikipedia.org/wiki/Pascal%27s_triangle| Triunghiul lui Pascal]]. Acesta este o reprezentare 2-dimensionala, a unor valori care apar in foarte multe aplicatii de combinatorica dar si algebra. Triunghiul lui Pascal cu 4 linii este ilustrat mai jos: | + | [[https://en.wikipedia.org/wiki/Pascal%27s_triangle| 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: |
<code> | <code> | ||
1 | 1 | ||
Line 233: | Line 243: | ||
... | ... | ||
</code> | </code> | ||
- | * Cea mai simpla modalitate de a descrie o linie ''l'' din triunghiul lui Pascal este urmatoarea: primul element $math[l_0] (si ultimul) este intotdeauna 1; Pt $math[i > 0], $math[l_i = lp_{i-1} + lp_{i}]. | ||
- | * Cu un pic de atentie, observam ca o valoare de pe linia ''n'' si coloana ''k'' este $math[C_n^k] (combinari de $math[n] luate cate $math[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 $math[C_n^k] folosind aceasta relatie de recurenta, pentru ca este mai eficientă și evită mai bine riscul de overflow față de formula cu factoriale. | + | * Cea mai simpla modalitate de a descrie o linie ''l'' din triunghiul lui Pascal este urmatoarea: primul element $math[l_0] (si ultimul) este intotdeauna 1; |
- | + | * Pentru restul elementelor din triunghi, se foloseste recurenta $math[i > 0], $math[l_i = lp_{i-1} + lp_{i}], unde $math[l] este linia curenta, si $math[lp] este linia precedenta. | |
- | * Asadar, triunghiul lui Pascal este format la fiecare linie de $ C_n^0 C_n^1 ... C_n^n $ unde n e numărul liniei (>= 0). Linia $math[n] din triunghiul lui Pascal descrie coeficienții din $ (a + b)^n $ după cum se poate vedea aici: | + | * Cu puțină atentie, observam ca o valoare de pe linia ''n'' si coloana ''k'' este $math[C_n^k] (combinari de $math[n] luate cate $math[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 $math[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. |
+ | * Asadar, triunghiul lui Pascal este format pe fiecare linie din $ C_n^0 C_n^1 ... C_n^n $ unde ''n'' este numărul liniei ( $ n \ge 0 $). | ||
+ | /* Linia $math[n] din triunghiul lui Pascal descrie coeficienții din $ (a + b)^n $ după cum se poate vedea mai jos: | ||
<hidden Vizualizare Coeficienți binomiali> | <hidden Vizualizare Coeficienți binomiali> | ||
{{ :pp:2023:pascal_dimension_visualisation.png?300 |}} | {{ :pp:2023:pascal_dimension_visualisation.png?300 |}} | ||
- | </hidden> | + | </hidden> */ |
- | + | <hidden Triunghiul lui Pascal size = 5 ; reprezentare ca matrice> | |
- | <note warning>Nu folosiți formula combinăriilor cu factorial pentru acest exercițiu!</note> | + | |
- | + | ||
- | + | ||
- | Așadar, limitându-ne la 5 x 5, triunghiul va arăta așa: | + | |
- | <hidden> | + | |
<code> | <code> | ||
1 0 0 0 0 | 1 0 0 0 0 | ||
Line 255: | Line 261: | ||
</code> | </code> | ||
</hidden> | </hidden> | ||
- | + | * In cele ce urmeaza, nu vom construi triunghiul lui Pascal cu valori absolute, ca mai sus, ci in locul fiecarei valori $math[l_i] vom construi valoarea $math[l_i \% M]. Deoarece numerele apărute in recurența mentionata anterior cresc foarte rapid, ar putea aparea un overflow la adunare, ducand la rezultate eronate. Asadar, pentru a obtine valoarea $math[l_i % M], vom folosi tot relatia de recurenta anterioara: $ Cmodulo_n^k = (Cmodulo_{n-1}^k + Cmodulo_{n-1}^{k-1}) \% M $. Observati ca $math[Cmodulo_n^k = C_n^k \% M = (C_{n-1}^k + C_{n-1}^{k-1}) \% M = (C_{n-1}^k \% M + C_{n-1}^{k-1} \% M) \% M = (Cmodulo_{n-1}^k + Cmodulo_{n-1}^{k-1}) \% M] Formula recursivă ne ajută mai mult deoarece putem să aplicăm modulo la fiecare pas. | |
- | Prin triunghiul lui Pascal se pot genera diferite șabloane (patterns), a căror reprezentare grafică este influențata de modul de colorare al matricii rezultate. Vom alege să folosim M culori, fiecare numar x din matrice avand asociată culoarea conform functiei urmatoare ce primește o valoare a restului impărțirii la M ( $ M \le 5 $ ): | + | <hidden Triunghiul lui Pascal size = 5 ; M = 4> |
+ | <code> | ||
+ | 1 0 0 0 0 | ||
+ | 1 1 0 0 0 | ||
+ | 1 2 1 0 0 | ||
+ | 1 3 3 1 0 | ||
+ | 1 0 2 0 1 | ||
+ | </code> | ||
+ | </hidden> | ||
+ | * Prin triunghiul lui Pascal **cu valori modulo** se pot genera diferite șabloane (patterns), ale căror reprezentare grafică este influențată de modul de colorare al matricii rezultate. Vom folosi ''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! | ||
<code scala> | <code scala> | ||
def pickColor(i: Integer) : Pixel = { | def pickColor(i: Integer) : Pixel = { | ||
Line 267: | Line 282: | ||
</code> | </code> | ||
- | {{ :pp:2023:hw1_pascal_visualization.png?600 |}} | + | <hidden Triunghiul lui Pascal size = 5 ; M = 4 DUPA COLORARE>{{:pp:2023:size5m4.png?250|}}</hidden> |
+ | * Se va completa pentru acest task functia: | ||
+ | <code scala> | ||
+ | def moduloPascal(M: Integer, // nr. de culori | ||
+ | pickColor: Integer => Pixel, // functia de colorare | ||
+ | size: Integer // dimensiunea triunghiului (nr de linii) | ||
+ | ): Image = ??? | ||
+ | </code> | ||
+ | * Sugestia de rezolvare ar fi generarea linie cu linie a triunghiului Pascal începând de la $ C_0^0 = 1 $ pe prima linie. Aceasta este o abordare de **programare dinamică**. | ||
+ | <note warning>Nu folosiți formula combinăriilor cu factorial pentru acest exercițiu!</note> | ||
+ | <note important>Triunghiul lui Pascal fiind infinit, va avea o marime limitata la $ size^2 $ (size > 2).</note> | ||
- | Pentru un număr M ales, se calculează resturile (modulo) acestor numere la împărțirea cu M. Formula recursivă ne ajută mai mult deoarece putem să aplicăm modulo la fiecare pas. Așadar nu contează de câte ori și unde aplicăm % M. Vom folosi formula finală: $ C_n^k = (C_{n-1}^k + C_{n-1}^{k-1}) \% M $ la fiecare pas. | + | {{ :pp:2023:hw1_pascal_visualization.png?600 | }} |
- | Sugestia de rezolvare ar fi generare linie cu linie a triunghiului Pascal începând de la $ C_0^0 = 1 $ pe prima linie. Aceasta este o abordare de **programare dinamică**. | ||
- | <hidden Exemplul anterior pentru M = 4> | ||
- | <code> | ||
- | 1 0 0 0 0 | ||
- | 1 1 0 0 0 | ||
- | 1 2 1 0 0 | ||
- | 1 3 3 1 0 | ||
- | 1 0 2 0 1 | ||
- | </code> | ||
- | </hidden> | ||
- | Apoi, după o funcție dată ca input (argumentul **funct**), fiecărui număr i se va acorda o culoare. Deasupra diagonalei (unde sunt zero-uri) va rămâne negru și nu se va aplica funcția! | ||
- | <note important>Acest triunghi fiind infinit, se va limita la $ size^2 $ (size > 2).</note> | ||
- | |||
- | Se va completa pentru asta funcția | ||
- | <code scala> | ||
- | def moduloPascal(m: Integer, funct: Integer => Pixel, size: Integer): Image = ??? | ||
- | </code> |