-
Notifications
You must be signed in to change notification settings - Fork 0
/
ppm.py
208 lines (144 loc) · 5.47 KB
/
ppm.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
[email protected]:szabba/automato.git#!/usr/bin/env python2
import StringIO
class Color(object):
"""Abstract base class for Colors."""
def __init__(self, scale):
self.scale = scale
def get_rgb(self):
raise NotImplementedError("""All end-use subclasses of Color should define
a get_rgb method.""")
def get_scale(self):
"""Half-abstract method. A scale attribute must be set first."""
if not hasattr(self, "scale"):
raise NotImplementedError("""All end-use subclasses of Color should define
a get_scale method.""")
return self.scale
class RGBColor(Color):
"""RGB color."""
def __init__(self, scale, rgb):
"""Create a color based on an RGB tuple."""
super(RGBColor, self).__init__(scale)
self.rgb = rgb
def get_rgb(self):
return self.rgb
class GrayscaleColor(Color):
"""Grayscale color."""
def __init__(self, scale, gray):
"""Create a color based on an grayscale color."""
super(GrayscaleColor, self).__init__(scale)
self.gray = gray
def get_rgb(self):
return tuple([self.gray] * 3)
class BinaryColor(Color):
"""Binary color."""
def __init__(self, boolee):
"""Create a color based on an boolable value color."""
super(BinaryColor, self).__init__(1)
self.boolee = bool(boolee)
def get_rgb(self):
return tuple([int(self.boolee)] * 3)
class PixelSource(object):
"""Abstract base class for PixelSources.
Your classes need not derive from it, though it'd be nic if they did,
because:
-> you only need to implement part of the methods then
-> it raises exceptions when you don't override some of it's methods
(which you ought to override).
You see - it's for your own good!"""
def get_width(self):
if not hasattr(self, 'w'):
raise NotImplementedError("""A PixelSource needs to implement a get_width
method.""")
return self.w
def get_height(self):
if not hasattr(self, 'h'):
raise NotImplementedError("""A PixelSource needs to implement a
get_height method.""")
return self.h
def get_size(self):
return (self.get_width(), self.get_height())
def pixels(self):
raise NotImplementedError("""A PixelSource needs to implement a pixels
method.""")
class PixelMatrix(PixelSource):
def __init__(self, width, height, default=BinaryColor(False)):
self.matrix = [[default for i in range(width)] for j in range(height)]
self.w = width
self.h = height
def set_at(self, pos, color):
i, j = pos
self.matrix[i][j] = color
def get_at(self, pos):
i, j = pos
return self.matrix[i][j]
def pixels(self):
for row in self.matrix:
for pixel in row:
yield pixel
class PAnyMImage(object):
"""Abstract base class for P*MImages."""
def __init__(self, pixel_source, scale=255):
self.pixel_source = pixel_source
self.scale = scale
def get_header(self):
raise NotImplementedError("""A P*Image must implement a get_header
method.""")
def format_pixel(self):
raise NotImplementedError("""A P*Image must implement a format_pixel
method.""")
def get_scale(self):
return self.scale
def get_content(self):
c = StringIO.StringIO()
for pixel in self.pixel_source.pixels():
c.write("%s\n" % self.format_pixel(pixel))
return c.getvalue()
def get_file_content(self):
"""Return the content of a file of a given format."""
s = StringIO.StringIO()
s.write(self.get_header())
s.write(self.get_content())
return s.getvalue()
def save_to(self, filelike_or_name):
if hasattr(filelike_or_name, "write"):
filelike_or_name.write(self.get_file_content())
elif type(filelike_or_name) in (str, unicode):
with open(filelike_or_name, 'w') as f:
f.write(self.get_file_content())
class PPMImage(PAnyMImage):
"""PPM Image."""
def get_header(self):
w, h = self.pixel_source.get_size()
return "P3 %d %d %d\n" % (w, h, self.get_scale())
def format_pixel(self, pixel):
return '%d %d %d' % tuple(int(channel * self.get_scale() / pixel.get_scale())
for channel in pixel.get_rgb())
class PGMImage(PAnyMImage):
"""PGM Image."""
def get_header(self):
w, h = self.pixel_source.get_size()
return "P2 %d %d %d\n" % (w, h, self.get_scale())
def format_pixel(self, pixel):
r, g, b = pixel.get_rgb()
in_scale = pixel.get_scale()
out_scale = self.get_scale()
l = int((0.3 * r + 0.59 * g + 0.11 * g) * out_scale / in_scale)
return '%d %d %d' % (l, l, l)
class PBMImage(PAnyMImage):
"""PBM Image."""
def get_header(self):
return "P2 %d %d\n" % self.pixel_source.get_size()
def format_pixel(self, pixel):
r, g, b = pixel.get_rgb()
in_scale = pixel.get_scale()
out_scale = self.get_scale()
l = int((0.3 * r + 0.59 * g + 0.11 * g) * out_scale / in_scale)
return str(l)
if __name__ == "__main__":
size = 1000
m = PixelMatrix(size, size)
for i in range(m.get_height()):
for j in range(m.get_width()):
m.set_at((i, j), RGBColor(size, ((i * j) % size, 0, 0)))
i = PPMImage(m, size)
i.save_to('image.ppm')