-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimages.py
executable file
·306 lines (215 loc) · 9.33 KB
/
images.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
"""
*****************************************************************************
FILE: images.py
AUTHOR: Lucas Barusek
DATE: 10/20/18
DESCRIPTION: Print and image by converting a list of ppm data to a list
of tuples Yertle the turtle can draw. This program will
also be able to print the image in grayscale, negative, and
print it blurry by modifying the list of red, green, blue
pixel values.
*****************************************************************************
"""
import turtle
import sys # for giving file name on command line
def read_file_lines(filename):
""" Opens and reads a file. Returns a list of lines from the file. """
# make sure we can open the file for reading
file = open(filename)
assert file
# Get all the lines and remove the trailing newlines
lines = file.readlines()
file.close()
for i in range(len(lines)):
lines[i] = lines[i][:-1]
return lines
def ppm_data_to_image(image):
""" Takes the ppm date, and converts it to a nested list of tuples
suitable to be used in draw_image """
# defines a list to compile the red, green, blue tuples
triples_list = []
# finds the width dimension of the image (the length dimension
# will come automatically in the for loop)
width = int(image[2][:image[2].index(' ')])
#splices off all non red, green, blue values
image = image[4:]
# While loop iterates for as long as there is three or more numbers
# in the ppm data.
while len(image) >= 3:
# creates a new row each time the while loop iterates
one_row = []
# for loop iterates for the length of the list, which will
# compile a nested list in accordance with the dimensions
# of the image
for _ in range(width):
# appends the first three items in the ppm data to one_row as
# a tuple, and then splices off the first three items in ppm data
one_row.append((int(image[0]), int(image[1]), int(image[2])))
image = image[3:]
# appends each row to the triples list to create the nested list
triples_list.append(one_row)
return triples_list
def draw_image(yertle, image):
""" From the nested list of tuples, yertle the turtle will draw
the image from each red, green and blue value in each tuple. """
# creates a nested for loop that iterates for each row and column
# in the image
for i in range(len(image)):
for j in range(len(image[i])):
# Yertle will draw each pixel, and then move forward one pixel
yertle.dot(1, image[i][j][0], image[i][j][1], image[i][j][2])
yertle.forward(1)
# resets Yertle to the left side of the image, and moves it down
# one row to draw another column
yertle.backward(len(image[0]))
yertle.right(90)
yertle.forward(1)
yertle.left(90)
def grayscale(image):
""" Converts the image to Grayscale by taking the average of the red,
green, and blue values in each tuple """
# Defines a list to compile each row of the grayscale image
grayscale_image = []
# iterates through the length of the image
for i in range(len(image)):
# creates a new row
one_row = []
# iterates for the width of the image
for j in range(len(image[i])):
# Calculates the average of the red, green, and blue, values, and
# converts every number in the tuple to that number
new_pixel = (image[i][j][0] + image[i][j][1] + image[i][j][2]) // 3
one_row.append((new_pixel, new_pixel, new_pixel))
# appends each row to the grayscale image
grayscale_image.append(one_row)
return grayscale_image
def negative(image):
"""Convert the image to negative to by subtracting each red, green
and blue value from 255 to get its negative value. """
# defines a list to compile each row of the negative image
negative_image = []
# iterates through the length of the image
for i in range(len(image)):
# creates a new row
one_row = []
# iterates for length of the column
for j in range(len(image[i])):
# appends the negative pixel by subtracting the red, green
# and blue values from 255
one_row.append((255 - image[i][j][0],
255 - image[i][j][1],
255 - image[i][j][2]))
# appends each row to negative image
negative_image.append(one_row)
return negative_image
def calculate_averages(pixel_neighbors):
""" From the list of neighbors, takes the average of each red,
green, and blue value and returns the averaged pixel"""
# defines variable to be used to calculate the sum of the red,
# green, ane blue color values
red_sum = 0
green_sum = 0
blue_sum = 0
# iterates through all the neigbors in the pixel_neighbors list
for num in pixel_neighbors:
# indexes each tuple at the 0, 1, and 2, to isolate the red,
# green, and blue color values, and adds them up independently
red_sum += num[0]
green_sum += num[1]
blue_sum += num[2]
# Calculates the average red, green, and blue values by dividing
# the red, green, and blue sums by the amount of neighbors
average_red = red_sum // len(pixel_neighbors)
average_green = green_sum // len(pixel_neighbors)
average_blue = blue_sum // len(pixel_neighbors)
return (average_red, average_green, average_blue)
def is_in_bounds(image, row, col):
"""Checks if the location of the neighbor is in bounds"""
# if statement checks if neighbor is out bounds and returns
# false if it is
if row < 0 or row >= len(image) or \
col < 0 or col >= len(image[0]):
return False
#returns true if neighbor is in bounds
return True
def search_neighbors(row, col, image):
"""For each pixel, searches in all directions for all neighboring
pixels, and if they are within the bounds of the image, returns
the red, green, and blue color values of that pixel"""
# defines all the directions to search for, including the
# starting position
directions = [(0, 0), (-1, 0), (-1, 1), (0, 1), (1, 1),
(1, 0), (1, -1), (0, -1), (-1, -1)]
# defines an empty list that will later compile all the neighbors
neighbor_list = []
# Makes a copy of the row and column as to not modify the original
new_row = row
new_col = col
# for loop iterates for every possible direction a neighbor could be,
# and isolates the row direction and the column direction
for direction in directions:
row_shift = direction[0]
col_shift = direction[1]
# applies the row and column shift to the row and column of
# the location of the pixel
neighbor_row = new_row + row_shift
neighbor_col = new_col + col_shift
# Calls on is_in_bounds to check if the neighor is in the
# bounds of the image. If it is, appends the red, green, blue
# tuple to neighbor_list
if is_in_bounds(image, neighbor_row, neighbor_col):
neighbor_list.append((image[neighbor_row][neighbor_col]))
return neighbor_list
def blur(image):
"""Makes the image blurry by making each pixel the average of each
neighboring pixel and itself"""
# Cite: Peers - Truong Pham and Denzel Capellan
# Descr: Explained how blur should be doing its job
# creates a list to compile each row of the blurred image
blur_list = []
# iterates through the length of the image
for i in range(len(image)):
# creates a new row
one_row = []
# iterates through the width of the image
for j in range(len(image[i])):
# calls on search_neighbors to get a list of all the neighboring
# pixels and the pixel itself
neighbors = search_neighbors(i, j, image)
# Calls on calculate_averages to find the average of all the
# pixels in neighbors
averages = calculate_averages(neighbors)
# appends each column
one_row.append(averages)
# appends each row
blur_list.append(one_row)
return blur_list
#------------------------------------------------------------------------
# Main function
#------------------------------------------------------------------------
def main():
"""This is the Main Function"""
# Load the picture data from the file given on
# the command line.
if len(sys.argv) != 2:
print("usage: python3 draw_picture.py FILENAME")
sys.exit(1)
filename = sys.argv[1]
picture = ppm_data_to_image(read_file_lines(filename))
# Create the turtle/window, and turn off tracing
yertle = turtle.Turtle()
turtle.tracer(False)
# Move turtle to upper left corner
yertle.up()
yertle.goto(-390, 340) # coordinates are a little weird
yertle.speed(0)
# Apply one or more manipulations, if desired:
picture = negative(picture)
picture = grayscale(picture)
for _ in range(60): # really blurry!
picture = blur(picture)
# # Draw it!
draw_image(yertle, picture)
turtle.mainloop()
if __name__ == "__main__":
main()