# ---
# jupyter:
# jupytext:
# formats: ipynb,py:percent
# text_representation:
# extension: .py
# format_name: percent
# format_version: '1.3'
# jupytext_version: 1.14.0
# kernelspec:
# display_name: Python 3 (ipykernel)
# language: python
# name: python3
# ---
# %% [markdown] toc=true slideshow={"slide_type": "notes"}
#
Table of Contents
#
# %% [markdown] slideshow={"slide_type": "slide"}
# # Fondamenti di Programmazione
#
#
# **Andrea Sterbini**
#
# lezione 12 - 14 novembre 2022
# %% [markdown] slideshow={"slide_type": "subslide"}
# # RECAP:
# - **query in un gruppo di files con tr-idf (term frequency - inverse document frequency)**
# - files **json**
# - files **yaml**
# - lettura di pagine e file da web con **requests**
# %% [markdown] slideshow={"slide_type": "subslide"}
# # OGGI: immagini
#
# - sono griglie rettangolari di pixel (**pic**ture **el**ement) colorati
# - ogni posizione a coordinate x,y contiene un colore
# - **[RGB](https://en.wikipedia.org/wiki/RGB_color_model)** è un modo di codificare i colori (altri: HSV/L/B, CMYK)
# - R: luminosità della componente rossa (red)
# - G: luminosità della componente verde (green)
# - B: luminosità della componente blu (blue)
#
# Questi valori in genere sono codificati in un byte [0 .. 255] quindi un pixel occupa 24 bit (16M colors)
# %% IMMAGINI come matrici di pixel slideshow={"slide_type": "subslide"}
# con valori tra 0 e 255 compresi (1 byte)
# definiamo qualche colore
black = 0, 0, 0
white = 255, 255, 255
red = 255, 0, 0
green = 0, 255, 0
blue = 0, 0, 255
cyan = 0, 255, 255
yellow= 255, 255, 0
purple= 255, 0, 255
grey = 128, 128, 128
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Immagine = matrice di pixel
# - per semplicità usiamo una lista di liste
# - la lista esterna è l'immagine, che contiene righe orizzontali di pixel
# - ciascuna riga di pixel è una lista
# - tutte le righe hanno la stessa lunghezza
# - le righe nella immagine sono disposte dall'alto in basso (l'asse Y va in giù)
# - ciascuna riga va da sinistra a destra (l'asse X va a destra)
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Creazione di una immagine/matrice monocolore
# ### nel modo sbagliato
# %%% costruire una immagine WxH di un dato colore slideshow={"slide_type": "fragment"}
import images
def crea_immagine_errata(larghezza, altezza, colore):
riga = [ colore ] * larghezza
img = [ riga ] * altezza
return img
img2 = crea_immagine_errata(30, 40, blue)
img2[5][7] = red # Coloro un solo pixel
images.visd(img2) # e trovo una colonna rossa!!!
## LAVAGNA!
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## PERCHE'?
# **`riga = [ colore ] * larghezza`** costruisce una lista di RIFERIMENTI ad un unico colore
#
# **`img = [ riga ] * altezza`** costruisce una lista di RIFERIMENTI ad una unica riga
#
# Mentre la prima va bene perchè tutti i colori sono tuple, immutabili
#
# La seconda NO perchè vogliamo che le righe siano modificabili indipendentemente
#
# 
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### Nel modo giusto
# %%% costruire una immagine WxH di un dato colore slideshow={"slide_type": "fragment"}
def crea_immagine(larghezza, altezza, colore=black):
return [ [ colore ]*larghezza
for i in range(altezza)
]
def crea_imm(L,H,C):
img = []
for y in range(H):
riga = []
for x in range(L):
riga.append(C)
img.append(riga)
return img
img = crea_imm(30, 40, red)
img[5][7] = blue # coloro un pixel
images.visd(img) # e ora va molto meglio
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Caricare/salvare una immagine da/su disco
# %%% caricare e salvare una immagine con la libreria images slideshow={"slide_type": "fragment"}
# Pixel = tuple[int,int,int]
# Line = list[Pixel]
# Picture = list[Line]
# images.load(filename : str) -> Picture
img3 = images.load('3cime.png')
print(len(img3), len(img3[0])) # altezza e larghezza
img3[40][30:250] = [red]*220 # coloriamo una fila di pixel
# images.save(img : Picture, filename : str) -> None
images.save(img3, '3cime-2.png')
images.visd(img3)
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Disegnare un pixel
# ... senza generare errori se le coordinate sono fuori dall'immagine
# %%% evitare di sbordare slideshow={"slide_type": "fragment"}
# --- controllando le posizioni
def draw_pixel(img, x, y, colore):
# ricavo l'altezza e larghezza dell'immagine
L,A = len(img[0]), len(img)
# cambio il pixel solo se dentro l'immagine
if 0 <= x < L and 0 <= y < A:
x = int(round(x))
y = int(round(y))
img[y][x] = colore
draw_pixel2(img3, 100, 150, red)
images.visd(img3)
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Try/except/finally
#
# Per catturare eventuali errori e gestirni nel proprio programma
#
# ```python
# try:
# codice che potrebbe produrre un errore
# except TipoDiErrore as e:
# codice da eseguire se TipoDiErrore
# finally:
# codice da eseguire alla fine in ogni caso
# ```
# %%% evitare di sbordare slideshow={"slide_type": "subslide"}
# --- usando try-except per catturare l'errore
def draw_pixel(img, x, y, colore):
# mi preparo a catturare l'errore (try)
try:
# disegno il pixel
assert x >= 0 and y >= 0
img[y][x] = colore
# se c'è errore (except) lo ignoro
except IndexError:
pass
except AssertionError:
print('sbordato a sinistra o in alto')
pass
# --- BEWARE of negative indexes!!! (che non producono errori)
# --- BEWARE of generic 'catch-all' except clauses!!!!! (che nascondono TROPPI errori)
draw_pixel(img3, -50, -50, red)
images.visd(img3)
# %% [markdown] slideshow={"slide_type": "slide"}
# ## Rotazione di 90° a sinistra (lavagna)
# 
# %%% ruotare una immagine di 90° in senso anti/orario slideshow={"slide_type": "subslide"}
# X_destinazione = y_sorgente
# Y_destinazione = larghezza_sorgente - 1 - x_sorgente
def ruota_sx(img):
altezza = len(img)
larghezza = len(img[0])
# creo una immagine con altezza e larghezza scambiate
img2 = crea_immagine(altezza, larghezza)
# per ogni pixel della immagine originale
for y, riga in enumerate(img):
for x, pixel in enumerate(riga):
# calcolo le coordinate della destinazione
X = y
Y = larghezza -1 -x
# e copio il pixel
img2[Y][X] = pixel
# torno l'immagine ruotata
return img2
img_r = ruota_sx(img3)
images.visd(img_r)
# --- PER CASA: rotazione destra
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Disegnare una linea orizzontale o verticale
# %%% Disegnare una retta orizzontale/verticale slideshow={"slide_type": "fragment"}
def draw_h_line(img, x, y, x2, colore):
# scandisco le X da x a x2
for X in range(x, x2+1):
# riusiamo la draw_pixel che controlla di non sbordare
draw_pixel(img, X, y, colore)
# oppure prima intersechiamo la linea con l'immagine e poi la disegnamo senza controllare
def draw_h_line2(img, x, y, x2, colore):
larghezza = len(img[0])
altezza = len(img)
# se la y è tra 0 e altezza
if 0 <= y < altezza:
# la parte da disegnare ha estremi non maggiori di larghezza-1 e non minori di 0
x, x2 = min(x,x2), max(x, x2)
xmin = max(x, 0)
xmax = min(x2, larghezza-1)
# una volta aggiustati gli estremi la si disegna
for X in range(xmin, xmax+1):
img[y][X] = colore
img = images.load('3cime.png')
draw_h_line2(img, 30, 100, 200, red)
images.visd(img)
# lo stesso per una linea verticale
def draw_v_line(img, x, y, y2, colore):
larghezza = len(img[0])
altezza = len(img)
# la parte da disegnare ha estremi non maggiori di altezza-1 e non minori di 0
# una volta aggiustati gli estremi la si disegna
for Y in range(y, y2+1):
# riusiamo la draw_pixel che controlla di non sbordare
draw_pixel(img, x, Y, colore)
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### e per le diagonali?
# %%% Disegnare una retta orizzontale/verticale slideshow={"slide_type": "fragment"}
# --- e diagonale??? come???
# dipende dalla direzione
def draw_slope(img, x1, y1, x2, y2, colore):
# FIXME: aggiungere i controlli sulle coordinate?
dx = x2-x1
dy = y2-y1
# si varia lungo la direzione più lunga
# se |dx| > |dy| ci calcola la y per ciascuna x in [x1 .. x2]
# FIXME: e se dx == 0 oppure dy == 0? disegnamo una retta verticale o orizzontale
if dx == 0:
draw_v_line(img, x1, y1, y2, colore)
elif dy == 0:
draw_h_line(img, x1, y1, x2, colore)
elif abs(dx) >= abs(dy):
# mi assicuro che x1