【问题标题】:Converting images to grey scale in Python在 Python 中将图像转换为灰度
【发布时间】:2014-01-10 01:02:51
【问题描述】:
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()

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

【问题讨论】:

  • 我假设您不允许为此使用 PIL/Pillow,对吧?
  • 我在这里给出的答案有什么问题? stackoverflow.com/questions/21031546/…
  • @M4rtini- 我什么都不懂,也不允许我使用 PIL/Pillow/numpy,我只有 12 岁,所以我对 Python 和图像编辑没有深入的了解

标签: python html css image image-processing


【解决方案1】:

困难部分你已经做好了,还有很多其他的小事情你需要处理。

您的第一个问题是您实际上从未在任何地方调用greysscale 函数,因此无论您放在那里都不会有任何好处。很可能你最后想要这样的东西:

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

您还拼错了grey_scale,既漏掉了_,又添加了一个额外的s,所以如果您的老师是个固执己见的人,您可能会因此而被打分。

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

接下来,您尝试使用在任何地方都不存在的变量 rowscolumnsimage。您有相似的值,如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))

【讨论】:

  • 我需要使用公式亮度 = int(round( (r + g + b) / 3 ))
  • 如何更改灰度公式,使亮度 = int(round( (r + g + b) / 3 ))
  • @abarnert- 另外,在 writetofunction 中,在 dataout.write 行上为什么要插入 {}。你在 dataout.write 行做了什么
  • @abarnert 为他的作业给出的图像,来自他在第一个问题中给出的链接,实际上确实将像素作为一串由空格分隔的值。它似乎是一个普通的 PPM,定义如下:netpbm.sourceforge.net/doc/ppm.html
  • @mmA 用于亮度公式,用您写的内容替换该行应该足够了。只需将末尾的 3 更改为 3.0 即可获得浮点除法。 write 函数中的那些 {} 被 format 函数的参数替换。
【解决方案2】:
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()

【讨论】:

    【解决方案3】:

    我看到您正在对您的行进行 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) 与此接近,但不幸的是,您必须让具有更多专家的人告诉您确切的区别。

    编辑2

    只看@abarnert 的回答,我没有意识到你拥有的是一个完整的程序。您应该按照他的建议进行整体改进。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-18
      • 2021-06-06
      • 2012-08-25
      • 2010-11-20
      相关资源
      最近更新 更多