# %% RECAP # %%% Immagini come matrici di pixel # --- creazione/load/save # --- rotazione # --- disegno di linee verticali/orizzontali/diagonali # --- disegno di rettangoli ed ellissi # --- trasformazione di colori (grigio/negativo/luninosità/contrasto) # --- trasformazione tramite filtri import images # %% CLASSI e oggetti # classi: attributi (di classe) e metodi # istanze: attributi di istanza e metodi # information hiding e "responsabilità" delle operazioni # ereditarietà come meccanismo per estendere un tipo di oggetti # %%% Colori come oggetti # --- operazioni matematiche sui colori # costruttore # rappresentazione (__repr__ e __str__) # somma, prodotto, differenza # conversione in tripla RGB class Color: def __init__(self, R, G, B): "un colore contiene i tre canali R,G,B" self._R = R self._G = G self._B = B def luminosità(self): "calcolo la luminosità media di un pixel (senza badare se è un valore intero)" return (self._R + self._G + self._B)/3 def __add__(self, other): "somma tra due colori" if not type(other) is Color: raise ValueError("L'oggetto other non è un Color") return Color(self._R + other._R, self._G + other._G, self._B + other._B) def __mul__(self, k): "moltiplicazione di un colore per una costante" return Color(self._R*k, self._G*k, self._B*k) def asTriple(self): "creo la tripla di interi tra 0 e 255 che serve per le immagini PNG" def bound(X): # solo quando devo creare un file PNG mi assicuro che il pixel sia intero nel range 0..255 return max(0, min(255, round(X))) return bound(self._R), bound(self._G), bound(self._B) def __repr__(self): "stringa che deve essere visualizzata per stampare il colore" return f"Color({self._R}, {self._G}, {self._B})" # uso una f-stringa equivalente a # return "Color(" + str(self._R) + ", " + str(self._G) + ", " + str(self._B) + ")" def grigio(self): "creo un golore grigio con la stessa luminosità" L = self.luminosità() return Color(L, L, L) # solo dopo che ho definito la classo Color posso aggiungerle gegli attributi ottenuti usandola Color.white = Color(255, 255, 255) Color.black = Color(0, 0, 0) Color.red = Color(255, 0, 0) Color.green = Color(0, 255, 0) Color.blue = Color(0, 0, 255) # --- trasformazioni: grey, luminosità, negativo, contrasto # bound # confronto # Esempi pixel = Color(255, 0, 0) p2 = Color(0,255,0) p3 = p2 + pixel # uso l'operatore somma tra due colori che ho definito p4 = p3 * 0.5 # uso l'operatore prodotto per una costante che ho definito # %%% Immagini come oggetti import images import math class Immagine: def __init__(self, larghezza=None, altezza=None, sfondo=None, filename=None): """posso creare una immagine in due modi: - leggendola da un file PNG - fornendo dimensioni e colore di sfondo """ if filename: img = images.load(filename) # letta la immagine la converto in una matrice di Color self._img = [ [ Color(r,g,b) for r,g,b in riga ] for riga in img ] self._W = len(img[0]) self._H = len(img) else: # altrimenti creo una immagine monocolore self._W = larghezza self._H = altezza self._img = [ [sfondo for _ in range(larghezza) ] for _ in range(altezza) ] def __repr__(self): "per stampare l'immagine ne mostro le dimensioni" return f"Immagine(larghezza={self._W},altezza={self._H}, ... )" def save(self, filename): "si salva l'immagine dopo averla convertita in matrice di triple" images.save(self.asTriples(), filename) def asTriples(self): "conversione della immagine da matrice di Color a matrice di triple" return [ [ pixel.asTriple() for pixel in riga ] for riga in self._img ] def set_pixel(self, x, y, color): "cambio un pixel se è dentro l'immagine" if 0 <= x < self._W and 0 <= y < self._H: self._img[y][x] = color def get_pixel(self, x, y): "leggo un pixel se è dentro l'immagine oppure torno None" if 0 <= x < self._W and 0 <= y < self._H: return self._img[y][x] def draw_line_H(self, x, y, l, color): "disegno una linea orizzontale" for X in range(x, x+l): self.set_pixel(X, y, color) def draw_line_V(self, x, y, l, color): "disegno una linea verticale" for Y in range(y, y+l): self.set_pixel(x, Y, color) def draw_rectangle_full(self, x, y, w, h, color): "disegno un rettangolo disegnando tante linee orizzontali" for Y in range(y, y+h): self.draw_line_H(x, Y, w, color) def draw_rectangle(self, x, y, w, h, color): "didegno un rettangolo vuoto" self.draw_line_H(x, y, w, color) self.draw_line_H(x, y+h, w, color) self.draw_line_V(x, y, h, color) self.draw_line_V(x+w, y, h+1, color) def draw_ellipse_full(self, x1,y1, x2,y2, D, color ): "una ellisse piena" for X in range(self._W): for Y in range(self._H): D1 = math.sqrt((X-x1)**2 + (Y-y1)**2) D2 = math.sqrt((X-x2)**2 + (Y-y2)**2) if D1+D2 <= D: #self.set_pixel(X,Y,colore) self._img[Y][X] = color def draw_ellipse(self, x1,y1, x2,y2, D, color ): "una ellisse" for X in range(self._W): for Y in range(self._H): D1 = math.sqrt((X-x1)**2 + (Y-y1)**2) D2 = math.sqrt((X-x2)**2 + (Y-y2)**2) if abs(D1+D2-D) < 1: #self.set_pixel(X,Y,colore) self._img[Y][X] = color def draw_circle(self, xc, yc, r, color): "un cerchio è una ellisse con i due fuochi coincidenti e D=2*r" self.draw_ellipse(xc, yc, xc, yc, 2*r, color) def visualizza(self): "visualizzo l'immagine in Spyder" return images.visd(self.asTriples()) img = Immagine(larghezza=50, altezza=100, sfondo=Color.red) img2 = Immagine(filename='3cime.png') img2.draw_ellipse(50,50, 100, 100, 100, Color.red) img2.draw_rectangle(20,60, 100, 80, Color.green) img2.draw_circle(-20,60, 100, Color.green) # --- costruttore: create/load, save # --- disegno su immagini # --- filtri # --- operazioni tra immagini (cut/paste, sovrapposizione con trasparenza) # %%%% pixellation # per pixellare una immagine su una dimensione S # costruisco una scacchiera di passo S # ogni quadrato ha il colore di: # il pixel centrale # oppure la media dei suoi pixel # %%%% random noise # per aggiungere rumore casuale ad una immagine # possiamo aggiungere a ciascun pixel un piccolo valore random # per aggiungere rumore casuale ad una immagine # oppure sostituiamo ciascun pixel con un suo vicino preso a caso # %%%% lens # per dare l'effetto lente # nella zona della lente # mettiamo dei pixel che stanno a distanza K volte # quella che si ha dal centro della lente # %%%% edge detection