Lab: Image Processing

5 minute read

Preparation

These exercises should be completed with your assigned partner. Both of you will take turns playing the role of the driver (the one currently screen sharing) and the navigator. Please refer to the course Code of Conduct for more details.

Note: This lab we will be writing a lot of functions that manipulate images instead of entire programs. I recommend creating a new file called images_lab.py and putting all of your solutions to the following exercises in it.

Exercise 1

In this exercise we are going to play around with the color_rgb function to create colors. To help with this, here is a function:

def show_color(r, g, b):
    """Displays a window showcasing the given rgb color

    Parameters:
        r: an integer
        g: an integer
        b: an integer

    Returns:
        Nothing; called for the side effect

    Preconditions:
        r,g,b are integers between 0 and 255, inclusive

    Postconditions:
        A GraphWin is created that shows the given color
    """
    title = str(r) + "," + str(g) + "," + str(b)
    win = GraphWin("color_rgb(" + title + ")")
    win.setBackground(color_rgb(r,g,b))

a. Copy and paste the above function into your images_lab.py file. Also add an import statement from graphics import * so that it has access to the GraphWin class.

b. Run the code so that you have access to the show_color function in the shell.

c. Try visualizing various RGB colors by calling the show_color function. It should open up a new window each time.

>>> show_color(255, 128, 0)
>>> show_color(0, 128, 128)
>>> show_color(128, 43, 97)

d. Experiment with other RGB values to see what colors you can produce.

e. How can you produce the color black? What about white? What about gray?

Exercise 2

In this exercise we will experiment with images.

a. Start by downloading this extremely cute cat picture and save it in the same directory as your images_lab.py file.

b. Now create and show the cat image:

>>> img = Image(Point(0,0), "cat.png")
>>> print(img)

Note: When you use the print function on an Image object, it converts the image to a string that Thonny interprets as an image. Thonny is kind enough to display this image in the shell for us instead of printing off a textual representation of the image.

e. We can also write functions that manipulate images and colors to create cool effects. Consider the following example:

def darken_pixel(r, g, b):
    """Applies a darkening effect to a color

    Parameters:
        r: an integer between 0-255, inclusive
        g: an integer between 0-255, inclusive
        b: an integer between 0-255, inclusive

    Returns:
       new_pixel: a string-encoded RGB color
    """
    return color_rgb(r//2, g//2, b//2)

def darken_image(img):
    """Applies a darkening effect to all pixels of img

    Parameters:
        img: an Image object

    Returns:
        Nothing; mutates the img object directly
    """
    # Iterates over all the pixels of img
    for y in range(img.getHeight()):
        for x in range(img.getWidth()):
            r,g,b = img.getPixel(x, y)
            new_pixel = darken_pixel(r,g,b)
            img.setPixel(x, y, new_pixel)

Copy the above functions into your lab file and rerun the file to gain access to the function in the shell.

f. Now try applying it to the cat image.

>>> cat = Image(Point(0,0), "cat.png")
>>> darken_image(cat)
>>> print(cat)

You should see the following image:

Darkened cat

g. We can even save images back to the disk after we have modified them. Try saving the modified image using the save method and then try viewing it with your default image viewer.

>>> cat.save("cat_darkened.png")

Exercise 3

What other cool effects can we implement in Python and apply to images? One common one is creating a grayscale effect.

a. Using the darken_pixel function as a guide, create a function named gray_pixel(r,g,b) that takes three RGB integers as parameters and returns a new color created from the average of the RGB components. For example, if your pixel is red, i.e. r,g,b = 255, 0, 0, the grayscale version of this pixel is color_rgb(85,85,85) because (255+0+0)//3 is 85.

b. Using the darken_image function as a guide, create a function named grayscale_image(img) that takes an Image object as a parameter and modifies it so that every pixel is grayscale. To turn a pixel gray, you should use your solution from part a.

When you print the modified image, you should see the following.

Gray cat

Exercise 4

a. Write a function akin to darken_pixel and gray_pixel that is called increase_contrast(r,g,b) that returns a new color where each component is brighter or darker depending on its value. In particular,

  • If an RGB component of the input color is between 0 and 127, then the corresponding RGB component in the output is decreased by 32.
  • If an RGB component of the input color is between 128 and 255, then the corresponding RGB component in the output is increased by 32.

For example, if you call increase_contrast(64, 200, 150), the returned color should be color_rgb(32, 232, 182).

Note: Your increase_contrast function will need to make sure that each output component is between 0 and 255. For example, increase_contrast(10, 250, 32) should return color_rgb(0, 255, 0).

b. Now write a function increase_contrast_image(img) akin to the grayscale_image and darken_image functions that applies the increase_contrast function to each pixel in the image.

Exercise 5

In the previous exercises, we applied an effect to an image by transforming each pixel uniformly. However, to do more interesting transformations, we need to change pixels in a non-uniform way. For example, if we want to create a gradient effect, we need to darken some pixels more than others.

Suppose we want to create the following effect:

Gradient cat

Doing this requires darkening pixels on the very left by 100% (i.e. make them black), darkening the pixels on the very right by 0% (i.e. leave them alone), and darken the middle pixels somewhere in-between.

Write a function called gradient_image(img) that takes an Image object as a parameter and modifies it to have the above effect. To create the effect, you’ll need a percentage of how close a pixel’s x value is to the left-hand-side of the image. You can compute this using the expression:

percent = x / img.getWidth()

The percent above is a number between 0 and 1 of how close a pixel is to the left-hand-side of the image. Using this, you can darken the RGB values of a pixel by how close it is to the left-hand-side.

Exercise 6

In the previous exercise, we created a left-to-right gradient effect. In this exercise, we will create a spherical gradient effect. Ultimately we want to produce an effect that looks like the following:

Spherical gradient cat

To do this, we will need a percent of how close a point (x, y) is to the center of the image. You may find the distance(p1, p2) function we wrote previously helpful in computing this percentage:

def distance(p1, p2):
    """Returns the distance between points p1 and p2"""
    diffx = p2.getX() - p1.getX()
    diffy = p2.getY() - p1.getY()
    return math.sqrt(diffx**2 + diffy**2)

The above image was created using a radius of img.getWidth() / 2 so that the pixels in the corners of the image are black. (In other words, any pixels that have a distance of radius or more from the center of the image are black.)