import png, math class Colore: '''Classe che rappresenta il colore di un pixel, come 3 valori float tra 0 e 1 ''' def __init__(self, r, g, b): '''il costruttore Colore(r, g, b), dopo aver memorizzato i 3 valori, li vincola nel range 0-1.''' self._r = r self._g = g self._b = b self.normalize() def normalize(self): '''vincola i 3 valori nel range 0-1''' self._r = max(0, min(1, self._r)) self._g = max(0, min(1, self._g)) self._b = max(0, min(1, self._b)) def clone(self): '''crea una copia di questo Colore''' return Colore(self._r, self._g, self._b) def __str__(self): '''Formato testuale human-friendly''' return "Colore({0._r}, {0._g}, {0._b})".format(self) def to_RGB8(self): '''Converte un colore in una terna RGB di valori interi tra 0 e 255 (rappresentabili da 3 byte)''' return int(self._r*255), int(self._g*255), int(self._b*255) @classmethod def from_RGB8(cls, r, g, b): '''costruttore che accetta 3 valori RGB tra 0 e 255''' return cls(r/255, g/255, b/255) def grigio(self): '''filtro che genera un colore grigio con la stessa luminosità del colore originale''' media = (self._r + self._g + self._b)/3 return Colore(media, media, media) def contrasto(self): '''filtro che aumenta la distanza tra colori chiari e colori scuri''' r = ((self._r - 0.5)*2 + 0.5) g = ((self._g - 0.5)*2 + 0.5) b = ((self._b - 0.5)*2 + 0.5) return Colore(r, g, b) def inverso(self): '''filtro che calcola il colore inverso (o il negativo) di un colore''' return Colore(1-self._r, 1-self._g, 1-self._b) class Immagine(object): '''Una Immagine è un rettangolo di pixel. La rappresentazione interna è basata su una lista di liste di Colore().''' def __init__(self, w, h, color=Colore(0,0,0)): '''Immagine(w,h, colore=nero) crea una immagini w x h colorata del colore fornito''' self._w = w self._h = h self._img = [] for _ in range(h): riga = [] for _ in range(w): riga.append(color.clone()) self._img.append(riga) def __str__(self): '''Rappresentazione testuale human-friendly di una Immagine''' return "Immagine({0._w}, {0._h})".format(self) def save(self, filename): '''Salva una immagine in un file 'filename' in formato PNG''' img = [[p.to_RGB8() for p in linea] for linea in self._img] pngimg = png.from_array(img,'RGB') pngimg.save(filename) def setPixel(self, x, y, colore): '''Colora il pixel che si trova alle coordinate y,x del colore dato. (senza fare controlli sulle coordinate)''' self._img[y][x] = colore.clone() def getPixel(self, x, y): '''legge il colore del pixel che si trova alle coordinate y,x. (senza fare controlli sulle coordinate)''' return self._img[y][x] @classmethod def load(cls, filename): '''costruttore che carica una immagine da un file''' with open(filename, mode='rb') as f: reader = png.Reader(file=f) w, h, png_img, _ = reader.asRGB8() immagine = cls(w,h) png_img = list(png_img) for y in range(h): for x in range(w): c = Colore.from_RGB8(png_img[y][3*x], png_img[y][3*x+1], png_img[y][3*x+2]) immagine.setPixel(x, y, c) return immagine def filtra(self, filtro): '''Costruisce una nuova immagine ottenuta applicando il filtro (una funzione) a ciascun pixel dell'immagineoriginale''' immagine = Immagine(self._w, self._h) for y in range(self._h): for x in range(self._w): c = filtro(self._img[y][x]) immagine.setPixel(x, y, c) return immagine def ruota90(self): '''Costruisce una immagine ottenuta ruotando l'immagine originale di 90° in senso antiorrio''' immagine = Immagine(self._h, self._w) for y in range(self._w): for x in range(self._h): c = self._img[x][self._w-1-y] immagine.setPixel(x, y, c) return immagine def draw_rect(self, x, y, w, h, color): ''' Disegna un rettangolo di colore uniforme nella posizione x,y e di dimensioni h,w, di colore dato. Eventuali sbordature vengono catturate ed ignorate (inefficiente).''' for px in range(x, x+w): for py in range(y, y+h): try: self._img[py][px] = color.clone() except IndexError: pass def inside(self, x, y): '''Verifica se il pixel di coordinate x,y è contenuto nella immagine''' return 0 <= x < self._w and 0 <= y < self._h def mosaico(self, l): '''Crea una nuova immagine mosaico di lato l colorando ciascun quadretto col colore del pixel in alto a sinistra del quadretto nell'immagine originale.''' immagine = Immagine(self._w, self._h) nh = math.floor(self._w/l) nv = math.floor(self._h/l) for y in range(nv): for x in range(nh): xc = x*l + l//2 yc = y*l + l//2 colore = self._img[yc][xc] immagine.draw_rect(x*l, y*l, l, l, colore) return immagine if __name__ == '__main__': c = Colore(1.0,0.5,0.7) print(c) print(c.to_RGB8()) c2 = Colore.from_RGB8(255, 128, 0) print(c2) i = Immagine(100, 300, c2) print(i)