Exploring lists of colors

Assignment overview

Although you’ve only just learned about lists, in fact, you’ve been using them all year! Why? Under the hood, an image is a list of colors.

In this assignment, you will learn to manipulate images by performing operations on the lists of colors that describe them. In the process, you’ll write Instagram-esque image filters and cropping functions.

Submission instructions

  1. Open a new window in DrRacket. Use the “Save definitions as…” menu option to save the file as yourname‑image‑filters.rkt.

  2. In your file, write the functions described in the seven required problems below. Each function (including any helper functions you write) should have a contract and—if not immediately clear from the function name—a purpose statement.

  3. When you’re finished, upload your Racket file to Google Classroom.

Two important functions

Racket comes with two built-in functions that help us convert images to lists of colors and vice versa:

1
image->color-list : Image -> (Listof Color)

This function takes in an image and produces a list of the colors, representing the colors of each pixel in the image. The list is ordered so that the top left pixel color comes first, then the rest of the pixels in the top row, and then the second row starts, and so on.

To make sure you understand: what do you think

1
(length (image‑>color‑list (square 10 "solid" "green")))

will evaluate to? (The length function determines the length of a list.) Check whether you were right by plugging into DrRacket.

Here is our second important function:

1
color-list->bitmap : (Listof Color) Number Number -> Image

This function takes in a color list, and a width and height, and returns an image created out of pixels of the colors in the list.

Extra Challenge: From list to image

Can you write your own version of color‑list‑>bitmap? You can use a square of side length 1 to create a single pixel. If you attempt this problem, name the function color‑list‑>image. It should take the same arguments as color‑list‑>bitmap does, and produce the same result. You may find it helpful (or even necessary) to write helper functions.

Image filters

Racket comes with a built-in struct called color:

1
2
(define-struct color [red green blue])
; you don’t need to define this yourself, as it’s already defined.

The three fields, red, green, and blue, are numbers between 0 and 255, inclusive. They represent the brightness of red, green, and blue components inside a color. For example, white is the color you get when all three components are at maximum brightness: (make‑color 255 255 255). A bright red color is achieved by setting the red component to 255 and the other two to 0: (make‑color 255 0 0). Lowering that 255 to, say, 100, gives a darker red color.

In the following problems, you will be writing image filters, functions that take in an image and change it in some way. They will do this by changing the image into a list of colors, changing each color in some way, and then reassembling the list into an image again.

1Image filter: Recolorize

1
recolorize : Image -> Image

Write a function recolorize that changes an image as follows. Each pixel in the original image is replaced with a pixel whose green component is equal to the original pixel’s red component, whose blue component is equal to the original pixel’s green component, and whose red component is equal to the original pixel’s blue component.

2Image filter: Brighten

1
brighten : Image -> Image

Write a function brighten that brightens an image. Add some number (something between 20 to 80 is a good bet) to every component (red, green, and blue) of every pixel color in the image. Make sure, however, that the components never exceed 255: this will cause an error.

3Image filter: Grayscale

1
grayscale : Image -> Image

A grayscale color is a color whose red, green, and blue values are equal, and therefore looks gray. Write a function grayscale that turns an image into a grayscale version of that image. To do this, transform each color of the image into a grayscale color, whose brightness is based on the overall brightness of the original pixel (average of red, green, and blue brightnesses).

4Image filter: Sepia

1
sepia : Image -> Image

A sepia filter can make an image look old-timey. To implement this filter, change each pixel of the image according to the following equations:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
newRed   = (oldRed   * .393)
         + (oldGreen * .769)
         + (oldBlue  * .189)

newGreen = (oldRed   * .349)
         + (oldGreen * .686)
         + (oldBlue  * .168)

newBlue  = (oldRed   * .272)
         + (oldGreen * .534)
         + (oldBlue  * .131)

5Image filter: Green screen

1
green-screen : Image Image -> Image

This one is a little different. Write a function green-screen that accepts two images, of identical size, as arguments. The first is the “green screen” image, an image taken in front of a green screen, and so it should contain a number of pixels that are very close to (make-color 0 255 0). The second image is a “backdrop.”

Your function should construct a new image, where each pixel is equal to the corresponding pixel in the green screen image, unless the pixel is very close to green (red and blue components less than 100, green component above 155), in which case you should use the pixel from the backdrop image. (You may need to play around with the green detection function for some images, changing the tolerance from 100 to, say, 130.)

Extra Challenge: Blur

Can you create a blur filter? One way to do this is to set each pixel to the average of the ones around it (i.e., all eight neighboring pixels).

Cropping

Cropping an image means deleting some of its rows and columns so that only a portion of the image remains. The last two questions ask you to write functions for cropping an image.

6Crop off the top

1
crop-top : Image Number -> Image

Write a function crop-top that takes in an image and a number n, and returns the same image but with the top n rows cut off.

7Top and bottom

1
crop-vertical : Image Number Number -> Image

Write a function crop-vertical that takes in an image and two numbers, from-top and from-bottom, and returns the image with the top from-top rows and bottom from-bottom rows chopped off. You may find it helpful to call crop-top and to write a helper crop-bottom function.

Extra Challenge: Horizontal cropping

Instead of cropping rows from the top and bottom, write functions that crop columns from the left and right.