from __future__ import absolute_import, division, print_function, unicode_literals
from PIL import Image
import numpy as np
import os
import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
class ElaboraImmagini:
# al costruttore della classe passiamo i seguenti argomenti:
# orig: file dell'immagine originale da elaborare, per esempio training.jpg oppure gioco_0.jpg
# dest: cartella di destinazione nella quale salvare le immagini ritagliate dall'immagine originale
# n: dimensione del lato del quadrato dell'immagine, 12 per training.jpg, oppure 3 per l'immagine di gioco_0.jpg, praticamente il numero di colonne della griglia
# w: dimensione di ciascuna cella della griglia in px, ogni immagine estratta avra' tale dimensione, per esempio 100 per immagini di 100x100px
# returnList: se vogliamo che il risultato, ovvero le immagini ritagliate, anziche' essere salvato in una cartella, venga restituito in una lista
def __init__(s, orig, dest = None, n = 12, w = 100, returnList = False):
# impostiamo alcune proprieta' private della classe
s.__return = returnList
s.__lista = [] # questo sara' l'eventuale elenco delle immagini
# preleviamo il percorso assoluto sul quale si trova l'immagine di origine
s.__orig = os.path.abspath(orig)
# se non vogliamo una lista, creiamo il percorso di salvataggio delle immagini
#a tale scopo prendiamo la cartella dell'immagine sorgente e ad essa accodiamo la cartella di destinazione
if not s.__return: s.__dest = os.path.dirname(s.__orig) + "\\" + dest
# se non vogliamo una lista
if not s.__return:
# allora dovremo salvare le immagini in una cartella
# se la cartella di destinazione non esiste
if not os.path.exists(s.__dest):
# allora la creiamo
os.makedirs(s.__dest)
# salviamo i valori in prorieta' private
s.__n = n
s.__w = w
# elaboriamo le immagini
s.elabora()
# per elaborare le immagini dobbiamo ritagliarle da quella originale
# creiamo dunque un metodo per ritagliare le immagini, passando per argomento le coordinate (coor) di ritaglio
# per coordinate si intendono xi, yi e xf, yf di ciascun quadrato di ritaglio (vedere l'articolo per maggiori informazioni)
def ritaglia(s, coor, i):
# leggiamo l'immagine con PIL
img = Image.open(s.__orig)
# ritagliamo l'immagine secondo le coordinate passate
cropped = img.crop(coor)
# se non vogliamo una lista, allora salviamo l'immagine nella destinazione prescelta
if not s.__return: cropped.save(s.__dest + "\\img_" + str(i) + ".jpg")
# altrimenti restituiamo l'immagine come array di NumPy
else: return np.asarray(cropped)
# con questo metodo elaboriamo l'immagine, chiamando ritaglia() per ciascuna cella della griglia
def elabora(s):
# per ciascuna riga
for i in range(s.__n):
#per ciascuna colonna
for j in range(s.__n):
# esegui ritaglia passando le coordinate calcolate e il numero progressivo dell'immagine
img = s.ritaglia( ( j*s.__w , i*s.__w , (j+1) *s.__w , (i+1) * s.__w ) , i * s.__n + j )
# aggiungi l'immagine alla lista delle immagini che si potranno eventualmente restituire
s.__lista.append(img)
# preleviamo la lista delle immagini elaborate
def get(s):
# convertiamo la lista in un array di NumPy
return np.array(s.__lista)
class CreaDBImmagini:
# creiamo delle costanti a livello di classe, che ci permettano di definire l'input tipo nel costruttore
# e quindi il tipo di immagini che vogliamo tirare fuori, questo e' importante solo ai fini della creazione
# delle etichette
TEST = 0
TRAINING = 1
# il costruttore accetta 2 argomenti:
# path: il percorso da cui leggere le immagini
# tipo: il tipo di etichette da creare in associazione
def __init__(s, path, tipo = 0):
s.__path = path
s.__immagini = []
s.__descrizioni = []
s.__tipo = tipo
s.elabora()
# elaboriamo le immagini
def elabora(s):
# per ogni elemento nella cartella
for f in os.listdir(s.__path):
# se si tratta di un file
if os.path.isfile(s.__path + "\\" + f):
# leggiamo il file dell'immagine aprendolo con PIL
img = Image.open(s.__path + "\\" + f)
# trasformiamo i dati dell'immagine in un array contenente i valori RGB dell'immagine stessa
bimg = np.asarray(img)
# aggiungiamo l'immagine all'elenco delle immagini
s.__immagini.append(bimg)
# otteniamo il numero dell'immagine, questo lo abbiamo per costruzione dalla precedente classe ElaboraImmagini
numero = int(f.replace("img_","").replace(".jpg",""))
# se l'immagine e' del gruppo di test
if s.__tipo == s.TEST:
# siccome le immagini di test sono alternate, tipo O, X, O, X ecc, vuol dire che posso distinguerle
# come pari e dispari, quindi i pari saranno 0 e i dispari 1, rispettivamente O e X
s.__descrizioni.append( int(numero % 2) )
# se l'immagine e' del gruppo di addestramento
if s.__tipo == s.TRAINING:
# siccome le immagini di addestramento sono divise a meta', la prima meta' sono O e la seconda sono X
# allora sara' sufficiente dividere per la meta' del totale e arrotondare per difetto
s.__descrizioni.append( int(np.floor(numero / 72)) )
# restituisci i due vettori come array di NumPy
def get(s):
return (np.array(s.__immagini) , np.array(s.__descrizioni))
class GiocoTris:
# prendiamo come argomenti:
# orig: cartella dove sono contenuti tutti i giochi
# train: cartella dove si trovano le immagini di addestramento
# test: cartella dove si trovano le immagini di test
# nome_modello: nome della cartella nella quale verra' salvato il modello calcolato
def __init__(s, orig = "GIOCHI", train = "TRAINING", test = "TEST", nome_modello = "gioco_del_tris"):
s.__orig = orig
s.__train = train
s.__test = test
s.__nome_modello = nome_modello
s.__modello = None
# leggiamo l'elenco dei file nella cartella orig, solo se sono dei file, e teniamoli come partite possibili
s.__partite = [f for f in os.listdir(orig) if os.path.isfile(orig + "\\" + f)]
# se il modello salvato esiste (ovvero se esiste la cartella)
if os.path.exists(s.__nome_modello):
# caricare il modello salvato
s.__modello = keras.models.load_model(s.__nome_modello)
#restituisce la lista dei file delle partite
def getGiochi(s):
return s.__partite
# prepara il modello
def prepara(s):
# prepara il modello se il modello non esiste, ovvero non e' stato caricato
if s.__modello is None:
# preleva le immagini di addestramento e le relative descrizioni, ossia gli output noti
train_img, train_desc = CreaDBImmagini(s.__train,CreaDBImmagini.TRAINING).get()
# prepara il modello
s.__modello = keras.Sequential([
keras.layers.Flatten(input_shape=(100, 100, 3)),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(2, activation='softmax')
])
# compila il modello
s.__modello.compile(optimizer="adam",
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
# allena il modello con le immagini scelte in precedenza
s.__modello.fit(train_img, train_desc, epochs=20)
# salva il modello
s.__modello.save(s.__nome_modello)
# valuta accuratezza del modello con immagini di test
def getAccuracy(s):
# preleva immagini e output di test
test_img, test_desc = CreaDBImmagini(s.__test,CreaDBImmagini.TEST).get()
# calcola le perdite e l'accuratezza sulle immagini di test
s.__test_loss, s.__test_acc = s.__modello.evaluate(test_img, test_desc, verbose=0)
return s.__test_acc
# valuta uno specifico gioco, scelto come posizione tra le partite possibili
def getImmagine(s, pos):
# elabora l'immagine del gioco compiuto
gioco = ElaboraImmagini(orig = s.__orig + "\\" + s.__partite[pos], n = 3, w = 100, returnList = True).get()
# predici i risultati in termini percentuali
previsione = s.__modello.predict(gioco)
# prepara l'output reinterpretato
s.__risultato = []
# per ciascun valore delle previsioni
for p in previsione:
# se i valori sono vicini per un parametro arbitrario di confidenza, allora la casella e' vuota
# per esempio se i valori sono 51% e 49%
if abs(p[0]-p[1]) < 0.1: v = 0
# se il primo valore supera il secondo allora abbiamo uno O
elif p[0] > p[1]: v = 1
# altrimenti abbiamo una X
else: v = 2
# aggiungi il valore al risultato
s.__risultato.append(v)
# ottieni il vincitore
vincitore = s.getVincitore( s.__risultato )
# restituisci il vincitore
return vincitore
# calcola il vincitore
def getVincitore(s,vect):
for i in range(0,9,3):
if vect[i] == vect[i+1] == vect[i+2] and vect[i] != 0: return vect[i]
for i in range(3):
if vect[i] == vect[i+3] == vect[i+6] and vect[i] != 0: return vect[i]
if (vect[0] == vect[4] == vect[8] and vect[0] != 0) or (vect[2] == vect[4] == vect[6] and vect[2] != 0): return vect[4]
# mostra la griglia di gioco
def stampaGioco(s):
symb = ["-","O","X"]
for k, r in enumerate(s.__risultato):
if k > 0 and not k % 3: print()
print(symb[r],end=" ")
print()
ElaboraImmagini("training.jpg","TRAINING",12,100)
ElaboraImmagini("test.jpg","TEST",4,100)
gt = GiocoTris(orig = "GIOCHI", train = "TRAINING", test = "TEST", nome_modello = "gioco_del_tris")
gt.prepara()
while True:
print("-- gioco del tris --")
print('Test accuracy:', gt.getAccuracy())
print("scegli la partita (-1 esci):")
for k, n in enumerate(gt.getGiochi()):
print("{0}. gioco {0}".format(k))
print("> ",end="")
partita = int(input())
if partita == -1: break
if partita >= 0 and partita <= 9:
vincitore = gt.getImmagine( partita )
if vincitore == 1: print("Ha vinto O")
elif vincitore == 2: print("Ha vinto X")
else: print("Nessun vincitore")
gt.stampaGioco()