在Python中将图像转换为灰度
我在编写一个名为grey_scale的函数时遇到问题,该函数将通过平均一个像素(红色、绿色和蓝色)的所有三个颜色数字的值,然后将它们全部替换为该平均值,从而将图片更改为灰度图像。在Python中将图像转换为灰度,python,html,css,image,image-processing,Python,Html,Css,Image,Image Processing,我在编写一个名为grey_scale的函数时遇到问题,该函数将通过平均一个像素(红色、绿色和蓝色)的所有三个颜色数字的值,然后将它们全部替换为该平均值,从而将图片更改为灰度图像。 因此,如果三种颜色分别为25、75和250,则平均值为116,三个数字都将变为116。 我该怎么做 我看到您的产品线正在进行YCrCb转换: class PPM(object): def __init__(self, infile, outfile): self.infile=infile
因此,如果三种颜色分别为25、75和250,则平均值为116,三个数字都将变为116。
我该怎么做 我看到您的产品线正在进行YCrCb转换:
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()
只需将此更改为:
brightness = int(round(0.299 * r + 0.587 * g + 0.114 * b))
编辑
我应该补充的是,你的方式实际上是一个更好的转换方式(虽然从技术上讲,你是创造亮度,而不是亮度)。结果更符合人眼对灰色的感知。这是一个关于这个主题的相当容易阅读的链接——你可以看到亮度转换看起来更好。您使用的转换(YCrCb)接近于此,但不幸的是,您将不得不让更专业的人告诉您确切的区别
编辑2
仅仅看@abarnert的回答,我没有意识到你有一个完整的程序。你应该听从他的建议,从整体上改进它。你把困难的部分做对了,还有很多其他的小事情需要处理
你的第一个问题是你从来没有在任何地方调用过greyscale
函数,所以不管你放在那里什么都不会有任何好处。最有可能的情况是,您希望在最后得到类似的结果:
brightness = int(round( (r + g + b) / 3 ))
你还拼错了灰度
,既省略了,又增加了一个额外的s
,因此如果你的老师是一个固执的人,你可能会因此被扣分
下一个问题是方法必须采用self
参数。对于\uuuu init\uuuu
和writetofile
,您已经正确地完成了这项操作;你只需要在这里做同样的事情
接下来,您将尝试使用任何地方都不存在的变量行
和列
和图像
。您可以使用类似的值,如self.row
、self.columns
和self.pixels
,但必须使用实际拥有的值,而不是类似的值
self.row
和self.columns
是字符串,而不是数字;您需要使用int
转换它们。当我们这样做时,调用第一个self.rows
会更清楚
而像素
似乎是一个字符串数组,以空格分隔。这实际上一点用都没有。如果您查看PPM文件,在前三行之后,它只是原始二进制数据。其中的任何空格都意味着某些颜色恰好具有值32,这并不完全有意义。因此,您只需要拆分前四个值,然后将其余值单独作为一个大的字节字符串
您绝对不能对该字符串调用像getPixel
或setPixel
这样的方法。它只是一堆字节;它不知道这意味着什么。每个像素是三个字节,每种颜色一个;列一列接一列,行一行接一行。因此,要获得行、列
处的像素,红色在行*self.columns*3+列*3
处,绿色和蓝色是下两个。您可以使用片一次获取所有三个字节。但是,因为这只是一个字节串,所以每个字节都是一个字符;您需要对它们调用ord
以获取字节数,然后调用chr
将其返回。另外,不允许在适当的位置对字符串进行变异。但是我们可以用一个很好的技巧来解决所有这些问题,bytearray
就像一个字符串,只是它是可变的,它的元素是数字而不是单字节字符串
同时,您希望使用“”。join
,而不是“”。join
,否则将在每个字节之间添加额外的空格,这将中断文件。但是你真的不需要它已经是一个bytearray
,它可以像字符串一样使用
最后,一旦你把所有的分割位都作为整数而不是字符串,你就不能再把它们串联起来了。使用格式
执行此操作要比手动将它们转换回字符串以将它们串联起来容易得多。此外,PPM文件通常在行和列之间放置空格,而不是换行符
当我们进行此操作时,您需要关闭您打开的文件,尤其是您正在编写的文件;否则,无法保证最后一块数据会被刷新到磁盘,您应该以二进制模式打开二进制文件
因此:
如果要使用不同的亮度公式,只需更改计算亮度的行即可,如下所示:
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()
如果您实际上有普通的PPM文件而不是普通的PPM文件(在这种情况下……哇,我从来没有在野外见过),那么您的解析代码就更接近正轨了,但仍然缺少一个关键元素
您可以返回到splits=retain.split()
,然后splits[4:]
将是所有像素颜色值的序列…但它将是这些像素颜色值作为字符串的序列。如果希望将它们作为整数,则需要调用每个整数,这可以通过列表理解或调用来实现,如:
brightness = int(round((r+g+b)/3.0))
然后你有一个数字序列,就像一个bytearray
,所以所有的代码可以是相同的…直到输出,你想把它们转换回以空格分隔的字符串来创建一个新的普通PPM。您原来的join
几乎可以正常工作,只是不能将整数连接起来;您必须首先将它们转换回字符串。同样,您可以在函数上使用map
:
self.pixels=map(int, splits[4:])
我想你不允许用这个,对吧?我给你的答案有什么问题@M4rtini-我一点都不懂,也不允许我使用PIL/枕头/
self.pixels=map(int, splits[4:])
pixelstring = " ".join(map(str, self.pixels))
dataout.write('{}\n{} {}\n{}\n{}'.format(self.type,
self.columns, self.rows,
self.colour,
pixelstring))
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()