from collections import namedtuple class Pixel(namedtuple('Pixel', ['r', 'g', 'b'])): def _verify(self): if max(self.r, self.g, self.b) > 255: raise ValueError('Some component too bright.') if min(self.r, self.g, self.b) < 0: raise ValueError('Some component negative.') def __add__(self, other): return Pixel( self.r + other.r, self.g + other.g, self.b + other.b, ) def __mul__(self, other): if not isinstance(other, int) or not isinstance(other, float): raise ValueError('Can only multiply by number') return Pixel( other*pixel.r, other*pixel.g, other*pixel.b, ) def weighted_average(p1, w1, p2, w2): too_big = w1*p1 + w2*p2 coef = 1/(w1+w2) return coef * too_big class Picture: def __init__(self, data): self.pixels = data self.w = len(data[0]) self.h = len(data) @classmethod def load(cls, filename): from PIL import Image im = Image.open(filename) im = im.convert('RGB') ht = im.height wt = im.width data = [] access = im.getdata().pixel_access() for y in range(ht): line = [] for x in range(wt): line.append(Pixel(*(access[x, y]))) data.append(line) result = cls(data) assert result.w == wt assert result.h == ht return result def _to_pil(self): # Create int iterable # TODO: If this is slow, we should use a generator. seq = [] for line in self.pixels: for pix in line: seq.extend(pix) # bytes… b = bytes(seq) # PIL image from PIL import Image img = Image.frombytes('RGB', (self.w, self.h), b) return img def show(self): img = self._to_pil() img.show() def save(self, filename): img = self._to_pil() img.save(filename) class Interpolator: def interpolate(self, picture, new_h, new_w): quantum_x = picture.w / new_w quantum_y = picture.h / new_h result = [[None for _ in range(new_w)] for _ in range(new_h)] for x in range(new_w): for y in range(new_h): result[y][x] = self.get_pixel(picture, x*quantum_x, y*quantum_y, result, quantum_x, quantum_y) return Picture(result) def get_pixel(self, picture, x, y, result, quantum_x, quantum_y): raise NotImplementedError('You write this :-)')