在Python中将图像转换为灰度

时间:2014-01-10 01:02:52

标签: python html css image image-processing

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()

我在编写一个名为grey_scale的函数时遇到问题,该函数会通过对像素(红色,绿色和蓝色)的所有三个颜色数值进行平均,然后将它们全部替换为平均值,将图片更改为灰度图像。
因此,如果三种颜色分别为25,75和250,则平均值为116,并且所有三个数字都将变为116 我该怎么做?

3 个答案:

答案 0 :(得分:1)

你有 hard 部分,你需要处理很多其他小事。

你的第一个问题是你从来没有在任何地方实际调用greysscale函数,所以无论你放在哪里都没有任何好处。最有可能你最终想要这样的东西:

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

你也拼错了grey_scale,两人都遗漏了_并添加了额外的s,所以如果你的老师是个坚持者,你可能会被标记出来。

您的下一个问题是方法必须采用self参数。您已为__init__writetofile正确完成此操作;你只需要在这里做同样的事情。

接下来,您尝试使用在任何地方都不存在的变量rowscolumns以及image。您有{em>类似的值可用self.rowself.columnsself.pixels,但您必须使用实际拥有的值,而不是类似的值。

self.rowself.columns是字符串,而不是数字;您需要使用int转换它们。当我们接触它时,可以更清楚地调用第一个self.rows

pixels似乎是一个字符串数组,以空格分隔。这根本没用。如果您查看PPM文件,在前三行之后,它只是原始二进制数据。那里的任何空格只是意味着某些颜色恰好具有值32,这不是完全有意义的。因此,您只需要拆分前四个值,然后将其余值作为一个大字节串。

您绝对无法在该字符串上调用getPixelsetPixel等方法。它只是一堆字节;它不知道这意味着什么。每个像素是三个字节,每种颜色一个;这些列只是一个接着一个接一个,而行一个接一个接一个。因此,要将像素设为row, column,红色为row * self.columns * 3 + column * 3,绿色和蓝色为下一个。您可以使用切片一次获取所有三个字节。但是,因为这只是一串字节,所以每个都是一个字符;你需要在它们上面调用ord来获取字节数,然后chr将它们调回来。另外,你不允许你就地改变一个字符串。但是我们可以使用一个很好的技巧来解决所有这些问题 - bytearray就像一个字符串,除了它是可变的,它的元素是数字而不是单字节字符串。

同时,您希望使用"".join,而不是" ".join,或者您将在每个字节之间添加额外的空格,这将打破文件。但你真的不需要它 - 它已经是bytearray,可以像字符串一样使用。

最后,一旦你将所有那些单独的分割位作为整数而不是字符串,你就不能再将它们连接起来了。使用format执行此操作要比将它们手动转换回字符串只是为了将它们连接起来要容易得多。此外,PPM文件通常在行和列之间放置一个空格而不是换行符。

当我们接触它时,你需要close你打开的文件 - 特别是你正在写的文件;否则,无法保证最后一个数据块将被刷新到磁盘 - 您应该以二进制模式打开二进制文件。

所以:

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()

如果你想使用不同的亮度公式,这很简单 - 只需改变计算亮度的线,如下所示:

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

如果你实际上有普通的PPM文件而不是普通的PPM文件(在这种情况下......哇,我从来没有见过一个),那么你的解析代码更接近正轨,但仍然缺少一个密钥元件。

您可以返回splits = detain.split(),然后splits[4:]将是所有像素颜色值的序列...但它将是这些像素颜色值的序列作为字符串。如果您希望它们作为整数,则需要在每个上面调用int,您可以使用列表理解或map调用来执行此操作,例如:

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

然后你有一系列数字,就像bytearray一样,所以所有代码都可以相同......直到输出,你要将它们转换回空白分隔的字符串来创建一个新的普通PPM。您的原始join几乎可以正常工作,但您不能加入整数;你必须先将它们转换回字符串。同样,您可以在str函数上使用map来执行此操作:

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

答案 1 :(得分:0)

我看到你正在进行YCrCb转换:

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

只需将其更改为:

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

修改

我应该补充说,你拥有的方式实际上是一种更好的转换方式(虽然从技术上讲,你创造的是亮度,而不是亮度)。结果更符合人眼感知灰色的方式。这是一个相当容易阅读的主题链接 - http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/您可以看到光度转换看起来更好。您使用的转换(YCrCb)接近于此,不幸的是,您必须让更多专家来告诉您确切的差异。

EDIT2

只是看看@ abarnert的回答,我没有意识到你所拥有的是一个完整的程序。你应该遵循他的建议来改善它。

答案 2 :(得分:0)

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()