Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
|
fp:lab07 [2021/04/22 12:45] pdmatei created |
fp:lab07 [2022/05/03 17:25] (current) pdmatei |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== 4. Working with matrices and images using higher-order functions ====== | + | ====== L07. For expressions ====== |
| - | ===== 1. Introduction ===== | + | 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 shall represent matrices as //lists of lists//, i.e. values of type ''[ [Integer ] ]''. Each element in the outer list represents a line of the matrix. | + | <code> |
| - | Hence, the matrix | + | 0 0 1 0 0 |
| - | + | 0 1 0 1 0 | |
| - | $math[ \displaystyle \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right)] | + | 0 1 1 1 0 letter A |
| - | + | 1 0 0 0 1 | |
| - | will be represented by the list ''[ [1,2,3],[4,5,6],[7,8,9] ]''. | + | 1 0 0 0 1 |
| - | + | ||
| - | To make signatures more legible, add the //type alias// to your code: | + | 5 4 3 2 1 |
| - | <code haskell> type Matrix = [[Integer]] </code> | + | 5 4 3 2 1 |
| - | which makes the type-name ''Matrix'' stand for ''[ [Integer] ]''. | + | 5 4 3 2 1 shader |
| - | + | 5 4 3 2 1 | |
| - | 1.1. Write a function ''parsem'' which takes a string and parses it to a ''Matrix''. In the string, the columns are separated by whitespace and lines - by '\n'. | + | 5 4 3 2 1 |
| - | * (Hint: You need to perform two types of very similar splitting operations: one into lines, and another into column values, for each line) | + | |
| - | + | ||
| - | <code haskell> | + | |
| - | parsem :: String -> Matrix | + | |
| - | parsem = | + | |
| </code> | </code> | ||
| - | 1.2. Write a function that converts a matrix to a string encoded as illustrated in the previous exercise. | + | Add the following type definition for the rest of your lab: |
| - | * (Hint: use folds) | + | <code scala> |
| - | * (Hint: test the function ''show'' on several different values) | + | type Img = List[List[Int]] |
| - | * (Hint: first make a function to display one line) | + | |
| - | + | ||
| - | <code haskell> | + | |
| - | toString:: Matrix -> String | + | |
| - | toString= | + | |
| </code> | </code> | ||
| - | 1.3. Add the following to your code. Test the function ''displayMatrix''. | + | 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> |
| - | displayMatrix = putStrLn . toString | + | object Matrix { |
| - | </code> | + | // define your functions here |
| - | + | ||
| - | ===== 2. Matrix operations ===== | + | |
| - | + | ||
| - | 2.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)] | + | def main(args: Array[String]) = { |
| + | // write your tests here | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| - | <code haskell> | + | **7.1.** Write a function which converts an image to a string (Hint: you can draw inspiration from a similar one from the lecture): |
| - | vprod :: Integer -> Matrix -> Matrix | + | <code scala> |
| - | vprod v = | + | def show(m: Img): String = ??? |
| </code> | </code> | ||
| - | 2.2. Write a function which adjoins two matrices by extending rows: | + | **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 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 hFlip(img: Img): Img = ??? |
| - | + | ||
| - | <code haskell> | + | |
| - | hjoin :: Matrix -> Matrix -> Matrix | + | |
| - | hjoin = | + | |
| </code> | </code> | ||
| - | 2.3. Write a function which adjoins two matrices by adding new rows: | + | **7.3.** Write a function which performs vertical flip. |
| + | <code scala> | ||
| + | def vFlip(img: Img): Img = ??? | ||
| + | </code> | ||
| - | $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) ] | + | **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 scala> | |
| - | <code haskell> | + | def rot90Right(img: Img): Img = ??? |
| - | vjoin :: Matrix -> Matrix -> Matrix | + | |
| - | vjoin = | + | |
| </code> | </code> | ||
| - | 2.4. Write a function which adds two matrices. | + | **7.5.** Write a function which performs a 90 degrees rotation to the left. |
| - | + | <code scala> | |
| - | <code haskell> | + | def rot90Left(img: Img): Img = ??? |
| - | msum :: Matrix -> Matrix -> Matrix | + | |
| - | msum = | + | |
| </code> | </code> | ||
| - | 2.5. Write a function which computes the transposition of a matrix: | + | **7.6.** Write a function which inverts an image (values 0 become 255, 1 - 254, and so forth). |
| - | $math[ tr \left(\begin{array}{ccc} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \\ \end{array}\right) = \left(\begin{array}{ccc} 1 & 4 & 7 \\ 2 & 5 & 8 \\ 3 & 6 & 9 \\ \end{array}\right) ] | + | **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 | ||
| - | <code haskell> | + | */ |
| - | tr :: [[a]] -> [[a]] | + | |
| - | tr ([]:_) = | + | |
| - | tr m = | + | |
| - | </code> | + | |
| - | 2.6. Write a function which computes the vectorial product of two matrices. | + | def cropAt(img: Img, xSt:Int, ySt:Int, xEnd: Int, yEnd: Int): Img = ?? |
| - | * (Hint: start by writing a function which computes $math[a_{ij}] for a given line $math[i] and column $math[j] (both represented as lists)) | + | </code> |
| - | * (Hint: write a function which takes a line of matrix m1 and the matrix m2 and computes the respective line from the product) | + | |
| - | <code haskell> | + | **7.8.** Write a function which returns a list of all positions which have pixels of larger intensity than x |
| - | mprod :: Matrix -> Matrix -> Matrix | + | <code scala> |
| - | mprod m1 m2 = | + | def largerPos(img: Img, int: Int): List[(Int,Int)] = ??? |
| </code> | </code> | ||
| - | ===== 3. Image operations ===== | + | **7.9.** Write a function which adds ''x'' to the intensity of each pixel. |
| - | + | <code scala> | |
| - | 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). | + | def contrast(x: Int)(img: Img): Img = ??? |
| - | + | ||
| - | <code haskell> type Image = [String] </code> | + | |
| - | + | ||
| - | For instance, a rectangle could be represented as: | + | |
| - | + | ||
| - | <code> | + | |
| - | ******** | + | |
| - | * * | + | |
| - | ******** | + | |
| </code> | </code> | ||
| - | more precisely, as the string: | + | **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> |
| - | "********\n* *\n********\n" | + | def hglue(img1: Img, img2: Img): Img = ??? |
| </code> | </code> | ||
| - | 3.1. Implement an image-displaying function. Use the image from the Appendix as a test. | + | **7.11.** Write a function which takes two images ''X'' and ''Y'' and //glues// them on the vertical axis: |
| - | <code haskell> | + | <code scala> |
| - | toStringImg :: Image -> String | + | def vglue(img1: Img, img2: Img): Img = ??? |
| - | toStringImg = | + | |
| </code> | </code> | ||
| - | Add the following function in order to view images in a nicer format: | + | **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 haskell> | + | <code scala> |
| - | displayImg = putStrLn . toStringImg | + | def diag(img: Img): Img = ??? |
| </code> | </code> | ||
| - | 3.2. Implement a function which flips an image horizontally: | + | **7.13.** Define a function which blurs an image as follows: |
| - | <code haskell> | + | * for each pixel p of intensity > 1: make all neighbouring pixels of intensity 0 equal to p-1 (including diagonals). |
| - | flipH :: Image -> Image | + | * 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 |
| - | flipH = | + | * to make the implementation easier, you do not need to implement the blur on the image edges. |
| - | </code> | + | |
| - | 3.3. Implement a function which flips an image vertically: | + | <code scala> |
| - | <code haskell> | + | def blur(img: Img): Img = ??? |
| - | flipV :: Image -> Image | + | </code> |
| - | flipV = | + | |
| - | </code> | + | |
| - | 3.4. Implement a function which rotates an image 90grd clockwise | + | **7.14. (!) ** Define a function which builds an effect of intensity x as shown below: |
| - | <code haskell> | + | <code scala> |
| - | rotate90r :: Image -> Image | + | val img2 = List( |
| - | rotate90r = | + | List(0,0,0,0,0,0,0,0,0), |
| - | </code> | + | List(0,0,0,0,0,0,0,0,0), |
| - | + | List(0,0,0,0,0,0,0,0,0), | |
| - | 3.5. Implement a function which rotates an image -90grd clockwise | + | List(0,0,0,0,1,0,0,0,0), |
| - | <code haskell> | + | List(0,0,0,1,2,1,0,0,0), |
| - | rotate90l :: Image -> Image | + | List(0,0,0,0,1,0,0,0,0), |
| - | rotate90l = | + | List(0,0,0,0,0,0,0,0,0), |
| - | </code> | + | List(0,0,0,0,0,0,0,0,0), |
| - | + | List(0,0,0,0,0,0,0,0,0) | |
| - | 3.6. Implement a function which inverts an image: ' ' becomes * and * becomes ' ': | + | ) |
| - | <code haskell> | + | |
| - | invert :: Image -> Image | + | |
| - | invert = | + | |
| - | </code> | + | |
| - | + | ||
| - | 3.7. 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) ] | + | |
| - | + | ||
| - | 3.8. Implement a function ''maskDiscard'' which takes a mask and some image and discards the part of the mask which overlays the image. | + | |
| - | + | ||
| - | $math[ maskDiscard\left(\begin{array}{ccc} & & * \\ & & * \\ & & * \\ \end{array}\right) \left(\begin{array}{ccc} * & & * \\ * & & \\ * & & * \\ \end{array}\right) = \left(\begin{array}{ccc} & & \\ & & * \\ & & \\ \end{array}\right) ] | + | |
| - | + | ||
| - | 3.9. Implement the ''union''' of two images. | + | |
| - | <code haskell> | + | |
| - | union' :: Image -> Image -> Image | + | |
| - | union' = | + | |
| - | </code> | + | |
| - | $math[ union \left(\begin{array}{ccc} & & * \\ & & * \\ & & * \\ \end{array}\right) \left(\begin{array}{ccc} * & & * \\ * & & \\ * & & * \\ \end{array}\right) = \left(\begin{array}{ccc} * & & * \\ * & & * \\ * & & * \\ \end{array}\right) ] | + | |
| - | + | ||
| - | 3.10. Implement a function which takes a list of transformation and an image and applies all those transformations. | + | |
| - | <code haskell> | + | |
| - | transformationSequence :: [Image -> Image] -> Image -> Image | + | |
| - | </code> | + | |
| - | Test it with the following sequence | + | |
| - | <code haskell> | + | |
| - | seq1 = transformationSequence [invert, union' mask, rotate90r] | + | |
| - | </code> | + | |
| - | + | ||
| - | ===== 4. More matrices ===== | + | |
| - | + | ||
| - | 4.1. Implement a function which places zeros **above** the diagonal of a square matrix. Use folds. | + | |
| - | + | ||
| - | 4.2. Implement a function which places zeros **below** the diagonal of a square matrix. Use folds. | + | |
| - | + | ||
| - | 4.3. Implement either of 4.1 or 4.2 using the function(s) ''take'' and ''drop''. Check the types of these and test them. | + | |
| - | + | ||
| - | 4.4. Implement a function which computes the **diagonal** matrix of a square matrix. | + | |
| - | + | ||
| - | 4.5. Implement a function which computes **recursively** the determinant of a matrix. What additional functions would be necessary? | + | |
| - | + | ||
| - | ===== 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> | + | |
| + | /* | ||
| + | Before: After (for x = 2) | ||
| + | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 1 1 2 2 2 1 1 0 | ||
| + | 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 | ||
| + | Hint: a single foldRight is sufficient. Which is the list you should apply it on? | ||
| + | */ | ||
| + | def effect(intensity: Int, img: Img): Img = ??? | ||
| + | </code> | ||