Lab Exercise 5: Lists as Information Collections
The purpose of this lab time is to prepare for project 5, which will use lists of information to produce collages.
Here is the reference guide to the Zelle graphics package.
Download two or more of the following images. Right click on the link and select 'Save As...' or 'Download Linked File As...'.
If you want to make the images smaller while testing, the command to shrink an image by half is below.
convert myImage.ppm -scale 50% mySmallImage.ppm
If the above command says "command not found", then try the following.
/opt/local/bin/convert myImage.ppm -scale 50% mySmallImage.ppm
On your personal volume, make a folder for project 5 (Project5). Then download the graphics and display packages. You can also just copy them over from project 4.
Copy your filter.py file from your Project4 directory into your Project5 directory.
- Write a placeImage function with alpha blending
Alpha blending is the formal name given to blending two images. Given two colors C_1 and C_2 and the opacity alpha of C_1, the alpha blend is given by the following. The opacity alpha should always be between 0 and 1.
C_blend = alpha * C_1 + (1.0 - alpha) * C_2
If you have two colors, (r1, g1, b1) and (r2, g2, b2), then the alpha blend of the red color band is given by the expression below. Use a similar expression for the green and blue values. Note that the graphics color_rgb function expects integers, hence the cast.
r = int(r1 * alpha + r2 * (1.0 - alpha))
Open your filter.py file. Add a parameter alpha to your placeImage function. Then implement alpha blending when setting the pixel in the destination image. This is a modification of your existing code.
+ (more detail)The new algorithm is given below. Each comment is a line of python code. Note the added function parameter alpha.
def placeImage( dst, src, x, y, alpha ): # for each row i # for each column j # assign to (r1, g1, b1) the rgb values from src location (j, i) # assign to (r2, g2, b2) the rgb values from dst location (j + x, i + y) # assign to rnew the alpha blend of r1 and r2 # assign to gnew the alpha blend of g1 and g2 # assign to bnew the alpha blend of b1 and b2 # set dst pixel (j+x, i+y) to (rnew, gnew, bnew)
Remember to remove our comments once you have written the code (they contribute to clutter).
When you are finished, download the file testimage.py, read through the test file, and run it. As the usage statement indicates, it wants the names of two ppm images. When the program is finished, look at the image blend.ppm using your show.py file from last week.
If you wish, use the program display to look at ppm images. Just type
on the command-line and it will display the image. Type 'q' in the image window to make the program quit.
If it says command-not found, use the following.
Note that in a terminal you can put a process into the background by adding the & symbol after the command. So if you type
display blend.ppm &
you can still use the terminal and the display program will run in the background. If you type return or enter in the display window it will re-read the image from the file, so you don't have to quit display while you are experimenting with an image.
- Defining the parts of a Collage
This section explains how we think about a collage for this project. It does not require any coding. The next section picks up with the first functions for this week's project.
The goal of the project this week is to enable you to easily blend together multiple images, with various effects, into a collage. Rather than hard code all the collage positions, however, we want to put the essential information defining a collage into a list. Then we can write a general function that takes in all of that information and generates the correct collage image. To change the collage, we then just change the information defining the collage, not the code.
What is the essential information that defines a collage?
First, identify the essential information for a single image, since a collage is just a collection of single images.
- Filename of the image
- X offset in the collage
- Y offset in the collage
- Effect for the image
- Alpha blend value
- The Image object (after being read from a file)
To store the information for a single image, we can use a list. To store the information for all of the images, we can use a list of lists. Consider the assignment below, which creates a list that defines a collage with two images.
collageList = [ [ 'maine1.ppm', 0, 0, 'rbswap', 0.8, None ], [ 'maine5.ppm', 200, 150, 'original', 0.8, None ] ]
The one problem with using a list is that when we need to access a parameter for a given image, we have to remember that parameter's index in the list. Consider looping over collageList and printing out the filename.
for pictureParams in collageList: print( pictureParams)
We have to remember that the filename will be pictureParams, since we set up the parameter list with that value in the first position.
Using a design where you have to use actual numbers means it is easy to make mistakes when you have to remember whether a given value is in position 4 or position 5. It also makes it hard to modify your code. For example, if you feel you need to add a new parameter to the list of parameters for a picture, what happens if you put it first in the list? That would mean you would have to go through all of your code and change the indexes everywhere you were accessing a parameter value. That's a lot of code and a lot of opportunities to make mistakes.
Instead, how could we use software engineering principles to avoid having to remember the index? What about using a module-level (top-level) variable whose value remains constant throughout the simulation? We can use it to index into the list. For example, we could create a variable IDXFilename that is set to 0, the index of the filename in the list of parameters for an image. Then, when we need to access the filename, we use code like
for pictureParams in collageList: print(pictureParams[IDXFilename])
instead of the code above. It's a lot easier to remember IDXFilename, and if you decide to change the position of the filename parameter in your list, you have to change only one place in your code: the assignment of a value to IDXFilename.
- Write the collage function
Create a new file called collage.py. You will want to import filter so you can use your filters to modify the images.
After the import statements, create the module-level variables you will use to access the individual picture lists. Create one variable for each of the picture parameters: filename, X offset, Y offset, filter, alpha, and Image. When naming your variables, please begin each name with IDX to indicate the value is an index, then use a name that indicates the field. Using IDX is not a Python rule, but a convention the differentiates these variables as having a special purpose. Each of the assignments will be something like the following.
IDXFilename = 0
Since we will be using mnemonic variables as indexes, it doesn't really matter what order you put the values into the list. However, if you want to be able to use the provided test code then the ordering needs to be: filename, x-offset, y-offset, filter, alpha, Image.
Copy the following test function into your collage.py and then add more print statements to test each of the parameter entries. Run it and make sure it prints out the right information for each of the pictures.
# project test function def test( argv ): collageList = [ [ 'maine1.ppm', 0, 0, 'rbswap', 0.8, None ], [ 'maine2.ppm', 200, 150, 'original', 0.8, None ] ] # test that the parameters are accessing the right value for picParams in collageList: print( '----------------------') print( 'filename:', picParams[IDXFilename]) print( 'X Offset:', picParams[IDXXoffset]) print( 'Y Offset:', picParams[IDXYoffset]) # add more print statements here, one for each parameter if __name__ == "__main__": test( sys.argv )
- Next, write a function readImages. Given the collage list structure we designed above, write a function that reads in each of the images in the collage and for each image stores the corresponding Image in the picture list. The template is below.
# reads in the files in the collage and stores the Image objects in the list def readImages( clist ): # for each pictureParams in clist # assign to the variable filename the IDXFilename element of pictureParams # assign to the variable src, the Image returned by reading filename (graphics.Image) # assign to the IDXImage element of pictureParams the value in src
Test the readImages function by adding to the end of your test function in collage.py. Call readImages, and then print out the names and sizes of each image. Since in the readImages function you stored a Image in the last location of each picture list, you can use the Image to getWidth() and getHeight(). You can use the template below in your test function.
# call readImages with collageList as the argument # for each picParams in collageList: # print the IDXFilename element of picParams # print the result of using the IDXImage element of picParams to call the method getWidth() # print the result of using the IDXImage element of picParams to call the method getHeight()
When you are done with the lab exercises, you may begin the project.