Assignment 2. Homework 2 - Images


Due Wednesday, July 7 at 11:59 pm Pacific

  • Submissions received by due date receive a small on-time bonus.
  • All students are granted a pre-approved extension or "grace period" of 48 hours after the due date. Late submissions are accepted during the grace period with no penalty.
  • The grace period expires Fri, Jul 9 at 11:59 pm Pacific, after which we cannot accept further late submissions.
  • In this course, we express all date/times in Pacific time GMT -7. Our Paperless submission system also displays/records due dates and submission times in Pacific time.

Homework 2a - Image

Solve these functions with nested image-range. We'll do more examples of this type of problem Friday, and hand out the other parts of this homework.

a. reflect()

b. hat()

c. purple()


Turn In

Turn In HW2a to Paperless


Homework 2b - Image Grid

How many times have you written the loops to copy some pixels from one image to another. I know right? A lot. For this project, you will write a function that solves pixel-copying once and for all, and then you can just call your function when you need that solved.

You'll write your code for this project in PyCharm and run your code from the command line. To get started, download image-grid.zip. Open that .zip file to get a "image-grid" folder. Copy that folder to your computer (in Windows, confusingly, the folder may be nested inside another folder with the same name). Open the image-grid folder with the PyCharm open. menu command to get started (do not double click the image-grid.py file to get started, as that introduces some problems).


Pillow Install

For this project you need to install the "Pillow" library. Use this line in the terminal, perhaps from within PyCharm (do not type the "%" sign – that is your "prompt"):

% python3 -m pip install Pillow

It may mention something about your pip version, but you can ignore that. (Replace "py" for "python3" on Windows.) The important thing is that Pillow is installed. More details in the Python install document on our home page.


main()

When a Python program runs, the special function main() is the first to run, and it calls other functions. For this program we provide a working main() function. Depending on the command line arguments, main() calls one or another of your functions as shown below.


a. Hello

Just to see that you have the command line working, run your program with the -hello flag with your name, like this (again, don't type the "%" sign):

% python3 image-grid.py -hello Alice
Everything's coming up Alice!
%

On Windows it's "python" or "py" instead of "python3". The easiest way to get a command line in your image-grid directory is probably the "terminal" tab at the lower-left of the PyCharm window of your image-grid project.


b. draw_image()

Write code for the draw_image() function. This function has five parameters, but what it does will seem familiar from your earlier nested/loop code. This function is the biggest part of this project — the rest of the project concerns calling this function to get various outputs.

def draw_image(image, out, left, top, mode):

Draw image is given two images, image and out. The function draws a copy of image into out. The location of image is specified by the left and top parameters — left is the x value where the copy should begin, and top is the y value. So if the function is called with left=100 and top=50, then the (0, 0) pixel of image goes to the location (100, 50) in out, the (1, 0) pixel goes to (101, 50), and so on.
alt: how draw_image maps its
parameters

The mode parameter controls how the image is copied, it has four possible values: 'red', 'green', 'blue', 'all'

If mode is 'red' then only the red value of each pixel should be copied, leaving the green and blue values untouched. If mode is 'green' then only copy the green values, and for 'blue' copy only the blue values. If the mode is 'all' then copy all three values.

Use if-logic inside your loops, checking the mode to see if each color should be copied. There are many different ways the if-logic can be structured.

In previous exercises you would create the image objects, but here the image and out objects are passed in to this function fully formed; your code just uses them, e.g. accessing image.width and calling image.get_pixel().


Milestone 1: -test

When you work on a function, you want a way to run it so you can see if it works.

The provided function make_test() works with any original image. It creates a blue image 80 pixels wider and 40 pixels taller than the original. It then calls your draw_image() function, passing left 40 and top 20, to draw the original image centered on top of the blue.

Here is the result of calling it with poppy.jpg
alt: poppy / blue test

To try it yourself, run your image-grid.py with the command line as shown below. If draw_image() is correct, the result should show the image copied and centered on the blue as in the example above.

% python3 image-grid.py -test poppy.jpg

c. make_channels()

Recall that the "red channel" of an image is a version of the image made of only its red lights, and there are analogous green and blue channels. Here are the red, green, and blue channels of the poppy.jpg image: alt: red green blue
channels

Complete the code for the make_channels() function. The provided code creates the out image. Call your draw_image() function three times inside make_channels() to draw the three channels. There are a lot of parameters here - the image and out parameters are easy enough. The left parameter is the most interesting, specifying the x value within the out image where each copy should land. For the top parameter pass 0, so the image is drawn at the top edge of the output.

To see an example of calling the draw_image() function, look at the make_test() function, which calls draw_image() passing 40 for the left, 20 for top, and 'all' for the mode.

To run your make_channels(), use the -channels flag on the command line like this.

% python3 image-grid.py -channels poppy.jpg

Debugging - Exceptions and print()

Often when code runs the first time, we learn that it's not doing what we wanted. For the image loop code, you might see an "exception" - a run-time error - like the following that halts your program. The exception text should appear in the same terminal where you launched the program.

  File "./image-grid.py", line 116, in main
    make_channels(args[1])
  File "./image-grid.py", line 57, in make_channels
    draw_image(image, out, image.width * 2 + 1, 0, 'blue')
  File "./image-grid.py", line 27, in draw_image
    pixel_out = out.get_pixel(left + x, top + y)  # key line
  File "/Users/nick/cs106ap/code/image-grid-solution/simpleimage.py", line 178, in get_pixel
    raise e
Exception: get_pixel bad coordinate x 600 y 0 (vs. image width 600 height 145)

The exception text is very busy looking, but just read it from the bottom up. In this example, the exception message explains that there was a call to get_pixel() with x=600 in an image of width 600, so that halted the program with an error (the max x value is 599).

Looking above that, you can see that the "offending" line that called get_pixel() with the bad x was line 27 in the draw_image() function. So here are two possible strategies:

1. Go look at line 27. You know for an absolute fact that line 27 called get_pixel() with a too big x value. Maybe you can spot the logic error just looking at the code.

2. Add a print() statement just above line 27, printing out the interesting values that go into your algorithm like: image.width out.width, left, x. Run your code again, and the print output will appear in the terminal, showing you the real numbers the code is using.

Another good spot for a print() is as the first line of a function, printing out its parameter values, like image.width, out.width and left. A function may be called many times with different values, so this gives you an insight into what's going.

You should remove your debugging print() calls from your code before you turn in it, they are a sort of scaffolding just used during construction.


Milestone 2 -grid and -random

Once -test and -channels are working, your draw_image() is likely correct. We provide the code for additional -grid and -random features shown below. They call your draw_image() function in more ways to get neat output. Every pixel of output you see here is coming from your draw_image() function.

% python3 image-grid.py -grid poppy.jpg 3

% python3 image-grid.py -random yosemite.jpg 3

The "3" after the image filename above specifies how big the grid is, so you can experiment with different values. The -random feature selects the mode for each copy randomly between `'red' 'green' 'blue', so each run of the program yields a different random combination. Try running the program with various combinations. Always use the up-arrow in the terminal to pull up your previous command line, and then edit it vs. typing it in from scratch.


All Done

Someday, perhaps you can leverage your code here to make some avant guard art, or perhaps the sort of art that hotels buy in bulk. Some of the output of -random is eerily reminiscent of hotel art. But for now you are done with project, getting quite a lot of use out nested-loops to process 2-d data. Please clean up your code in image-grid.py and turn it at paperless as hw2.2.


Homework 2c - Bluescreen

For this project, you will use code and your creativity to create a bluescreen image. We will have a class art-show and some small prizes with all this artistic output.


Bluescreen Contest Categories

We will have an in class art-show and contest using these images.

The contest categories for the images are:

  • Best artistic
  • Best humor
  • Best use of background
  • Best you hanging out with someone super famous

The foreground image for the bluescreen must be one that you yourself take; not something you grabbed off the internet. Often you yourself will appear in your foreground image. The Background image can be from anywhere, and please keep in mind that a selection of the entries will be shown to the whole class.


Bluescreen Code

See file bluescreen.py (in the image-grid folder) which has working code set up like the lecture monkey example. You can use adjust this code in any way, if needed, to get the effect you want. Run the front strategy with this command line.

% python3 bluescreen.py monkey-500.jpg moon-600.jpg 

With the front strategy, the back image needs to be at least as big as the front image

See the back strategy with with the following command line. In the command line below, the "200" is a shift_x applied to the monkey, so 200 moves it to the right, and -50 would move it to the left on the background image.

% python3 bluescreen.py monkey-500.jpg 200 stanford-600.jpg 

Art Show

For the grading of this project, you can get full marks from an image that demonstrates creating a real image and processing it with the bluescreen algorithm, and we will not be picky about the level of artistry or creativity. The artistry and creativity are important instead for the in-class art show and contest.

How to create your own bluescreen:


1. Take Several Foreground Images

First create several "foreground" pictures with people or whatever in front of a blue or green colored background, and save those images on your computer. You can use, say, a blue towel as background. We are not grading on the artistic details of your photography here; we just want an image and some code that work together. For the contest however, the sky is the limit.

Take a few different foreground images so you can experiment. For example, take one image with people in the center and another with them off to the side to fit in with interesting parts of the background. You can also try dressing the people in the foreground with blue clothes, so the background will show through those areas. You don't have to use blue, green is also popular, and you would just fix the code to work with your background color.


Image Size

Digital cameras create very large images like 4000 by 3000 pixels, 12 megapixels, and such large images will take too long to process and have far more pixels than the displays we have. Keep your original images, and create smaller versions for processing, say reduced to a width of 600 or 800 pixels with a filename like "myimage-600.jpg".

On the Mac, the built-in Preview application can crop and resize and export images, and on Windows I assume there's something similar. In Preview: open your original, use the Duplicate menu command first to save your original, then crop and resize and save.


2. Fix It In Post (optional)

"Fix It In Post" is a sort of Hollywood joke, referring to fixing some on-camera mistake later on in the computer post-production phase. This is totally optional, but you can do the same sort thing with your bluescreen image. For example, if part of the blue background is not quite big enough, you can edit the image in a paint program, and draw a blue rectangle over the area to just make it be blue the way you want. Since the blue gets removed by the bluescreen algorithm, it never appears in the final image. You are just creating a sort of hole for the background image to show through. On the Mac, the built-in Preview image app has a very limited ability to put colored rectangles and ovals on things, and on Windows the Paint program is similar.

You can fix up your image this way, but we want the actual bluescreen composition to be done by your code.

There are two strategies shown in the bluescreen.py file; either of these is fine for your output.


3. Bluescreen "front" Algorithm

The simplest bluescreen form is the one shown with the monkey/moon example.

Front Algorithm: Loop over the main front image (e.g. monkey in front of blue). Detect blue pixels, copy over pixels from the background on top of the blue pixels. The background needs to be as big as the front image, so we do not get out-of-bounds errors accessing non-existent pixels on the background. If you get that error, resize the back to be bigger in an image editor and try again. The changed front image is the final output


4. Alternate: Bluescreen "back" Algorithm

Loop over the front image. Detect non-blue (i.e. monkey) pixels. Copy these monkey pixels over to the back image, possibly shifted by some shift_x/shift_y amount to move the monkey to a specific spot on the background. When done, the changed background image is the final output.


5. Saving Your Output

The bluescreen output image files should be the product of your input files and your edits to the bluescreen.py code. You will submit your output image files and your bluescreen.py code. Save your favorite bluescreen output as a .png or .jpg file for upload. Please use your name in the file name, like "jane-smith1.png" to help use keep the entries organized. You must submit 1 bluescreen image, and you can submit a second if you like.


All Done

When you have your bluescreen output looking good, please submit your 1 or 2 output image files and your edited bluescreen.py file on Paperless as HW2.2 along with 2b.