Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
fp:lab07 [2021/04/22 13:01] lfa |
fp:lab07 [2022/05/03 17:25] (current) pdmatei |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== 7. Working with matrices and images using higher-order functions ====== | + | ====== L07. For expressions ====== |
| - | ===== 7.1. Bitmaps as matrices ===== | + | In this lab, we will use matrices to encode Bitmap images. The format is called BPM, and more details are available [[https://en.wikipedia.org/wiki/Netpbm#File_formats|here]]. Our format will be grayscale only. Each pixel of the matrix is encoded as an integer, with values from 0 to 255. Some examples are shown below: |
| - | We can represent images as matrices of pixels. In our example a pixel will be represented as a ''Char'', which can take the values: '*' (signifying that the respective pixel is //on//) and ' ' (the pixel is off). | ||
| - | |||
| - | <code haskell> type Image = [String] </code> | ||
| - | |||
| - | For instance, a rectangle could be represented as: | ||
| <code> | <code> | ||
| - | ******** | + | 0 0 1 0 0 |
| - | * * | + | 0 1 0 1 0 |
| - | ******** | + | 0 1 1 1 0 letter A |
| + | 1 0 0 0 1 | ||
| + | 1 0 0 0 1 | ||
| + | |||
| + | 5 4 3 2 1 | ||
| + | 5 4 3 2 1 | ||
| + | 5 4 3 2 1 shader | ||
| + | 5 4 3 2 1 | ||
| + | 5 4 3 2 1 | ||
| </code> | </code> | ||
| - | more precisely, as the string: | + | Add the following type definition for the rest of your lab: |
| - | <code haskell> | + | <code scala> |
| - | m = "********\n* *\n********\n" | + | type Img = List[List[Int]] |
| </code> | </code> | ||
| - | 7.1.1. Implement an image-displaying function. Use the image from the Appendix as a test. | + | Also, in order to benefit from visualisation, instead of using a worksheet, you can create a new Scala project, containing an object with a main method: |
| - | <code haskell> | + | <code scala> |
| - | toStringImg :: Image -> String | + | object Matrix { |
| - | toStringImg = | + | // define your functions here |
| - | </code> | + | def main(args: Array[String]) = { |
| + | // write your tests here | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| - | Add the following function in order to view images in a nicer format: | + | **7.1.** Write a function which converts an image to a string (Hint: you can draw inspiration from a similar one from the lecture): |
| - | <code haskell> | + | <code scala> |
| - | displayImg = putStrLn . toStringImg | + | def show(m: Img): String = ??? |
| </code> | </code> | ||
| - | 7.1.2. Implement a function which flips an image horizontally: | + | **7.2.** Write a function which performs a horizontal flip on an image. Try to first visualise (you may use pen and paper) how the transformation would look like. |
| - | <code haskell> | + | <code scala> |
| - | flipH :: Image -> Image | + | def hFlip(img: Img): Img = ??? |
| - | flipH = | + | |
| </code> | </code> | ||
| - | 7.1.3. Implement a function which flips an image vertically: | + | **7.3.** Write a function which performs vertical flip. |
| - | <code haskell> | + | <code scala> |
| - | flipV :: Image -> Image | + | def vFlip(img: Img): Img = ??? |
| - | flipV = | + | </code> |
| - | </code> | + | |
| - | 7.1.4. Implement a function which rotates an image 90grd clockwise | + | **7.4.** Write a function which performs a 90 degrees rotation to the right. (Hint: you need an ingredient from the lecture. Also, note that there are multiple possible implementations.) |
| - | <code haskell> | + | <code scala> |
| - | rotate90r :: Image -> Image | + | def rot90Right(img: Img): Img = ??? |
| - | rotate90r = | + | |
| </code> | </code> | ||
| - | 7.1.5. Implement a function which rotates an image -90grd clockwise | + | **7.5.** Write a function which performs a 90 degrees rotation to the left. |
| - | <code haskell> | + | <code scala> |
| - | rotate90l :: Image -> Image | + | def rot90Left(img: Img): Img = ??? |
| - | rotate90l = | + | |
| </code> | </code> | ||
| - | 7.1.6. Implement a function which inverts an image: ' ' becomes * and * becomes ' ': | + | **7.6.** Write a function which inverts an image (values 0 become 255, 1 - 254, and so forth). |
| - | <code haskell> | + | |
| - | invert :: Image -> Image | + | |
| - | invert = | + | |
| - | </code> | + | |
| - | ===== 7.2. Combining images ===== | + | **7.7.** Write a function which crops a given image, using two, two-dimensional coordinates: the higher-left point x and y, and the lower-right point x and y. An example is shown below: |
| + | <code scala> | ||
| + | val img = List(List(0,0,1,0,0), List(0,1,0,1,0), List(0,1,1,1,0), List(1,0,0,0,1), List(1,0,0,0,1)) | ||
| + | /* | ||
| + | 0 0 1 0 0 | ||
| + | * 0 1 0 1 0 1 0 1 | ||
| + | * 0 1 1 1 0 cropping from 1,1 to 2,3 yields: 1 1 1 | ||
| + | * 1 0 0 0 1 | ||
| + | * 1 0 0 0 1 | ||
| - | 7.2.1. Implement a function ''maskKeep'' which takes a mask and some image and only keeps the part of image which overlays the mask. Use the mask and the logo from Appendix to test it. | + | */ |
| - | <code haskell> | + | |
| - | maskKeep :: Image -> Image -> Image | + | |
| - | maskKeep = | + | |
| - | </code> | + | |
| - | $math[ maskKeep \left(\begin{array}{ccc} & & * \\ & & * \\ & & * \\ \end{array}\right) \left(\begin{array}{ccc} * & & * \\ * & & \\ * & & * \\ \end{array}\right) = \left(\begin{array}{ccc} & & * \\ & & \\ & & * \\ \end{array}\right) ] | + | |
| - | 7.2.2. Implement a function ''maskDiscard'' which takes a mask and some image and discards the part of the mask which overlays the image. | + | def cropAt(img: Img, xSt:Int, ySt:Int, xEnd: Int, yEnd: Int): Img = ?? |
| + | </code> | ||
| - | $math[ maskDiscard\left(\begin{array}{ccc} & & * \\ & & * \\ & & * \\ \end{array}\right) \left(\begin{array}{ccc} * & & * \\ * & & \\ * & & * \\ \end{array}\right) = \left(\begin{array}{ccc} & & \\ & & * \\ & & \\ \end{array}\right) ] | + | **7.8.** Write a function which returns a list of all positions which have pixels of larger intensity than x |
| + | <code scala> | ||
| + | def largerPos(img: Img, int: Int): List[(Int,Int)] = ??? | ||
| + | </code> | ||
| - | 7.2.3. Implement the ''union''' of two images. | + | **7.9.** Write a function which adds ''x'' to the intensity of each pixel. |
| - | <code haskell> | + | <code scala> |
| - | unionp :: Image -> Image -> Image | + | def contrast(x: Int)(img: Img): Img = ??? |
| - | unionp = | + | |
| </code> | </code> | ||
| - | $math[ union \left(\begin{array}{ccc} & & * \\ & & * \\ & & * \\ \end{array}\right) \left(\begin{array}{ccc} * & & * \\ * & & \\ * & & * \\ \end{array}\right) = \left(\begin{array}{ccc} * & & * \\ * & & * \\ * & & * \\ \end{array}\right) ] | ||
| - | 7.2.4. Implement a function which takes a list of transformations and an image and applies all those transformations. | + | **7.10.** Write a function which takes two images ''X'' and ''Y'' and //glues// them on the horizontal axis (the resulting image will be ''XY'') |
| - | <code haskell> | + | <code scala> |
| - | transformationSequence :: [Image -> Image] -> Image -> Image | + | def hglue(img1: Img, img2: Img): Img = ??? |
| </code> | </code> | ||
| - | Test it with the following sequence | ||
| - | <code haskell> | ||
| - | seq1 = transformationSequence [invert, unionp mask, rotate90r] | ||
| - | </code> | ||
| - | |||
| - | ===== 7.3 Number Matrices ===== | ||
| - | |||
| - | 7.3.1. Write a function that computes the scalar product with an integer: | ||
| - | |||
| - | $math[ \displaystyle 2 * \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right) = \left(\begin{array}{ccc} 2 & 4 & 6 \\ 8 & 10 & 12 \\ 14 & 16 & 18 \\ \end{array}\right)] | ||
| - | <code haskell> | + | **7.11.** Write a function which takes two images ''X'' and ''Y'' and //glues// them on the vertical axis: |
| - | vprod :: Integer -> Matrix -> Matrix | + | <code scala> |
| - | vprod v = | + | def vglue(img1: Img, img2: Img): Img = ??? |
| </code> | </code> | ||
| - | 7.3.2. Write a function which adjoins two matrices by extending rows: | + | **7.12.** Define a function that takes a **square** image, and draws two diagonal lines of intensity 1 across it. Use ''_.until(_)'' and ''_.toList''. |
| - | + | <code scala> | |
| - | $math[ \displaystyle \left(\begin{array}{cc} 1 & 2 \\ 3 & 4\\\end{array}\right) hjoin \left(\begin{array}{cc} 5 & 6 \\ 7 & 8\\\end{array}\right) = \left(\begin{array}{cc} 1 & 2 & 5 & 6 \\ 3 & 4 & 7 & 8\\\end{array}\right) ] | + | def diag(img: Img): Img = ??? |
| - | + | ||
| - | <code haskell> | + | |
| - | hjoin :: Matrix -> Matrix -> Matrix | + | |
| - | hjoin = | + | |
| </code> | </code> | ||
| - | 7.3.3. Write a function which adjoins two matrices by adding new rows: | + | **7.13.** Define a function which blurs an image as follows: |
| + | * for each pixel p of intensity > 1: make all neighbouring pixels of intensity 0 equal to p-1 (including diagonals). | ||
| + | * if some pixel of intensity 0 is in the vicinity of two different >1 pixels of different intensities, the one which is larger should be taken into account | ||
| + | * to make the implementation easier, you do not need to implement the blur on the image edges. | ||
| - | $math[ \displaystyle \left(\begin{array}{cc} 1 & 2 \\ 3 & 4\\\end{array}\right) vjoin \left(\begin{array}{cc} 5 & 6 \\ 7 & 8\\\end{array}\right) = \left(\begin{array}{cc} 1 & 2 \\ 3 & 4 \\ 5 & 6\\ 7 & 8\\ \end{array}\right) ] | + | <code scala> |
| + | def blur(img: Img): Img = ??? | ||
| + | </code> | ||
| - | <code haskell> | + | **7.14. (!) ** Define a function which builds an effect of intensity x as shown below: |
| - | vjoin :: Matrix -> Matrix -> Matrix | + | <code scala> |
| - | vjoin = | + | val img2 = List( |
| - | </code> | + | List(0,0,0,0,0,0,0,0,0), |
| + | List(0,0,0,0,0,0,0,0,0), | ||
| + | List(0,0,0,0,0,0,0,0,0), | ||
| + | List(0,0,0,0,1,0,0,0,0), | ||
| + | List(0,0,0,1,2,1,0,0,0), | ||
| + | List(0,0,0,0,1,0,0,0,0), | ||
| + | List(0,0,0,0,0,0,0,0,0), | ||
| + | List(0,0,0,0,0,0,0,0,0), | ||
| + | List(0,0,0,0,0,0,0,0,0) | ||
| + | ) | ||
| - | 7.3.4. Write a function which adds two matrices. | + | /* |
| + | Before: After (for x = 2) | ||
| - | <code haskell> | + | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 |
| - | msum :: Matrix -> Matrix -> Matrix | + | 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 |
| - | msum = | + | 0 0 0 0 0 0 0 0 0 0 1 1 2 2 2 1 1 0 |
| - | </code> | + | 0 0 0 0 1 0 0 0 0 0 1 2 2 3 2 2 1 0 |
| + | 0 0 0 1 2 1 0 0 0 0 1 2 3 4 3 2 1 0 | ||
| + | 0 0 0 0 1 0 0 0 0 0 1 2 2 3 2 2 1 0 | ||
| + | 0 0 0 0 0 0 0 0 0 0 1 1 2 2 2 1 0 0 | ||
| + | 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 | ||
| + | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 | ||
| - | ===== Appendix ===== | ||
| - | |||
| - | <code haskell> | ||
| - | logo = [l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15,l16,l17,l18,l19] | ||
| - | where l1 =" ***** ** ***** ** " | ||
| - | l2 =" ****** **** ****** **** " | ||
| - | l3 =" ** * * *** ** * * *** " | ||
| - | l4 =" * * * *** * * * *** " | ||
| - | l5 =" * * ** * * ** " | ||
| - | l6 =" ** ** ** ** ** ** " | ||
| - | l7 =" ** ** ** ** ** ** " | ||
| - | l8 =" **** ** * **** ** * " | ||
| - | l9 =" * *** ** * * *** ** * " | ||
| - | l10=" ** ******* ** ******* " | ||
| - | l11=" ** ****** ** ****** " | ||
| - | l12=" ** ** ** ** " | ||
| - | l13=" ** ** ** ** " | ||
| - | l14=" ** ** ** ** " | ||
| - | l15=" ** ** ** ** ** ** " | ||
| - | l16="*** * * *** * * " | ||
| - | l17=" *** * *** * " | ||
| - | l18=" ****** ****** " | ||
| - | l19=" *** *** " | ||
| - | </code> | ||
| - | |||
| - | <code haskell> | ||
| - | mask = [l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11,l12,l13,l14,l15,l16,l17,l18,l19] | ||
| - | where l1 =" *****************" | ||
| - | l2 =" *****************" | ||
| - | l3 =" *****************" | ||
| - | l4 =" *****************" | ||
| - | l5 =" *****************" | ||
| - | l6 =" *****************" | ||
| - | l7 =" *****************" | ||
| - | l8 =" *****************" | ||
| - | l9 =" *****************" | ||
| - | l10=" *****************" | ||
| - | l11=" *****************" | ||
| - | l12=" *****************" | ||
| - | l13=" *****************" | ||
| - | l14=" *****************" | ||
| - | l15=" *****************" | ||
| - | l16=" *****************" | ||
| - | l17=" *****************" | ||
| - | l18=" *****************" | ||
| - | l19=" *****************" | ||
| - | </code> | ||
| + | Hint: a single foldRight is sufficient. Which is the list you should apply it on? | ||
| + | */ | ||
| + | def effect(intensity: Int, img: Img): Img = ??? | ||
| + | </code> | ||