Computing in the Life Sciences

CPSC 301:Computing in the Life Sciences
Term 2, Winter 2016–2017
Lab 9
Manipulating Images in Python
MARK: [15]
Due: Sunday, April 2, 9:00am
Name
Number
Account
Name
Partner
Number
Account
Of student
Email
submitted
the lab
Approximate Before Lab
time to
In Lab
finish
After Lab
In previous labs you manipulated DNA, RNA and protein sequence data taken from a FASTA file.
All of these sources of sequence data—and many other types of data, such as the characters in
a file—share a common property that makes them easy to deal with: the data can be treated as
a single long list. However, there are many other types of data which are more like tables in
that they have rows and columns. In programming languages, this type of data is commonly
stored in a data structure called an array. In this lab we will manipulate one particularly
common class of such data: images.
Submitting
Student
In an earlier lab you used modules from the Biopython package to perform some DNA
manipulation. Biopython is an external package: a collection of modules which is not part of
basic Python. In this lab we will use another external package: Pillow, which grew out of (was
"forked") from an earlier package called PIL (the Python Image Library). Unlike Scratch, Python
has no built-in support for images; however, by loading components of Pillow we can get that
support. For more information on Pillow, you can look at the online documentation (the most
useful sections are those labelled "Guides" and "Reference"). In this lab we will only be using
the Image module of Pillow. A word of warning: Pillow's documentation, like a lot of
programming documentation, can sometimes be challenging to decipher.
Objectives
After this lab you will be able to:

Create programs that will open, modify and save images.
CPSC 301: Lab 9
1
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17


Use Python to modify and combine existing images with special effects.
Iterate over and manipulate two dimensional arrays.
1. [1] Digital images and colour
In a previous lab we explored spreadsheets, which store data in a two dimensional array of
rows and columns. The intersection of a row and a column is called a cell. Each cell contains a
single number, string or function.
Images are also stored as a two dimensional array of rows and columns. The intersection of a
row and a column is called a pixel. However, pixels do not store a number, string or array;
rather, they need to store a colour. The figure below shows an example image, and a zoomed
in view of a tiny portion of the image right next to the baboon’s eye. In this zoomed view, each
square region of constant colour is a pixel.
Instead of expressing colours with names, computers often express colours in a format called
“RGB” (red-green-blue): A number is stored for each of the colours red, green and blue
representing the amount of that colour in the pixel. By combining different amounts of red,
green and blue, any colour can be generated. Modern text and graphic software often give
sliders to select colours in RGB. For example, in Microsoft Word (on Windows) select the Font
Colour button, the “More Colour...” option, click the “Custom” tab, and select RGB as the Color
model (on Mac: More Colour > sliders tab > option drop down > RGB slider). You will see
something similar to the following panel, where you can adjust RGB values to obtain different
colours.
CPSC 301: Lab 9
2
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17
1.1. [1] Before lab: Using the Colours menu in Microsoft Word, Excel, or any similar RGB
viewer (eg: http://www.colorschemer.com/online.html), pick two different colours that
are not in the lecture notes and give their common English names and RGB values. For
example: “dark yellow” could be R: 155 G: 160 B: 0.
Answer
2. [2] Exploring Pillow’s Image module
Now we are ready to use the Image module from PIL to explore and manipulate images in
Python. In order to do so, we need to be able to tell Python which pixel we want to examine or
modify. Remember that in a spreadsheet, columns are identified by letter(s) and rows by
numbers, so to specify an element we give a letter-number combination, such as “C7” (the cell
in the third column and seventh row). That same identification scheme could be used for
images, but it is easier to manipulate numbers in Python so Pillow uses a pair of numbers
instead. A pixel is identified by a two element tuple: the first element is the column and the
second is the row. Although it involves two numbers, this tuple works like the index in a list—in
both cases a unique element in the collection is identified—so it is often called the index or
coordinate of the pixel. Like a spreadsheet, counting starts in the upper left corner of the
image; however, in keeping with Python tradition counting starts with zero. An image with five
pixels in each direction (often called a 5x5 image) would be indexed as shown:
CPSC 301: Lab 9
3
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17
2.1. Before lab: Using the documentation for Pillow's Image module, determine what the
following function, methods and attributes do: open(), save(), getpixel(),
putpixel(), and size.
2.2. In lab: Download the files image_modify.py and baboon.png from the course
web site into a Lab09/ directory.
2.3. [1] In lab: Open image_modify.py and run it. Note that you will have to open the
output image file in order to see the effect of this code.1 Summarize in plain English, in
a maximum of 25 words, what it does. This is roughly 1-2 brief sentences -- longer
answers will lose marks. (See the clicker questions from the Image Processing lectures
for many good examples of brief summaries.)
Show the TA your summary.
Answer
2.4. [1] In lab: Because it is a script, all of the variables in image_modify.py are global
variables and remain defined when the script finishes. Use the Python shell after this
script completes to determine the red channel at the upper left corner pixel, the blue
channel at the upper right corner pixel, and the green channel at the pixel one inward
both horizontally and vertically from the lower right corner of the image. What
command(s) did you use, and what was Python’s response? Show the TA your results.
1
Although Pillow’s Image module does have a method called show() which will display images from within
Python, we have found this method to be unreliable when used repeatedly in the labs; consequently, we
recommend that you just save your output images and view them using a separate viewer (eg: double click the
image in your file viewer).
CPSC 301: Lab 9
4
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17
3. [5] Function Practice
Before starting to manipulate your image, here are some examples of functions that you might
use for the task. Some of these functions use the getpixel() and putpixel() methods
from the Image module of Pillow. In answering the questions, you may assume that the
appropriate components of Pillow have been imported.
3.1. [4] Before lab: For each fragment, summarize in plain English, in a maximum of 25
words, what that fragment does. This is roughly 1-2 brief sentences -- longer answers
will lose marks. (See the clicker questions from the Image Processing lectures for many
good examples of brief summaries).
3.1.1. Fragment one: You may assume that rgb contains a colour tuple.
def dominant(rgb):
which = -1
out = -1
for i in range(len(rgb)):
if(rgb[i] > out):
out = rgb[i]
which = i
return which
Answer:
3.1.2. Fragment two:
def dominate(image, y):
for x in range(image.size[0]):
rgb_in = image.getpixel((x,y))
rgb_out = list(rgb_in)
which = dominant(rgb_in)
for i in range(len(rgb_in)):
if(i != which):
rgb_out[i] = 0
image.putpixel((x,y),tuple(rgb_out))
CPSC 301: Lab 9
5
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17
Answer:
3.1.3. Fragment three:
def rotate(tuple_in):
list_out = list(tuple_in)
for i in range(len(tuple_in) - 1):
list_out[i] = tuple_in[i+1]
list_out[-1] = tuple_in[0]
return tuple(list_out)
Answer:
3.1.4. Fragment four:
(width, height) = image.size
for x in range(width):
for y in range(height):
color = image.getpixel((x,y))
if((x % 2 == 0) and (y > (height / 2))):
image.putpixel((x, y), rotate(color))
Answer:
3.2. [1] In lab: Write a short Python function which computes the average of a tuple or list.
Your function should take a single input: a sequence (something that you can iterate
over with a for loop). It should return a single output: the average of the numbers in
that sequence (as a float). You may assume that the elements of the sequence are all
numbers. Be sure to include the line beginning “def ...” and a docstring explaining
your function. Write your function in Spyder, test it, and then copy it here when you
are satisfied.
CPSC 301: Lab 9
6
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17
4. [4] Programming in Image
Now we will try some nontrivial manipulations of a sample image of your choosing.
4.1. Before lab: Obtain an image file (not too large) that you would like to transform in the
lab, change its name to test_image, and keep the original extension. We
recommend that you use a file of type jpg, png, gif or tif; however, Pillow understands
many other formats as well. Note: You can read and write files in different image
formats; for example, you can open() a jpg file, modify the pixels and then save() it
as a png file.
4.2. [2] In lab: Create a copy of image_modify.py and call it grey.py. Modify the
code in grey.py to implement the following algorithm (you will find your averaging
function from the previous section useful for this implementation):
 Open your test_image file. Be sure to use the full filename including the
extension, since Pillow needs to know the type of image file in order to figure out
how to load it.
 For every row in the image:
 For every column in the image:
 Determine the colour tuple for the pixel at that row and column
 Compute the average of that color tuple using a function call.
 Replace all three colors for that pixel with the average computed by the
function.
 Save the modified image as grey_image.png
When you are happy with your results show the TA your code and your input and
output images.
4.3. [2] In lab: Create a copy of image_modify.py and call it quadrant.py. Modify
the code in quadrant.py to implement the following algorithm:
 Open your test_image file
 For every pixel in the image
CPSC 301: Lab 9
7
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17

If the pixel is in the upper left quadrant of the image, set the red colour channel
to zero.
 If the pixel is in the upper right quadrant of the image, do nothing.
 If the pixel is in the lower left quadrant of the image, set the green colour
channel to zero.
 If the pixel is in the lower right quadrant of the image, set the blue and red
colour channels to zero.
 Save the modified image as quadrant_image.png
When you are happy with your results show the TA your code and your input and
output images.
Hint: Note that getpixel() returns a tuple that represents the color of a pixel, and
that values in that tuple can be unpacked into separate variables using multiple
assignment. For instance, the assignment(r, g, b) = image.getpixel((x,
y))will assign to the variables r, g and b the values of the red, green and blue
channels for the pixel (x,y) of the given image.
5. [3] More Programming
In this section you will write programs to implement some other image manipulations. Note
that Pillow has specialized functions and/or methods which can quickly create some of these
effects. In order to be given credit for this part, you must create each effect using only the
following functions, methods and/or attributes from Pillow’s Image module: open(),
new(), save(), copy(), getpixel(), putpixel(), and size.
5.1. [1] After lab: Generate the negative of an image; in other words, replace all of the
colour channels in all of the pixels by 255 - <current colour>. The algorithm to
accomplish this task will be similar to the algorithm in grey.py in the previous
section, except that you are performing a slightly different manipulation of the colour
tuple for each pixel. Save your program as negative.py. Test your program with
your test_image and store the modified image as negative_image.png.
5.2. [1] After lab: Flip an image horizontally. The result should be a new image which is the
horizontal flip of the input image. That is, the top side of the original image will be the
bottom side of the new image and the bottom side of the original will be the top side of
the new image. Like your programs from the previous section, you will need to iterate
over all of the pixels in the input image; however, in this case it is the coordinates of
the output pixel which are different rather than the color. To determine the
coordinates of the output pixel, you will need to perform some arithmetic on the
coordinates of the input pixel. You will also have to be careful not to overwrite pixels
CPSC 301: Lab 9
8
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17
before you have read them. Save your program as flip.py. Test your program with
your test_image and store the modified image as flip_image.png.
5.3. [1] After lab: Form a collage: Replace some portion of a large image with pixels from a
smaller image, or put multiple images side by side or on top of one another. For this
task, you will need multiple input images; you can either find multiple raw input
images, or you can use a single raw image and apply different transformations (such as
the transformations discussed above) to generate multiple versions. The basic
algorithm will involve iterating over the pixels in the input and/or output images, so
you will probably find your quadrant.py code a useful starting point; however, you
will definitely need to perform transformations of the pixel coordinates for this task,
since the input and output image(s) will be of different sizes. For that reason you are
allowed to use the resize() method of the Image class. Save your program for the
extension as collage.py and the resulting picture collage_image.png (or
.jpg or any other common image format). Note: This code will be noticeably more
complicated than the previous parts; have fun, but don't go overboard trying to get this
single point.
Submission Checklist
Only one person in each group will submit the lab using the handin tool.
The assignment name you should use with handin to submit this assignment is of the form
“Lab09x” where x is the lower case last letter of your lab section, that is, a for section L2A, b
for section L2B, c for section L2C, d for section L2D, e for section L2E, and f for section L2F. If
you are working with a partner in another section, submit to the section which you attended.
While you may submit multiple times using handin (before the deadline), only your last
submission will be graded. Therefore, you must submit and re-submit all the relevant files in a
single zip archive. For this lab your submission archive should include

A completed version of this lab document. Do not forget to fill in the table at the top
with your identity information, your partner’s identity information, and a rough
estimate of how long each component of the lab took (to the nearest 10 minutes is
fine).

The Python files grey.py, quadrant.py, negative.py, flip.py and
collage.py.
CPSC 301: Lab 9
9
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17

Your image files baboon, test_image, grey_image, quadrant_image,
negative_image, flip_image , collage_image and any other images that
you used to construct the collage.
Appendix: Pillow installation instructions
Some versions of Anaconda appear to come with the Pillow package already operational, and
others do not. To check whether Pillow is operational, open a new console in Spyder and at the
Python prompt enter (with exactly the same upper and lower case letters):
from PIL import Image
If this command generates no output, then Pillow is available. If Python generates an error of
the form ImportError: No module named 'PIL' then Pillow is not available and you
must install it following the procedure described below. This procedure is the same as when
you installed BioPython in Lab 06.
To install non-default packages into Anaconda, we use the conda command. Note that the
conda command is not part of Python – you must run it from a command prompt. The details
of starting a command prompt depend on your operating system:



In Windows, run the "Anaconda command prompt" (use the search feature to find this
program). It should start in your Anaconda directory.
In MacOS, start a "Terminal" and go to your Anaconda directory.
In Linux, start a shell and go to your Anaconda directory.
Once you have a command prompt and are in your Anaconda directory, you should be able to
type conda install pillow, press enter, and then accept the default choices from that
point forward. You must have an Internet connection for this to work, since conda needs to
download the Pillow package (and possibly some other packages which it wants to update).
Assuming that the conda command completes successfully, when you restart Spyder you
should be able to repeat the import command given above without error.
For your future reference, there is a full list of the packages integrated with Anaconda. Those
that are included by default are labelled as "True" in the final column "In Installer"; you should
be able to import any of the modules within those packages without any further effort. Those
packages that are not included by default (such as Biopython) can be installed using the conda
command as described above.
CPSC 301: Lab 9
10
© 2016–2017 Jessica Dawson, Ian M. Mitchell, George Tsiknis
Version: 28-Jul-17