import matplotlib.pyplot as plt
import imageio

#Img=imageio.imread("Hooke_Bernoulli_200x150.jpg").tolist() # Convertit l'image TP6Image.jpg en liste de listes
Img=imageio.imread("Hooke_Bernoulli.jpg").tolist() # Convertit l'image TP6Image.jpg en liste de listes
plt.figure() # Création d'une figure
plt.imshow(Img) # Commande pour dire qu'on veut afficher Img
plt.title("Image originale",fontsize=20) # Titre
plt.show() # Affichage finale

n=len(Img)
p=len(Img[0])

D={} #liste des couleurs de l'image sans répétition
for i in range(n):
    for j in range(p):
        R,G,B = Img[i][j]
        color = (R,G,B)
        if color not in D: # on a trouvé une nouvelle couleur
            D[color]=True # on la rajoute dans le dictionnaire

print("Il y a ",len(D),"couleurs")

#%%
import numpy as np

def Barycentre(cluster):
    """Renvoie la couleur moyenne parmi toutes les couleurs des pixels du cluster
    cluster est ici une liste de pixels où chaque pixel est de la forme [i,j] où 
    i est le numéro de la ligne et j le numéro de la colonne du pixel"""
    if len(cluster)!= 0:
        b=[0,0,0] # couleur moyenne du cluster
        for k in range(3): # k=0 pour le rouge, k=1 pour le vert et k=2 pour le bleu
            for [i,j] in cluster:
                b[k] = b[k]+Img[i][j][k]
            b[k] = b[k]/len(cluster) 
        return b
    else:
        return [np.random.randint(256) for k in range(3)] # on renvoit une couleur de pixel au hasard

def Dist(M,N):
    """Renvoie la distance euclidienne entre deux listes de même taille"""
    S=0
    for i in range(3):
        S = S+(M[i]-N[i])**(2)
    return S**(1/2)

def PlusProcheBarycentre(B,color):
    """Renvoie l'indice du barycentre de B le plus proche de la couleur color"""
    dmin=np.inf
    for j in range(len(B)):
        if Dist(color,B[j]) < dmin:
            jmin, dmin = j, Dist(color,B[j])
    return jmin

def kMoyennes(Img,k):
    #liste de k couleurs aléatoires 
    B=[[np.random.randint(256),np.random.randint(256),np.random.randint(256)] for i in range(k)]
    compteur=0
    while True: # fausse bouche infinie
        Clusters = [ [] for i in range(k) ] # calcul des différents clusters
        for i in range(n):
            for j in range(p): # pour le pixel à la ligne i et la colonne j
                jmin = PlusProcheBarycentre(B,Img[i][j])
                # Img[i][j] est plus proche du barycentre B[jmin] que de n'importe quelle autre barycentre
                Clusters[jmin].append([i,j]) # on le rajoute donc à ce cluster
        # listes des nouveaux barycentres des nouveaux clusters
        newB = [ Barycentre(Clusters[j]) for j in range(k) ]
        compteur +=1
        print("Etape n°",compteur)
        if newB == B: # si les barycentres n'ont pas changé
            return Clusters # Arrêt de la fonction et renvoi des clusters obtenus
        B = newB # sinon on remplace les anciens barycentres par les nouveaux

def ReductionCouleur(k):
    Clusters = kMoyennes(Img,k)
    Img2 = [[0 for j in range(p)] for i in range(n)]
    for cluster in Clusters:
        b = Barycentre(cluster)
        for [i,j] in cluster:
            Img2[i][j] = [int(b[0]),int(b[1]),int(b[2])]
    plt.figure() # Création d'une figure
    plt.imshow(Img2) # Commande pour dire qu'on veut afficher Img
    plt.title("Image avec "+str(k)+" couleurs",fontsize=20) # Titre: nb de couleurs
    plt.show()  # Affichage final
    
for k in [2,5,10,20,50,500]:
    ReductionCouleur(k)
