Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
fp:assig-01 [2021/04/26 11:01] pdmatei |
fp:assig-01 [2021/05/06 17:32] (current) dmihai [Submission] |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ==== Introduction ==== | + | **Deadline: May 7th, 23:55** |
+ | |||
+ | ===== Introduction ===== | ||
For this assignment, we will work with images and implement some basic image manipulations. | For this assignment, we will work with images and implement some basic image manipulations. | ||
We will work with two types of images: greyscale and RGB. | We will work with two types of images: greyscale and RGB. | ||
- | === Image representation === | ||
+ | === Image representation === | ||
Internally, images are modeled as two-dimensional matrices of pixels; in Haskell we will model this by using lists of lists. | Internally, images are modeled as two-dimensional matrices of pixels; in Haskell we will model this by using lists of lists. | ||
- | * A greyscale image is a ''[%%[%%Int]%%]%%''. Each ''Int'' is a value in the range ''0-255'' and represents the brightness of a pixel: 0 is pure black, 255 is pure white. | + | * A **greyscale** image is a ''[%%[%%Int]%%]%%''. Each ''Int'' is a value in the range ''0-255'' and represents the brightness of a pixel: 0 is pure black, 255 is pure white. |
- | * An RGB image is a ''[%%[%%(Int, Int, Int)]]''. Each ''(Int, Int, Int)'' contains three values in the range ''0-255'', with the intensity for that pixel's primary colors: Red, Green and Blue. | + | * An **RGB** image is a ''[%%[%%(Int, Int, Int)]]''. Each ''(Int, Int, Int)'' contains three values in the range ''0-255'', with the intensity for that pixel's primary colors: Red, Green and Blue. |
- | ==== Tasks ==== | + | ===== Tasks (total 1p) ===== |
- | For each of the two formats (greyscale and RGB), you are asked to implement the | + | For these two formats (greyscale and RGB) you are asked to implement some transformations. The next section lists common transformations (that should not depend on whether the pixel is a grayscale value or a RGB triplet); the section after that describes transformations that should have separate implementations, one for each format. |
- | following image transformations: | + | |
- | * **vertical flip** | + | ===== 1. Common transformations (0.2p) ===== |
- | * **horizontal flip** | + | 1.1. **vertical flip** |
- | * **90 degree left-rotation** | + | 1.2. **horizontal flip** |
- | * **90 degree right-rotation** | + | 1.3. **90 degree left-rotation** |
- | * **180 degree left-rotation** | + | 1.4. **90 degree right-rotation** |
- | * **180 degree right-rotation** | + | 1.5. **180 degree left-rotation** |
- | * **color inversion** | + | 1.6. **180 degree right-rotation** |
- | * you will have to flip a color value to the other side of the 0-255 interval | + | |
- | * **cropping with a rectangle selection** | + | ===== 2. Type-specific transformations (0.8p) ===== |
- | * your function should take a rectangle modelled by a ''(Int, Int, Int, Int)'' tuple (the first two ''Int''s are the coordinates of the top-right corner, the next two ''Int''s are the height and width of the rectangle). It should return a new image with the same height and width as the rectangle selection containing the pixels in the image which fall withing the rectangle's area. | + | |
- | * **brightness adjustment** | ||
- | * your function should take an adjustment value (an ''Int'') with which to increase the value of each color (remember that 0 is black and 255 is the pure color, either white for greyscale, or R/G/B for color images). | ||
- | * **masking with a bitmap mask** | + | 2.1. **color inversion** |
- | * your function should take a mask represented as a two-dimensional bitmap with the same size as the image (a ''[%%[%%Bool]%%]%%''). The resulting image should look like this: if the mask has ''False'' at position ''(x, y)'' the pixel at position ''(x, y)'' is black; otherwise it retains its original value. | + | * you will have to flip a color value to the other side of the 0-255 interval |
+ | |||
+ | 2.2. **cropping with a rectangle selection** | ||
+ | * your function should take a rectangle modelled by a ''(Int, Int, Int, Int)'' tuple (the first two ''Int''s are the coordinates of the top-left corner, the next two ''Int''s are the height and width of the rectangle). It should return a new image with the same height and width as the rectangle selection containing the pixels in the image which fall withing the rectangle's area. | ||
- | * **superimposing another picture with a transparency factor** | + | 2.3. **brightness adjustment** |
- | * your function will take an additional image and a factor ''alpha'' that affects the transparency of this second image. The result should be an image whose pixel values are a linear combination of the two input images and the factor ''alpha''. For example, if ''alpha'' is 0, the result is identical to the first image; if ''alpha'' is 1, the result is identical to the second one. | + | * your function should take an adjustment value (an ''Int'') with which to increase the value of each color (remember that 0 is black and 255 is the pure color, either white for greyscale, or R/G/B for color images). |
- | * **scaling with a horizontal and a vertical factor** | + | 2.4. **masking with a bitmap mask** |
- | * your function will take two scaling factors (a ''(Float, Float)''), for vertical and horizontal scaling. For upscaling (factors larger than 1), you should duplicate lines (vertical) or columns (horizontal). For downscaling (factors lesser than 1), you should remove every $n^{th}$ line/column, where $n$ is $1 / scaling\_factor$. | + | * your function should take a mask represented as a two-dimensional bitmap with the same size as the image (a ''[%%[%%Bool]%%]%%''). The resulting image should look like this: if the mask has ''False'' at position ''(x, y)'' the pixel at position ''(x, y)'' is black; otherwise it retains its original value. |
- | * **color swapping** | + | 2.5. **color swapping** |
* //this transformation is relevant only for RGB images//. Your function should swap the colors for each pixel (the exact permutations is your choice). | * //this transformation is relevant only for RGB images//. Your function should swap the colors for each pixel (the exact permutations is your choice). | ||
- | * **conversion to greyscale** | + | 2.6. **conversion to greyscale** |
* //this transformation is relevant only for RGB images.//. Your function should convert an RGB image (''[%%[%%(Int, Int, Int)]%%]%%'') to a greyscale one (''[%%[%%Int]''). For each pixel, you can set the grey intensity to the average of its three colors. | * //this transformation is relevant only for RGB images.//. Your function should convert an RGB image (''[%%[%%(Int, Int, Int)]%%]%%'') to a greyscale one (''[%%[%%Int]''). For each pixel, you can set the grey intensity to the average of its three colors. | ||
- | |||
For all the tasks listed above you **do not** need to implement any sanity check on the inputs; you can safely assume all inputs are valid. | For all the tasks listed above you **do not** need to implement any sanity check on the inputs; you can safely assume all inputs are valid. | ||
For example, you can assume that all lists of lists you work with are valid two-dimensional matrices (no line is longer or shorter than the others); all color values are between 0 and 255; for cropping, the rectangle described will always fall within the bounds of the image; for masking, the mask will always be the same size as the image; etc. | For example, you can assume that all lists of lists you work with are valid two-dimensional matrices (no line is longer or shorter than the others); all color values are between 0 and 255; for cropping, the rectangle described will always fall within the bounds of the image; for masking, the mask will always be the same size as the image; etc. | ||
+ | |||
+ | You can download the starting code {{:fp:helpercode.zip|here}}. Unpack the archive and look in the folder ''skel/''. Write the implementations in the files ''Common.hs'', ''Greyscale.hs'' and ''Color.hs''; the function types are already provided. You can ignore all the other haskell source files in that folder, they are only needed if you intend to test it on real ''Netpbm'' images. | ||
<note tip> | <note tip> | ||
Line 64: | Line 65: | ||
Our focus here is simply on how to program in a functional style, so we don't really aim for performance and smooth results. | Our focus here is simply on how to program in a functional style, so we don't really aim for performance and smooth results. | ||
- | For example, the scaling algorithm presented (a form of [[https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation|nearest-neighbor]] interpolation}) is easy to write, but its results are poor. | + | For example, the scaling algorithm presented (a form of [[https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation|nearest-neighbor interpolation]]) is easy to write, but its results are poor. |
You can see [[https://en.wikipedia.org/wiki/Comparison_gallery_of_image_scaling_algorithms|here]] a quality comparison of various algorithms. | You can see [[https://en.wikipedia.org/wiki/Comparison_gallery_of_image_scaling_algorithms|here]] a quality comparison of various algorithms. | ||
Line 73: | Line 74: | ||
For very basic testing, you can define your own test-cases. However, to give you a big-picture view and some satisfying results, we offer you a testing framework for [[https://en.wikipedia.org/wiki/Netpbm#File_formats|Netpbm]] ASCII-format images. | For very basic testing, you can define your own test-cases. However, to give you a big-picture view and some satisfying results, we offer you a testing framework for [[https://en.wikipedia.org/wiki/Netpbm#File_formats|Netpbm]] ASCII-format images. | ||
- | Netpbm a simplistic pixel-map image format, capable of modelling both greyscale and RGB pictures. | + | Netpbm is a simplistic pixel-map image format, capable of modelling both greyscale and RGB pictures. |
A Netpbm image file has the following structure: | A Netpbm image file has the following structure: | ||
Line 88: | Line 89: | ||
We provide you with a few Netpbm samples, but you can create your own images. | We provide you with a few Netpbm samples, but you can create your own images. | ||
- | Most image editors (e.g. Photoshop, GIMP) should be able to convert an arbitrary image to a Netpbm format; when asked about ``data formatting'' (or similar terms), select "ASCII", not "raw". | + | Most image editors (e.g. Photoshop, GIMP) should be able to convert an arbitrary image to a Netpbm format; when asked about "data formatting" (or similar terms), select "ASCII", not "raw". |
A warning: for various reasons the resulting code is quite inefficient, so it will take a few seconds to process even small images. The bigger the image, the longer the processing time. | A warning: for various reasons the resulting code is quite inefficient, so it will take a few seconds to process even small images. The bigger the image, the longer the processing time. | ||
- | ==== Examples ==== | ||
- | |||
- | TODO | ||
==== Usage ==== | ==== Usage ==== | ||
- | TODO | + | To run the provided helper code and test your functionality, you first need to select which function you want to test out. For that, you need to edit one or two lines in the file ''Main.hs''. |
+ | |||
+ | * for single image transformations (i.e. all except masking), edit line 13. The following example will configure it with a crop of a 50x50 square that starts at coordinates (30, 30): | ||
+ | * <code>myTransform img = crop (30, 30, 80, 80) img</code> | ||
+ | * for transformations that require two images (just masking), line 16 is relevant; but you can leave it as it is, as there's only one transformation candidate. | ||
+ | |||
+ | After having edited ''Main.hs'' you can run it; depending on the number of arguments, it will choose an appropriate type of transformation (single/multiple image); it also correctly chooses between greyscale/RGB functions based on the actual filetype. To run the file you need the ''runhaskell'' binary (or ''runghc''; both should come with the haskell platform), then run it from a shell: | ||
+ | |||
+ | <code> | ||
+ | # Runs the single-image transformation set on line 13 on the image in file "./input.ppm"; the result is written to "./out.ppm". | ||
+ | runhaskell Main.sh ./input.ppm ./out.ppm | ||
+ | </code> | ||
+ | |||
+ | <code> | ||
+ | # Runs the mask transformation set on line 16 on the image in file "./input.ppm" with the mask in "./mask.pbm"; the result is written to "./out.ppm". | ||
+ | runhaskell Main.hs ./image.ppm ./mask.pbm ./out.ppm | ||
+ | </code> | ||
+ | |||
+ | The folder ''res/'' contains several greyscale and RGB images, as well as two masks. The masks are also in a ''Netpbm'' format; you can view any of these images with a regular image-viewer. | ||
==== Scoring ==== | ==== Scoring ==== | ||
- | TODO | + | The assignment is worth 1 point; 0.2p for the common transformations; 0.4p for greyscale and 0.4p for RGB. |
- | ==== Submission ==== | ||
- | TODO | + | ==== Submission ==== |
+ | For submission you should create a zip archive of the three files you modified: ''Common.hs'', ''Greyscale.hs'' and ''Color.hs'' and a ''README''. The ''README'' should contain a succinct high-level presentation of your work. The archive name should be ''LASTNAME_Firstname_FP_A1.zip'' (if you have multiple firstnames, separate them by an underline "_"). Mail it to [[mailto:mihai.dumitru2201@upb.ro|mihai.dumitru2201@upb.ro]]. | ||