简体   繁体   中英

Converting images to grey scale in Python

class PPM(object):
    def __init__(self, infile, outfile):
        self.infile=infile
        self.outfile=outfile

        #Read in data of image
        data= open(self.infile,"r")
        datain=data.read()
        splits=datain.split()

        #Header info
        self.type=splits[0]
        self.columns=splits[1]
        self.row=splits[2]
        self.colour=splits[3]
        self.pixels=splits[4:]

    def greysscale():
            for row in range(rows):
                for column in range(columns):
                    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))


    def flattenred():
        for colour in range (0,len(self.pixels),3):
            self.pixels [colour]=str[0]

        return picture

    def writetofile(self):
        dataout= open(self.outfile,"w")
        dataout.write(self.type +"\n" + self.columns + "\n" + self.row +"\n"+ self.colour +"\n"+ " ".join (self.pixels))


sample= PPM("cake.ppm", "Replica.ppm")
sample.writetofile()

I am having trouble writing a function called grey_scale which will change the picture into a grey scale image by averaging the values of all three color numbers for a pixel, the red, green and blue, and then replacing them all by that average.
So if the three colors were 25, 75 and 250, the average would be 116, and all three numbers would become 116.
How would I do this?

You've got the hard part right, there are just a lot of other little things you need to deal with.

Your first problem is that you never actually call the greysscale function anywhere, so whatever you put there won't do any good. Most likely you wanted something like this at the end:

sample = PPM("cake.ppm", "Replica.ppm")
sample.greysscale()
sample.writetofile()

You've also misspelled grey_scale , both leaving out the _ and adding an extra s , so if your teacher is a stickler you might get marked off for that.

Your next problem is that a method has to take a self parameter. You've done this properly for __init__ and writetofile ; you just need to do the same thing here.

Next, you're trying to use variables rows and columns and image that don't exist anywhere. You have similar values available as self.row , self.columns , and self.pixels , but you have to use the values you actually have, not similar ones.

self.row and self.columns are strings, not numbers; you need to convert them with int . While we're at it, it's much clearer to call the first one self.rows .

And pixels seems to be an array of strings, separated on spaces. That's actually not useful at all. If you look at a PPM file, after the first three lines, it's just raw binary data. Any spaces in there just mean that some color happens to have the value 32, which isn't exactly meaningful. So you need to only split off the first four values, then leave the rest alone as one big string of bytes.

You definitely can't call methods like getPixel or setPixel on that string. It's just a bunch of bytes; it has no idea what that means. Each pixel is three bytes, one per color; the columns just come one right after the other, and the rows one right after the other. So, to get the pixel at row, column , the red color is at row * self.columns * 3 + column * 3 , and the green and blue are the next two. You can use a slice to get all three bytes at once. But, since this is just a string of bytes, each of those will be a character; you need to call ord on them to get the byte numbers, and then chr to turn them back. Plus, you're not allowed to mutate a string in-place. But there's a nice trick we can use to fix all of those problems—a bytearray is like a string, except that it's mutable, and its elements are numbers instead of single-byte strings.

Meanwhile, you want to use "".join , not " ".join , or you'll add an extra space between each byte, which will break the file. But you really don't need that—it's already a bytearray , which can be used like a string.

And finally, once you've got all those individual split bits as integers instead of strings, you can't just concatenate them anymore. It'll be a lot easier to do this with format than to manually convert them back to strings just to concatenate them up. Also, PPM files generally put a space, not a newline, between the rows and columns.

While we're at it, you need to close the files you open—especially for files you're writing; otherwise, there's no guarantee that the last block of data will ever get flushed to disk—and you should open binary files in binary mode.

So:

class PPM(object):
    def __init__(self, infile, outfile):
        self.infile=infile
        self.outfile=outfile

        #Read in data of image
        data= open(self.infile,"r")
        datain=data.read()
        splits=datain.split(None, 4)

        #Header info
        self.type=splits[0]
        self.columns=int(splits[1])
        self.rows=int(splits[2])
        self.colour=int(splits[3])
        self.pixels=bytearray(splits[4])

    def grey_scale(self):
            for row in range(self.rows):
                for column in range(self.columns):
                    start = row * self.columns * 3 + column * 3
                    end = start + 3
                    r, g, b = self.pixels[start:end]
                    brightness = int(round(0.299 * r + 0.587 * g + 0.114 * b))
                    self.pixels[start:end] = brightness, brightness, brightness

    def writetofile(self):
        dataout= open(self.outfile, "wb")
        dataout.write('{}\n{} {}\n{}\n{}'.format(self.type, 
                                                 self.columns, self.rows, 
                                                 self.colour, 
                                                 self.pixels))

sample = PPM("cake.ppm", "Replica.ppm")
sample.grey_scale()
sample.writetofile()

If you want to use a different brightness formula, that's easy—just change the line that computes the brightness, like this:

brightness = int(round((r+g+b)/3.0))

If you actually have plain-PPM files rather than normal PPM files (in which case… wow, I've never seen one in the wild), then you were closer on track with your parsing code, but still missing one key element.

You can go back to splits = detain.split() , then splits[4:] will be a sequence of all of the pixel-color values… but it will be a sequence of those pixel-color values as strings . If you want them as integers, you need to call int on each one, which you can do with a list comprehension or a map call, like:

self.pixels=map(int, splits[4:])

Then you've got a sequence of numbers, just like a bytearray , so all of that code can be the same… up until output, where you want to convert them back into whitespace-separated strings to create a new plain-PPM. Your original join almost works, except that you can't join integers; you have to convert them back to strings first. Again, you can do this by using map on the str function:

pixelstring = " ".join(map(str, self.pixels))
dataout.write('{}\n{} {}\n{}\n{}'.format(self.type, 
                                         self.columns, self.rows, 
                                         self.colour, 
                                         pixelstring))

I see you are doing a YCrCb conversion in your line:

brightness = int(round(0.299 * r + 0.587 * g + 0.114 * b))

Just change this to:

brightness = int(round( (r + g + b) / 3 ))

Edit

I should add that the way you have is actually a better way to do the conversion (although technically you are creating luminance and not brightness). The result is more in tune with how the human eye perceives grays. Here is a fairly easy to read link on the subject - http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/ You can see how the luminosity conversion looks better. The conversion you were using (YCrCb) is close to this, unfortunately you will have to get someone with more expertees to tell you the exact difference though.

Edit2

Just looking at @abarnert's answer, I didn't realise what you had was a full program. You should follow his advice for improving it as a whole.

rom_file= [0,3,1]
#Main Function which adds s dots and xs to the deck list(s) depending on the data input file
def main():
    #Container for the output of the program, each nested list contains one row of the output
    decks = [[], [], [], [], []]
    #list that contains the number of empty rows for inputs 1-5(location of input given by [each - 1])
    empty_rows = [4, 3, 2, 1, 0]
    #Scan through each element of the list
    for each in from_file:
        #If the element 'each' is equal to 0, append a single dot to all 5 rows
        if each == 0:
            for i in range(5):
                decks[i].append('.')
        #If the input is in the range 1-5, define variables and the nested for loops
        else:
            #Maximum width of each pyramid
            max = (each * 2) - 1
            half_dots = int((max - 1) / 2)
            base_x = 1
            loc = each - 1
            #For loop that appends the max. number of dots to rows depending on data in empty_rows
            for every in range(empty_rows[loc]):
                decks[every].append(max * '.')
            #Primary for loop; appends the dots and xs to any row not covered by the previous loop (ALl rows that do not already have max dots) for each between 1-5
            for i in range(each):
                decks[i + empty_rows[loc]].append(half_dots * '.')
                decks[i + empty_rows[loc]].append(base_x * 'x')
                decks[i + empty_rows[loc]].append(half_dots * '.')
                half_dots -= 1
                base_x += 2
    #A loop that print out the results
    for each in decks:
        text = ""
        for i in each:
            text += i
        print(text)


#Starts the program by calling the main function
main()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM