CS 110 Fall 2010 

Lab 6: Strings and Pictures

Due: Thursday, October 14th at class time

This lab will have you implement some string manipulations for some interesting applications.  Please READ THE LAB CAREFULLY! You'll also be creating a few additional programs that manipulate images.  For the various programs involving strings, you'll be using:

s[i]: return the element (character) at index i in the sequence (string) s.

You'll also be using the built-in function len:

len(s): return the length of the sequence (string) given by s.

For example, if s is a string referring to the value "monkey" then s[2] would give the value 'n'.  The last character in a string is always given by s[len(s) - 1].

Part 1 - Palindromes

A palindrome is a string of characters that reads the same forwards as backwards.  For example, "UFO Tofu", backwards, is "ufoT OFU."  In other words, it's the same backwards, assuming you ignore spacing, punctuation and capitalization.  Other well known palindromes include "A man, a plan, a canal, panama", "Evil, a sin, is alive", "Damn! I, Agassi, miss again! Mad!" and "Yo, bozo boy!"  Alright, maybe not all of them are so well known.

As you now know, we can use Python to access any particular character in a given string.  That is, I could ask Python for the character at index 2 in the string "monkey" and it would tell me it was 'n' (note it isn't 'o'... char 0 is the first char in the string, char 1 is the second, etc.)  Lets say I have a string s and I know that its length is n.  So the last character in s has an index of n-1.  Write a program that looks at a particular string and determines whether or not it is a palindrome.  That is, after running your program, a boolean variable that you've defined within your program called palindrome should be true if and only if the string s really is a palindrome.  You may assume that all spacing and punctuation have already been removed, and that all letters are lowercase.  So our first example would be input as the string "ufotofu".  In such an example, after running your algorithm, the value of palindrome should be set to True.  If the input was instead "salads" then palindrome should be set to False. The result should be printed out to the user.

Create a program called Palindrome.java.  This should read in a string from the user and report whether or not it is a palindrome. For extra credit, allow the original string to include punctuation and to have mixed case and still operate correctly. You must use an explicit loop in your program to traverse the string character by character. ... so although built-in string operators are ok, the string methods are specifically disallowed.

Part 2 - Semordnilaps

As you all know very well, a semordnilap is a pair of words such that one backwards is the other forwards.  You know, like "gateman" and "nametag".  Or "diaper" and "repaid".  Just in case some of you didn't know this word, it comes from palindrome spelled backwards.  Now you know.  Maybe you knew of these peculiar pairs by one of their many other names, including volvograms, mynoretehs or anadromes.  No really, google it, I'm not making this stuff up. 

Anyhoo, your task now is to write a program that determines whether a pair of strings s and t are semordnilaps.  Again, you should have a boolean variable with a reasonable name that is set to true if the two strings have the desired property, and is false otherwise. Create a program called Semordnilaps.py.  This should read in two strings from the user and report whether one forwards is the other backwards. Again, this must not use the string methods and must traverse the strings character by character.

Part 3 - More practice with nested loops

Write a program named Pyramid.py that generates a pyramid of stars of height n.  For example, if n = 5, the output should look like

    *
   ***
  *****
 *******
*********

Note that you'll need to be printing spaces as well as stars so that things line up as they should.  You must use nested loops to print the pyramid -- so you are not allowed to use string multiplication to compute each line. The program should read in an integer from the user and generate a pyramid as given above. 

Part 4 - Image manipulation

Getting started

Recall that we can use our (extended) graphics library to load jpeg images and to manipulate the pixels in the image. We begin by importing the library:

from graphics import *

If we wish to create a blank picture image of a particular size, say 400 by 300, we could execute the following:

blankPic = Picture(400, 300)

This initialized a blank 400 x 300 image (or whatever dimensions you specified). 

Say, however, that we want to create an image that we can work with from an existing jpeg file located somewhere in our filesystem (i.e. in some folder on one of the disks we can access from the computer. We can accomplish that with code that looks like the following:

mediadir = '/Volumes/Shared/BRESSOUD/CS110-01/Resources/media/'
octopusFile = mediadir + 'octopus.jpg'

octopusPic = Picture(octopusFile)

In this example, there is a folder whose pathname is given by the string assigned to mediadir. In that folder is a jpeg image in a file named octopus.jpg. We use the Picture constructor to obtain an object instance of a Picture, just like the first example, but here the image is created from a disk file instead of being blank. Here we do not get to specify a size, because the size is already determined by the jpeg file. We can find out the size by querying the width and height of a Picture, like so:

width = octopusPic.getWidth()
height = octopusPic.getHeight()

Objects of the Picture class have methods (functions) for both retrieving the current color of a particular pixel, and for changing the color of a particular pixel. One can get the red, green, and blue intensities for a pixel with a single function invocation that returns an (r, g, b) tuple (named aColor) and assign the individual intensities to variables red, green, and blue as follows:

aColor = octopusPic.getPixel(100, 150)
(red, green, blue) = aColor

Here, aColor, red, green, and blue, are variable names of our own invention. We are invoking the gePpixel method of the octopusPic object instance and specifying the (100, 150) coordinate as arguments. The function returns a tuple with the red, green, and blue intensities (in the range 0 to 255).

If we want to set the color of a particular pixel, we need to specify three arguments, an x, y coordinate, along with a tuple specifying the (r, g, b) value:

redgreen = (255, 255, 0)
octopusPic.setPixel(125, 180, redgreen)

The following snippet of code would draw a horizontal line in a 256 by 100 image with increasing values of a grayscale color:

image1 = Picture(256, 100)

for i in range(256):

grayrgb = (i, i, i)
xval = i
image1.setPixel(xval, 50, grayrgb)

Try the above and make sure you understand how it works. Experiment with the abilities shown We can also save pictures that we have created using the save() method of the Picture class. To save the image1 Picture created by the code snippet above to a file on my desktop, I can simply do the following:

image1.save('/Users/bressoud/Desktop/mypic.jpg')

Note that in a blank picture, the save jpeg image will have black pixels for any pixel that has not been given a value, instead of the white shown in the display.

Part 5 - Interesting image manipulation

If you look in the course shared directory under Resources, you'll find a folder called media.  This contains a number of images. .  In this part, you'll be writing three programs.  The first will invert the colors of an image.  The second will desaturate an image.  And the last will posterize an image.  For example, if your initial image was something like

then the inverted, desaturated, and posterized versions will look respectively something like this:

You can use whatever images you want from that folder, just pick something you like. 

Invert.py

Write a program that inverts the color of each pixel in a given image and display the image to the screen.  An inverted image has each channel of each pixel "inverted".  That is, if the green value was 0, it is now 255.  If the green value was 1, it is now 254, and so on.

Grey.py

Write a program that makes a greyscale version of an image.  Recall that shades of grey are created by setting all three color channels equal.  Of course, picking arbitrary shades of grey for each pixel won't produce much of anything.  You'll want to set the grey value to match the average brightness of the pixels.  For example, if the pixel had a color given by (50, 50, 200) then the greyscale version should be (100, 100, 100), since (50 + 50 + 200)/3 = 100. 

Poster.py

Write a program to posterize an image.  Posterizing is done by reducing the number of colors the image uses in a natural way.  For example, currently the blue channel of any given pixel could have any integer value from 0 to 255.  To posterize the image, we might simplify things by only allowing 3 values of blue: perhaps if the blue is between 0 and 100, we set it to 50, if between 101 and 200 we set it to 150, and if between 201 and 255 we set it to 250.  If we were to do the same with red and green, then each color channel could only have 3 possible values, and thus the total number of possible colors would be 3 x 3 x 3 = 27.  This is in stark contrast to the millions of colors that were originally possible.  This creates a posterized effect (the way old posters looked when there were only a few colors that could be printed at any one time). 

Play around with how many colors you allow of any particular channel, how you vary the ranges of colors, and how you pick what each range gets mapped to.  Find something you like.  The most natural way to set colors would be with if or if-else statements, although you could also use modular arithmetic if you're just planning on having regular intervals of color.

Part 7 - Uploading your lab

At this point your folder called Lab06 should contain: