CSC111 Lab 5
--Thiebaut 03:47, 24 February 2009 (UTC)
- 1 Exercise 1: Using the Watcher
- 2 Exercise 2: Top-Down Design, and Blurring Pictures
- 3 Exercise 3: Functions returning pictures
Exercise 1: Using the Watcher
For this problem we are going to play with the Watcher part of JES.
Copy/Paste the following program in JES:
def doubleIt( a ): return a * 2 def incrementIt( b ): return b + 3 def main(): x = 3 y = 7 v = doubleIt( x ) + y v = incrementIt( v )
Run the program.
Notice that nothing gets printed. Can you guess, though, the final value of t? Go through the program "by hand" and see if you can figure out the answer...
The Watcher Window
JES has a debugging tool called the Watcher, which we are going to use to see how the variables evolve with the program
- Click on the Watcher button to bring up its window:
- Click the Add Var button several times to add all the variables used in our program: a, b, x, y, and v. Note that the Watcher adds a column for each variable we add:
- Now run the program by executing the main() function.
- Observe the Watcher window:
- The left column indicates all the steps taken by the program. the line and instruction columns indicate which line of the program executed during a given step. The remaining columns show when the variables come into play, and what value they have when they exist.
- Go through each line of the JES Watcher window, and make sure you understand why the different variables have different values at different times, and why sometimes they are represented by a dash (-). If it does not make sense, make sure to ask questions!
- The Watcher's last reported step is Step 9, when return b+3 is executed. So, in this case, what gets stored into v in Line 12 of the code? Figure it out. To verify that your answer is correct, add a print statement to the end of your program, so that the program can tell you what v is:
print "v = ", v
- From now on, use the watcher when you debug your programs. It will help you figure out why your program is not behaving the way you think... The more you use it, the more helpful it will become!
Use the Watcher to find the final value of y, without making the program print its value at the end?
def doubleIt( a ): return a * 2
def incrementIt( b ): return b + 3
def main(): x = 3 y = 7 v = doubleIt( increment( x ) ) + y x = incrementIt( v ) y = doubleIt( v )
Exercise 2: Top-Down Design, and Blurring Pictures
For this exercise we'll use a small version of Bert, shown at right. The reason is that blurring takes a lot of time, so working on small pictures will avoid having to wait too long.
As we saw in class, blurring a picture requires working with two pictures: an original that is not modified in the process, and a copy of the original where the color of each pixel is the averaged of the color of the neighboring pixels.
We will proceed in a top-down fashion. Please follow the steps carefully, making sure you do not move on to the next step until the previous one works fine.
Type out the code, and don't copy & paste.
Step 0: the problem at hand
The task is to have a program that takes a picture, any picture, and blurs it, returning a blurred version to us.
Step 1: the main program
- Start from scratch, with an empty program.
- The first thing we'll do is create the main program:
def main( original ): # gets an image, compute a blurred version of it, # and returns the blurred image back return original
- It doesn't make much sense at present, but this is our approach: write one function at a time, and make it do something close to what it is supposed to do, getting us closer to the final solution.
- Write the program, save it, load it, and pass it a picture. Verify that you do not have any errors.
>>> show( main( pict ) ) # I assume that you have a variable pict in your environment that contains a picture.
Step 2: flesh out main
- main() must receive a picture, blur it, and return it...
def main( original ): return blur( original )
- Which makes sense... Right?
- However, to make JES not complain about the above function, we need to introduce a blur(...) function. Instead of writing a real blurring function, we simply write a skeleton function that returns the image without touching it. Your program will now look like this:
def blur( original ): # gets an image, and returns the blurred version of it, without modifying the # original return original def main( original ): # gets an image, compute a blurred version of it, # and returns the blurred image back return blur( original )
- Write this code and test it. Make sure it does not generate any errors. Your program should show the image you pass it:
>>> show( main( pict ) )
Step 3: fleshing out blur(...)
We saw in class that blurring requires taking all four neighbors of a pixel, getting their color components, and creating a new color from the averaged components. In other words, the blue component of a blurred pixel will be the average of the blue component of its original top neighbor, original left neighbor, original bottom neighbor, original left neighbor, and its original self. Similarly for the green and red component.
- So, let's make blur(...) create a new image that, for right now, is an exact duplicate of the image it receives, and that will be enough for this step:
def blur( original ): # gets an image, and returns the blurred version of it, without modifying the # original # add some code to create an empty image the same size as the original copyPict = makeEmptyPicture( w, h, black ) # where w and h are the width and height # add some code that will take every pixel of original and copy its color onto # the pixel at the same coordinates in the empty image for ... # return the nwe image return copyPict
- Test that your program works without errors.
>>> show( main( pict ) )
- Does it still display the original? Should it display the blurred picture yet? Are you sure?
Step 4: Implementing the blurring algorithm
Now comes the part where drawing a picture of what we want to do might be helpful.
- For each pixel o in the new image, we want to locate 5 pixels in the original image, one pixel at the same location, and 4 at the four cardinal points (North, West, South, East).
- Moreover, we want to make sure that the nested loops that control x and y of the pixels verify that the value of x for all pixels touched is always going to be between 1 and width of the image, and y always between 1 and height.
- Modify the code below, and make the nested loop compute the red component of a pixel in copyPict to be the average of the red components of the 5 neighboring pixels in the original.
def blur( original ): # gets an image, and returns the blurred version of it, without modifying the # original # add some code to create an empty image the same size as the original width = getWidth( original ) height = getHeight( original ) copyPict = makeEmptyPict( width, height, black) # add some code that will take every pixel of original and copy its color onto # the pixel at the same coordinates in the empty image for x in range( 1, width+1 ): for y in range( 1, height+1 ): originalPixel = getPixel( original, x, y ) copyPixel = getPixel( copyPict, x, y ) setColor( copyPixel, getColor( originalPixel ) ) # return the nwe image return copyPict
- run your program, and get rid of any syntax errors, then check to see if any blurring of the red takes place. You may have to blur the image a few times to see some result.
>>> show( main( pict ) ) >>> blurPict = main( pict ) >>> blurPict = main( blurPict ) >>> blurPict = main( blurPict ) >>> show( blurPict )
Step 5: From special case to generalization
If some blurring of the image seems to happen, then it's time to treat the green and blue component of the pixels the same way you did with the red component.
- Go ahead and modify the blur function some more: add the code that will blur all three color components.
- Test your code by repeatedly blurring the picture.
Step 6: Testing, testing, testing...
Are you really sure that your program works? It may seem to blur... but is it really blurring all components exactly? The best way to find out is to test it with special-case pictures that contain easy patterns to test our algorithm on.
One such picture is one that is all white (or all black) with one green line, one blue line, and one red line.
Write a simple program in Python to generate this picture and to save it to your H: drive. Then blur the picture once, saving it in a variable. Then use the media tool to check the color components of the pixels on the colored lines and right next to the lines. What should the values be on the line and next to the lines? Is that what you observe?
Exercise 3: Functions returning pictures
If you are still having a hard time with functions returning values, this exercise is for you! It will make you play with functions that return things back to their caller.
Getting a copy of a picture
The function below receives a picture, creates a new picture with the same dimension, and with a white background, and returns the new picture:
def blankVersion( picture ): # receives a picture, creates a white picture with same dimension, # and returns it. w = getWidth( picture ) h = getHeight( picture ) newPict= makeEmptyPicture( w, h, white ) return newPict
- Add it your program area, and verify that you can pass it a picture and that it will return a new blank picture:
>>> pict = makePicture( pickAFile() ) >>> newPict = blankVersion( pict ) >>> show( newPict )
- Using this function as example, create a new function called newCopyOf( ... ) that returns a new picture that is a copy of the picture passed to it. In other words, create a function that
- receives a picture as parameter,
- creates a blank picture with same dimensions,
- copies all the pixels from the picture just received to the blank picture, and then
- returns the new picture.
- Test that your new function works!
>>> show( pict ) # verify that the original pict is still there >>> newPict = newCopyOf( pict ) >>> pict = blankVersion( pict ) # erase all the pixels in pict! >>> show( pict ) # pict should be of a solid color >>> show( newPict ) # but newPict shouldn't!
Changing the background of a picture
Imagine that we want to copy the heart picture over a picture of the Mardi Gras character shown at right.
Try copying the heart onto the the Mardi Gras picture using the function below which we have seen in Lab #3:
def copyToOffset( fromPic, toPic, offsetX, offsetY ): # copies all the pixels from the fromPic picture and put # then location offsetX, offsetY in the toPic Picture for pixel in getPixels( fromPic ): x = getX( pixel ) y = getY( pixel ) col = getColor( pixel ) toPixel = getPixel( toPic, x+offsetX, y+offsetY ) setColor( toPixel, col )
- It doesn't look too good, does it?
- One problem is that the background of the heart is black and the background of the Mardi Gras character is white. Why don't we write a function to which we pass a picture with a black background, so that it makes a new copy of it where all the black pixels have been turned white?
- Go ahead and write such a function, and call it changeBckGrnd( pict ). You may find that changing all the pixels for which the distance between their color and black is less than 50 does a nice job.
- Verify that your new function works well:
>>> heart = makeAPicture( pickAFile() ) >>> whiteHeart = newCopyOf( heart ) >>> show( whiteHeart ) >>> whiteHeart = changeBckGrnd( whiteHeart ) >>> show( whiteHeart )
- Now that you have created functions returning pictures, though, you can write this in a much more concise form:
>>> show( changeBckGrnd( newCopyOf( heart ) ) )
- Try it!
Figure out a way to start with the picture of the heart on black background, and the Mardi Gras picture, and in one line of Python code, and one line only, copy the heart with white background onto the Mardi-Gras picture, and to show it on the screen.
This is it for this lab!
You are now ready for Homework #5
Laissez le bon temps rouler... Joyeux Mardi Gras!