from PIL import Image
from copy import deepcopy
from math import sqrt,pi,atan2

#===========================================================================   DATABASE   =======================================================================================================================================================

cassiopée=[[0.8286901903137861, 3.491760321197075], [0.6327821549873603, 2.581334018683134], [0.4914365001645071, 0.5996702901640077], [1.0, 0.0]]
grande_ourse=[[0.981963943474469, 0.3498517385972837], [1.0, 0.0], [0.347444000839927, 0.04145375439070609], [0.28625054830645774, 3.0886265246416027], [0.5740184704926564, 5.916040641847373], [0.6858578808355741, 3.5017616291835076]]
im=Image.open('cassiopee.jpg')
constellations=[cassiopée,grande_ourse]#base de données
print('a')


#========================================================================   MAIN FUNCTIONS   ========================================================================================================================================================
s=im.size
m=im.mode
hauteur=im.size[1]#nb de lignes de pixels
largeur=im.size[0]# nb de colonnes de pixels

def definir(im,nombre):# im:l'image avec une constellation;nombre:nombre le nombre d'étoiles de la constellation
#cette fonction renvoie une liste comme ci-dessus qui caractérise une photo de constellation
    s=im.size
    m=im.mode
    hauteur=im.size[1]#nb de lignes de pixels
    largeur=im.size[0]# nb de colonnes de pixels
    #im=inversion(im)
    imf2=niveau_de_gris(im)
    #print(moyenne)
    imf3=simplifié(imf2)
    #imf3.show()
    B=binaire(imf3)
    nettoyage(B)
    #print(B)
    L,nbLabels=flood_fill(B)
    #print(nbLabels)
    #print(L)
    liste_centres=centres(nbLabels,L,nombre)
    if liste_centres==False:
        return False
    #print(liste_centres)
    #check(liste_centres)
    centres_classés=classer_centres(liste_centres)
    #print(centres_classés)
    listepositions=positions(centres_classés,liste_centres)
    return listepositions

def identifier(im):
    r=0
    s=im.size
    m=im.mode
    hauteur=im.size[1]#nb de lignes de pixels
    largeur=im.size[0]# nb de colonnes de pixels
    for k in range(len(constellations)):
        L1=deepcopy(constellations[k])
        nombre=len(L1)+1
        L2=definir(im,nombre)
        #print(L2)
        if L2==False:
            pass
        else:
            résultat=compare(L1,L2)
            if résultat==True:
                if k==0:
                   return"Il s'agit de la constellation Cassiopée"
                
                elif k==1:
                    return"Il s'agit de la constellation de la Grande Ourse"
                    
    return "Aucune constellation reconnue"


#=============================================================================   SUBFUNCTIONS =======================================================================================
	
def matrice0(hauteur,largeur):#crée une matrice de zéros
    M=[]
    nbLabels=0
    for i in range (hauteur):
        M.append([])
        for j in range (largeur):
            M[i].append(0)
    return M


#inversion (négatif)
def inversion(im):
    imf1=Image.new(m,s)
    for i in range(hauteur):
        for j in range(largeur):
            pixel = im.getpixel((j,i)) # récupération du pixel
        # on calcule le complement à MAX pour chaque composante - effet négatif
            p = (255 - pixel[0], 255 - pixel[1], 255 - pixel[2])
        # composition de la nouvelle image
            imf1.putpixel((j,i), p)
    #imf1.show()
    return imf1

#niveau de gris
def niveau_de_gris(im):
    imf2=Image.new(m,s)
    somme=0
    for i in range(hauteur):
        for j in range(largeur):
            pixel = im.getpixel((j,i)) # récupération du pixel
            
# calcul du poids de chaque composante du gris dans le pixel (CIE709:convention européenne)
            gris = int(0.2125 * pixel[0] + 0.7154 * pixel[1] + 0.0721 * pixel[2])
            somme+=gris
# gris = int(0.33 * pixel[0] + 0.33 * pixel[1] + 0.33 * pixel[2])
            p = (gris,gris,gris)

    # composition de la nouvelle image
            imf2.putpixel((j,i), p)
    #imf2.show()
    return imf2

def simplifié(imf2): #renvoie une image binaire (infonctionelle ici)
    imf3=Image.new(m,s)
    somme=0
    for i in range(hauteur):
        for j in range(largeur):
            pixel = imf2.getpixel((j,i))
            if (pixel[0]+pixel[1]+pixel[2])/3 <200:#<------------------On modifie le seuillage ici
                 pixel = (0,0,0)#pixels noirs
                 
            else :
                pixel = (255,255,255)
                imf3.putpixel((j,i), pixel)
        
    #imf3.show()
    return imf3

def binaire(imf3): #prend une image binaire et renvoie une matrice binaire (1 et 0)
    B=deepcopy(matrice0(hauteur,largeur))
    for i in range (hauteur):
        for j in range (largeur):
             pixel = imf3.getpixel((j,i))
             if pixel!=(0,0,0):
                 B[i][j]=1
    
    return B


             
def flood_fill(B):
    L=deepcopy(matrice0(hauteur,largeur))
    nbLabels=0
    for i in range (hauteur):
        for j in range (largeur):
            if B[i][j]==1:
                nbLabels+=1
                connexites(i,j,B,L,nbLabels)            
    #print(nbLabels)       
    return L,nbLabels

def nettoyage(B):#tous les pixels qui bordent l'image sont mis en noir
    for i in range(hauteur):
        B[i][0],B[i][largeur-1]=0,0
    for j in range(largeur):
        B[0][j],B[hauteur-1][j]=0,0 #pour éviter les erreurs 'out of range'
    

def connexites(i,j,B,L,nbLabels):#récursif
    pixel=B[i][j]
    if pixel==1:
        L[i][j]=nbLabels
        B[i][j]=0
        if B[i][j+1]==1:
            connexites(i,j+1,B,L,nbLabels)
        if B[i-1][j]==1:
            connexites(i-1,j,B,L,nbLabels)
        if B[i][j-1]==1:
            connexites(i,j-1,B,L,nbLabels)
        if B[i+1][j]==1:
             connexites(i+1,j,B,L,nbLabels)


def centres(nbLabels,L,nombre):
    liste_coord=[]
    longueurs=[] #suite qui compile les longueurs des sousclistes de la liste list_coord
    étoiles=[] #liste des coordonnées "intéréssantes"
    liste_centres=[]
    for k in range (nbLabels):
        liste_coord.append([])
    for i in range(hauteur):
        for j in range (largeur):
            if L[i][j]!=0:
                liste_coord[(L[i][j]-1)].append([i,j])
    #print(liste_coord)
    taille=len(liste_coord)
    #print(taille)
    if taille<nombre:
        #print("false")
        return False
    for h in range (taille):
        longueurs.append(len(liste_coord[h]))
    longueurs.sort()
    plus_petit_pt=longueurs[(nombre*(-1))] #nb de pixels composant le plus petit point qui intéresse
    for l in range (taille):
        if len(liste_coord[l])>=plus_petit_pt:
            étoiles.append(liste_coord[l])
    #print (étoiles)
    for s in range(nombre):
        x,y=0,0
        for a in range(len(étoiles[s])):
            x+=étoiles[s][a][0]
            y+=étoiles[s][a][1]
        X=x/len(étoiles[s])
        Y=y/len(étoiles[s])
        liste_centres.append([X,Y])
    return liste_centres

def classer_centres(liste_centres):#obtenir une liste des points suivis des deux plus courtes distances qui le séparent d'un autre point
    N=len(liste_centres)
    centres_classés=[]
    for k in range(N):
        distancemax=0
        point1,point2=[0,0],[0,0]
        for l in range(N):
            if k!=l:
                distance=sqrt((liste_centres[k][0]-liste_centres[l][0])**2+(liste_centres[k][1]-liste_centres[l][1])**2)
                if distance>distancemax:
                    distancemax=distance
                    point1=liste_centres[l]
        centres_classés.append([liste_centres[k],distancemax,point1])
    return centres_classés


def positions(centres_classés,liste_centres):
    N=len(centres_classés)
    positions=[]
    #je cherche le point en  le plus près de tous les autres pour
    #servir d'origine au repère
    distancemax1minimale=centres_classés[0][1]
    i=0
    for k in range(1,N):
        if centres_classés[k][1]<distancemax1minimale:
            distancemax1minimale=centres_classés[k][1]
            i=k #indice du point ds la liste centres_classés qui servira d'origine du répère
#coordonnées du point d'origine
    #print(i)
    #print(distancemax1minimale)
    X=centres_classés[i][0][0]
    Y=centres_classés[i][0][1]
    angleref=atan2((centres_classés[i][2][1]-Y),(centres_classés[i][2][0]-X))
    del(liste_centres[i])
    for j in range(N-1):
        x=liste_centres[j][0]-X
        y=liste_centres[j][1]-Y
        angle=atan2(y,x)
        distance_relative=sqrt(x**2+y**2)/distancemax1minimale
        if angle>=angleref:
            positions.append([distance_relative,(angle-angleref)])
        else :
            positions.append([distance_relative,2*pi-(angleref-angle)])
    return positions
                         

def check(liste_centres):
    imf=Image.new(m,s)
    pixel=(255,255,255)
    for k in range (len(liste_centres)):
        imf.putpixel((int(liste_centres[k][1]),int(liste_centres[k][0])), pixel)
    imf.show()

def compare(L1,L2):
    if len(L1)!=len(L2):
        return False
    n=len(L1)
    points_communs=0
    for i in range(n):
        for j in range(n):
            if abs(L1[i][0]-L2[j][0])<0.05 and abs(L1[i][1]-L2[j][1])<0.05:
                points_communs+=1
    if points_communs==n:
        return True
    else:
        return False


                
        
    


