views:

100

answers:

2

I'm trying to do an exercise in John Zelle's "Python Programming: An Introduction to Computer Science". I downloaded a special graphics package for his book (graphics.py, which is on the linked website). The question reads as follows:

Write a program that converts a color image to grayscale. The user supplies the name of a file containing a GIF or PPM image, and the program loads the image and displays the file. At the click of the mouse, the program converts the image to grayscale. The user is then prompted for a filename to store the grayscale image in.

You will probably want to go back and review the Image object from the graphics library (Section 4.8.4). The basic idea for converting the image is to go through it pixel by pixel and convert each one from color to an appropriate shade of gray. A gray pixel created by setting its red, green, and blue components to have the same brightness. So, color_rgb(0, 0, 0) is black, color_rgb(255, 255, 255) is white, and color_rgb(127, 127, 127) is a gray "halfway" in between. You should use a weighted average of the original rgb values to determine the brightness of the gray. Here is the pseudocode for the grayscale algorithm [for some reason the four-space indent is not working on preview]:

for each row in the image:  
    for each column in the image:  
        r, g, b = get pixel information for current row and column  
        brightness = int(round(0.299r + 0.587g + 0.114b))  
    update the image # to see progress row by row

Note: the pixel operations in the Image class are rather slow, so you will want to use relatively small images (not 12 megapixels) to test your program.


I've been working on this for hours. This is the latest version of my code:

# grayscale.py

from graphics import *

def main():  
    infileName = input("File name: ")  
    outfileName = input("Save to: ")

    image = Image(Point(100,100), infileName)
    width = image.getWidth()
    height = image.getHeight()
    win = GraphWin("rgb")
    image.draw(win)

    row = 0
    column = 0

    win.getMouse()

    for row in range(200):
        for column in range(200):
            r, g, b = image.getPixel(row, column)
            brightness = int(round(0.299 * r + 0.587 * g + 0.114 * b))
            image.setPixel(row, column, color_rgb(brightness, brightness, brightness))
            win.update()

    win.getMouse()
    win.close()

main()

I finally got it so Python's not giving me any error messages. But all the program does is load the image, take a couple of mouse clicks, and then close. I've been entering the input file as U:\My Pictures\yay.gif, and the output file as U:\My Pictures\yay2.gif. But I just searched my computer and U:\My Pictures\yay2.gif doesn't exist. What am I doing wrong? This is NOT for a class by the way--there's no instructor for me to ask.

Maybe I should follow up in the post instead. I added the save function, but I got an image with a 200x200 grayscale box and the rest of it in color. So, here are some lines I changed:

win = GraphWin("rgb", width, height)
for row in range(height):
    for column in range(width):

And I get the following error message: Traceback (most recent call last):
File "C:\Python31\grayscale.py", line 31, in
main()
File "C:\Python31\grayscale.py", line 22, in main
r, g, b = image.getPixel(row, column)
File "C:\Python31\lib\graphics.py", line 810, in getPixel
value = self.img.get(x,y)
File "C:\Python31\lib\tkinter_init_.py", line 3301, in get
return self.tk.call(self.name, 'get', x, y)
_tkinter.TclError: pyimage1 get: coordinates out of range

I understand that I probably need to change the anchor point of the image to the center. But I can only determine that by the image's width and height, and I have to have uploaded the image first to get those values. Is there a workaround?

+1  A: 

You aren't saving the image. Notice that the variable outfileName is never used. You should be passing it to some sort of save function. took a quick look at graphics.py, I think this is what you need:

EDIT

To solve the issue with it only converting one corner, do this instead, your old code was only converting the first 200x200 pixels. I also changed range to xrange, not necessary but more efficient.

for row in xrange(height):
        for column in xrange(windth):
mikerobi
Thanks. I tried that--I got a new image saved where it should be, but the only a 200x200 box in the corner was converted to grayscale. So I changed the fourth line in the second block to <code>win = GraphWin("rgb", width, height)</code> and the ranges in the for loops to height and width, respectively. I got this error:
Traceback (most recent call last): File "C:\Python31\grayscale.py", line 31, in <module> main() File "C:\Python31\grayscale.py", line 22, in main r, g, b = image.getPixel(row, column) File "C:\Python31\lib\graphics.py", line 810, in getPixel value = self.img.get(x,y) File "C:\Python31\lib\tkinter\__init__.py", line 3301, in get return self.tk.call(self.name, 'get', x, y)_tkinter.TclError: pyimage1 get: coordinates out of range
@user460847, updated to address the issue with the 200x200 box.
mikerobi
@mikerobi: In Python 3 `range` is an iterator like `xrange` in Python 2...so there's no need to change it. In fact I think `xrange` was removed in Python 3, so using `range` is more portable. Also, it's not always the most efficient thing to use. See [Should you always favor xrange() over range()?](http://stackoverflow.com/questions/135041/should-you-always-favor-xrange-over-range)
martineau
@mikerobi: I changed both ranges to xrange and got an error message (global name not defined). Also, was there something else you suggested? You say "do this instead" to fix the 200x200 issue but then you don't say what to do. Maybe it just didn't show up in your comment?
A: 

I don't see any use of outfileName after it's set -- in other words, you appear to never, ever save the result to that file (or any other file, for that matter).

Alex Martelli