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.
Как мне это сделать?
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()
У тебя тяжелая часть права, есть много других мелочей, с которыми тебе нужно иметь дело.
Ваша первая проблема заключается в том, что вы никогда не называете функцию greysscale
нигде, поэтому все, что вы там ставите, не принесет никакой пользы. Скорее всего, вам нужно что-то подобное в конце:
sample = PPM("cake.ppm", "Replica.ppm")
sample.greysscale()
sample.writetofile()
Вы также ошибочно grey_scale
, оба оставляли _
и добавляли дополнительные s
, поэтому, если ваш учитель является наклейкой, вы можете быть выделены для этого.
Ваша следующая проблема заключается в том, что метод должен принимать параметр self
. Вы сделали это правильно для __init__
и writetofile
; вам просто нужно сделать то же самое здесь.
Далее, вы пытаетесь использовать переменные rows
и columns
и image
, которые не существуют в любом месте. У вас есть аналогичные значения, доступные как self.row
, self.columns
и self.pixels
, но вы должны использовать значения, которые у вас есть, а не похожие.
self.row
и self.columns
- это строки, а не числа; вам нужно преобразовать их с помощью int
. Пока мы на нем, гораздо яснее назвать первый self.rows
.
И pixels
кажутся массивом строк, разделенных на пробелы. Это на самом деле не полезно вообще. Если вы посмотрите на файл PPM, то после первых трех строк это просто сырые двоичные данные. Любые пробелы просто означают, что какой-то цвет имеет значение 32, что не имеет особого значения. Таким образом, вам нужно только отделить первые четыре значения, а затем оставить остальных в качестве одной большой строки байтов.
Вы определенно не можете вызывать такие методы, как getPixel
или setPixel
для этой строки. Это всего лишь куча байтов; он понятия не имеет, что это значит. Каждый пиксель имеет три байта, по одному на цвет; столбцы просто идут один за другим, а строки - один за другим. Итак, чтобы получить пиксель в 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
почти работает, за исключением того, что вы не можете присоединиться к целым числам; вы должны сначала преобразовать их в строки. Опять же, вы можете сделать это, используя map
на функции str
:
pixelstring = " ".join(map(str, self.pixels))
dataout.write('{}\n{} {}\n{}\n{}'.format(self.type,
self.columns, self.rows,
self.colour,
pixelstring))
Я вижу, что вы делаете преобразование 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), близко к этому, к сожалению, вам нужно будет заставить кого-то с более опытными сказать вам точную разницу.
Просто глядя на ответ @abarnert, я не понимал, что у вас была полная программа. Вы должны следовать его совету для улучшения его в целом.
PIL
/Pillow
для этого, верно?