diff --git a/Python Files/images.py b/Python Files/images.py new file mode 100644 index 0000000..06d7405 Binary files /dev/null and b/Python Files/images.py differ diff --git a/Python Files/kmoy.py b/Python Files/kmoy.py new file mode 100644 index 0000000..a7b09a9 --- /dev/null +++ b/Python Files/kmoy.py @@ -0,0 +1,100 @@ +from PIL import Image, ImageOps +import numpy as np +import random as rd +import math + +# Lecture de l'image et changement du contraste +image = Image.open("./images/2.jpg",mode = 'r') +image = ImageOps.autocontrast(image, cutoff=10) + +im = np.asarray(image) + +nb_lignes, nb_colones, nb_subpixel = np.shape(im) # nb_subpixel est le nombre d'élements d'un points (R, G, B) = 3 +classe = [[-1 for i in range(nb_colones)] for j in range(nb_lignes)] # Tableau des classes de chaque éléments +nb_classe = 8 + +# initialisation centres +centres = [] + +for i in range(nb_classe) : + ir = rd.randint(0,nb_lignes-1) + jr = rd.randint(0,nb_colones-1) + while [im[ir,jr,s] for s in range(nb_subpixel)] in centres : + ir = rd.randint(0,nb_lignes-1) + jr = rd.randint(0,nb_colones-1) + centres.append([im[ir,jr,s] for s in range(nb_subpixel)]) # On prend des pixels aléatoires dans l'image, et on vérifie qu'ils sont tous differents + +# distance (voire pour prendre plus efficace ?) + +def distance(e1: list, e2: list) : # distance entre deux couleurs + d = len(e1) + res = 0 + for i in range(d) : + res = max(res,(e1[i]-e2[i])**2) + return(res) + +# ranger au plus proche des centres +def classer() : # permet de classer les elements dans chaque classe + global classe, im, nb_lignes, nb_colones, centres + b = False + for i in range(nb_lignes) : # On parcours toute l'image + for j in range(nb_colones) : # -- + dmin = 0 + if classe[i][j] == -1 : # si il n'y a pas encore de classe + dmin = math.inf # la distance est la minimum + else : + dmin = distance(im[i,j], centres[classe[i][j]]) + for m in range(nb_classe) : + dist = distance(list(im[i,j]), centres[m]) + if dist list: + return(img[::-1]) + +# lecture de l'image liée au lien [lien] depuis le serveur web +def read_img(lien: str) -> np.array: + im = io.v2.imread(lien) + im = np.array(im) + return(inv_img_y(im)) + + +# affiche l'image [img]. +# [cmap='gray'] premet d'afficher correctement les images en noir et blanc ainsi que les images en couleur +# pour les images en niveaux de gris, le pixel le plus sombre sera noir et le pixel le plus clair sera blanc. +# [rev] premet d'inverser l'image +def show_image(img: list, rev: bool = False) -> None: + plt.axis('off') # enlève les axes + if rev : + plt.imshow(img, cmap='gray',origin='lower', aspect='auto') + else : + plt.imshow(img, cmap='gray', aspect='auto') + + +## Traitement des images + +# color_to_grayscale [image] convertis l'image [image] en RGB en une image (np.array) en niveau de gris +def color_to_grayscale(image: list) -> np.array: + image = list(image) + r = [[] for x in range(len(image))] #image vide + for i in range (len(image)) : + for j in range(len(image[i])) : + valeur = 0.2126*image[i][j][0] + 0.7152*image[i][j][1] + 0.0722*image[i][j][2] #formule de conversion de noir et blanc a couleur + r[i].append(valeur) + + return np.array(r) + +# grayscale_to_black_and_white img prend en entrée l'image [img] et renvoie un image en noir et blanc. les pixels +# de couleur superieure à [seuil] vont être laissé dans leur couleur originale et ceux inferieur a seuil vont etre mis en noir +# par default, la fonction conserve uniquement les pixels parfaitement blanc +def grayscale_to_black_and_white(img: np.array, seuil:int = BLANC) -> np.array: + r = [[] for x in range(len(img))] + for i in range(len(img)): + for j in range(len(img[i])): + if img[i][j] < seuil : + r[i].append(0) + else : + r[i].append(int(img[i][j])) + return(np.array(r)) + +## Matrice de Hough + +# retourne la taille du tableau qui represente l'espace de hough. +# l'option precision change la taille du tableau selon la dimention x, c'est a dire selon les theta +def taille_Hough(img: list, precision: int = 1) -> tuple: + Px = len(img) + Py = len(img[0]) + hauteur = (Px ** 2 + Py ** 2)**0.5 # distance maximum (coin en bas à gauche) + largeur = 360 + return(round(hauteur), largeur*precision) + + +# rho_f(x, y, θ) retourne la distance a l'origine de la droite passant par le point (x, y) et d'angle θ +def rho_f(x: int, y: int, θ: float) -> float: + return(x*cos(θ) + y*sin(θ)) + + +#retourne la matrice de l'espace Hough de l'image img dans l'ordre m[r][theta] +def espace_Hough(img: list, precision: int = 1) -> list: + hauteur_H, largeur_H = taille_Hough(img, precision) + hauteur_I, largeur_I = len(img), len(img[0]) + #creer le tableau qui va contenir la matrice + matrice_hough = [[0 for i in range (largeur_H)] for i in range(hauteur_H)] + #on parcours toute l'image + for y in range(hauteur_I): + for x in range(largeur_I): + if img[y][x] >= BLANC: + for theta_deg in range(largeur_H): + theta_rad = theta_deg /(180 * precision) * pi + rho = round(rho_f(x, y, theta_rad)) + if rho>0: #comme on fait varier θ jusqu'a 2π, on ne garde que les valeurs positives + matrice_hough[rho][theta_deg] += 1 #on met l'origine du repere en bas de l'image + return(matrice_hough) + + +# retourne une liste de maximum locaux. +def maximum_global(m: list, d: int = 100): + return(peak_local_max(np.array(m), min_distance=d)) + + +## Géometrie + +# calcule l'équation d'une droite passant par deux points en coordonnées carthésiennes. +# la valeur de retour sera un couple (a, b) représentant la droite sous forme y = ax + b +def equation(point1: tuple, point2: tuple) -> tuple: + (x1,y1),(x2,y2) = point1,point2 + assert(x1 != x2), "les points sont alignés, la droite ne peut pas être prise en compte" + a = (y1 - y2) / (x1 - x2) #si la droite n'est pas verticale + b = y2 - a*x2 + return(a,b) + + +# retourne les coefficients de l'equation de la droite représentée par une distance a l'origine r et un angle theta +# sous la forme d'un couple (a, b) représentant la droite sous forme y = ax + b +def droite(r: float, theta: float, precision: int = 1) -> tuple: + theta = theta / precision + if theta == 0 or theta == 180: + return (0,r) + else : + theta_rad = (pi/180)*theta + a = -(cos(theta_rad)/sin(theta_rad)) + b = r/sin(theta_rad) + return(a,b) + +# Renvoie la distance euclidienne entre les points a: (x1, y1) et b : (x2, y2) +def distance (a: tuple, b: tuple) -> int: + (x1,y1),(x2,y2) = a,b + distance = ( (x1-x2)**2 + (y1 - y2)**2 )**0.5 + return(distance) + + +# calcule la matrice de Hough a l'aide de d'un programme en C qui utilise du multithreading. +# permet une vitesse d'execution beaucoup plus élevée : temps d'exécution divisé par 100 +# l'argument bypass permet de tester d'autres partie du code plus rapidement +def hough_c(image: list, precision: int = 10, bypass: bool = False) -> list: + if not bypass: + save_img_c(image) + subprocess.run(['../c/multi.out', str(precision)]) + mat = read_c() + return(mat) + +# renvoit la matrice renvoyée par le calcul de l'espace de hough en C +def read_c() -> list: + f = open("../c/out.txt","r") + m_read = f.readlines() + f.close() + return([[int (x) for x in y.split(",")] for y in m_read]) + + +# enregistre la liste [image] dans le dossier C sous le nom in.txt, pour pouvoir partager des informations entre C et python +def save_img_c(image: list) -> None: + f = open(f"../c/in.txt","w") + for i in image: + f.write(str(list(i)).replace(" ","").replace("[","").replace("]","") + "\n") + f.close() + + +#donne les intervalles de déb fin d'un segment de droite +def intervalle_une_droite(r: float, theta: float, plan: list, precision_h: int, blanc: int = BLANC) -> list: + (a,b) = droite(r, theta, precision_h) + largeur = len(plan) #axe des y + longueur = len(plan[0]) #axe des x + segments = [] + x, deb, fin = 0, 0, 0 + while x < longueur : + y = round(a*x + b) + if 0 < y < largeur : + if plan[y][x] >= blanc : + deb = x, y + while (0 < y < largeur) and x < longueur and (plan[y][x] >= blanc) : + x += 1 + y = round(a*x +b) + fin = x, y + segments.append((deb, fin)) + else : + x += 1 + else : + x += 1 + return(segments) + + +# Donne les intervalles (début,fin) d'une liste de droites +# in - listes_droites : couple (r, theta), plan: list +# out - [[(deb, fin) (...) (deb,fin)] [...] [(deb, fin) (...) (deb, fin)] +def intervalle(liste_droites: list, plan, precision_h:int) -> list: + n = len(liste_droites) + liste_intervalles =[] + for i in range (n): + r, theta = liste_droites[i][0], liste_droites[i][1] + segments = intervalle_une_droite(r, theta, plan, precision_h) + liste_intervalles.append(segments) + return(liste_intervalles) + + +# raccorde des segments qui sont peu éloignés (a une distance inferieur a distance_min) pour en faire une droite +def raccordement_un_intervalle(segments: list, distance_min:int) -> list: + new_segments = [] + retry = False + i = 0 + while i < len(segments) : + if (i < len(segments) -1 ) and (distance(segments[i][1], segments[i+1][0]) < distance_min) : + retry = True + new_segments.append((segments[i][0], segments[i+1][1])) + i += 1 + else : + new_segments.append(segments[i]) + i += 1 + if retry : + return(raccordement_un_intervalle(new_segments, distance_min)) + return(new_segments) + + +# raccorde des segments pour une liste d'intervalles d'une liste de droites +def raccordement(liste_segments: list, distance_min:int = 50) -> list: + new_segments = [] + for i in liste_segments: + new_segments.append(raccordement_un_intervalle(i, distance_min=distance_min)) + return(new_segments) + + +#enlève les intervalles trop petits, qui sont probablement des erreurs +def enlever_petits_intervalles_une_droite(segments: list, longueur_min: int) -> list: + new_segments =[] + for i in range (len(segments)): + if(distance(segments[i][0], segments[i][1])) > longueur_min: + new_segments.append(segments[i]) + return(new_segments) + + +# applique la fonction enlever_petits_intervalles_une_droite sur chaque segement +def enlever_petits_intervalles_general(liste_segments: list, longueur_min:int = 150) -> list: + new_segments =[] + for i in liste_segments: + new_segments.append(enlever_petits_intervalles_une_droite(i, longueur_min)) + return(new_segments) + + +# Retourne l'ensemble des points d'intersection entre deux segments +def intersection_deux_segments(segment1: tuple, segment2: tuple) -> tuple: + ((xdeb1,ydeb1),(xfin1,yfin1)),((xdeb2,ydeb2),(xfin2,yfin2)) = segment1,segment2 + (a1,b1), (a2,b2) = equation((xdeb1,ydeb1),(xfin1,yfin1)), equation((xdeb2,ydeb2),(xfin2,yfin2)) + if a1 != a2: + x_sol = (b2 - b1) / (a1 - a2) + y_sol = a1 * x_sol + b1 + if ((xdeb1 < x_sol < xfin1) or (xfin1 < x_sol < xdeb1)) and ((xdeb2 < x_sol < xfin2) or (xfin2 < x_sol < xdeb2)) and ((ydeb1 < y_sol < yfin1) or (yfin1 < y_sol < ydeb1)) and ((ydeb2 < y_sol < yfin2) or (yfin2 < y_sol < ydeb2)): + return (x_sol, y_sol) + +# calcule toutes les intersections entre une liste de segments +# type : [[droite i] [...] [droite j] ] -> ((droite i, droite j), (x, y)) +# avec (x,y) : les coordonnées du point d'intersection +def intersection_general(liste_segments): + n = len(liste_segments) + liste_intersection = [] + for i in range (n): + for j in range (n): + if i > j: # permet d'eviter les doublons. La matrice est alors inferieur gauche. + try: + x,y = intersection_deux_segments(liste_segments[i],liste_segments[j]) + liste_intersection.append(((liste_segments[i], liste_segments[j]), (x,y))) + except Exception as e: + # Il peut ne pas y avoir d'intersection entre ces deux segements, la fct intersection_deux_segments ne renvoie + # rien, on a donc une erreur et c'est pour cela qu'on passe. + pass + + return (liste_intersection) + + +# transforme la une liste de liste en liste d'elements +def flatten(liste: list) -> list: + flat_list = [] + for sublist in liste: + for item in sublist: + flat_list.append(item) + return(flat_list) + + +# calcule la matrice d'intersections +# renvoie : matrice[i][j] =(x,y) <=> (x,y) est le point d'intersection entre le ième et le jème segment de [liste_segments] +def matrice_intersections(liste_intersections: list, liste_segments: list): + len_segments = len(liste_segments) # nombres de routes + len_intersection = len(liste_intersections) # nombre d'intersections + m = [[ (-1,-1) for i in range(len_segments)] for j in range(len_segments)] + for z in range (len_intersection): + (droite_1,droite_2), (x,y) = liste_intersections[z] + index_i = liste_segments.index(droite_1) + index_j = liste_segments.index(droite_2) + m[index_i][index_j] = (x,y) + return(m) + + +# renvoie la matrice d'adjacence correspondant au graphe de notre ville +# on definit m[i][i] comme les coordonnées du point i et m[i][j] la distance entre le sommet i et le sommet j si ils sont relié. sinon, -1 +# add_str permet de modifier le nom du fichier +def mat_adjacence(mat_intersections: list, liste_intersections: list, img_NB: list, img: list, add_str: str = ""): + liste_pts = list(set([x[1] for x in liste_intersections])) # enlève les doublons + n_r = len(liste_pts) # nb de routes + n_i = len(mat_intersections[0]) # nb de sommets + graphe_return = [[-1 for y in range(n_r)] for x in range(n_r)] + for i in range(n_r): + graphe_return[i][i] = liste_pts[i] # definition de m[i][i] + for j in range(n_r): + if i > j: + if voisin(liste_pts[i], liste_pts[j], img_NB): + d = int(distance(liste_pts[i], liste_pts[j])) + graphe_return[i][j] = d + graphe_return[j][i] = d + plt.plot((liste_pts[i][0], liste_pts[j][0]),(liste_pts[i][1], liste_pts[j][1]) ) + return(graphe_return) + + +# renvoie si a et b sont des voisins. +# pour cela, on verifie si il y a plus de [p]% de points de route entre les deux +def voisin(pt1: tuple, pt2 : tuple, img : list, p: int = 60) -> tuple: + a, b = equation(pt1, pt2) + pts = [x for x in range(int(min(pt1[0], pt2[0])),int(max(pt1[0], pt2[0])))] # valeurs que doit prendre x sur le trajet + nb_tot = len(pts) + def f(x): # équation de la droite + return(int(a*x+b)) + c = 0 # compteur de points blanc + for x in pts: # on compte le nombre de points blanc entre les deux points + if f(x) < len(img): + if img[f(x)][x] == BLANC : + c += 1 + if nb_tot == 0 : + return(False) + return((c/(nb_tot)*100)>p) + + +# Applique l'algorithme de dijkstra à une matrice d'adjacence et un sommet de début donné +# Renvoie la liste des distances du sommet de départ à tout les autres +# la liste des sommets qui ont découvert t[i] (liste des parents) +def dijkstra(mat_adj: list, sommet_deb:int, sommet_fin: int): + vue = [sommet_deb] # liste des sommets déjà vu + n = len(mat_adj) + dist = [ -1 for i in range(n)] # liste des distances + dist[sommet_deb] = 0 + liste_parents = [ -1 for i in range(n)] + nb = 0 + for _ in range(n-1): + nb += 1 + mini = MAX_INT # distance minimum des voisins + sommet_min = -1 + a_decouvert = 0 + for cur in range(n): # on parcours les sommets + if cur in vue : # si le sommet a déjà été vu + for vois in range(n): # on parcours ses voisins + if mat_adj[cur][vois] != -1 and not (vois in vue): # si il est de distance inferieure + if mat_adj[cur][vois] + dist[cur] < mini : # on le définit comme meilleur candidat et on continue + mini = mat_adj[cur][vois] + dist[cur] + sommet_min = vois + a_decouvert = cur + liste_parents[sommet_min] = a_decouvert # on conserve le meilleur candidat + dist[sommet_min] = mini + vue.append(sommet_min) + if sommet_min == sommet_fin: + break + return(liste_parents,dist, nb) + +# Renvoie la liste des sommets à parcourir pour aller du sommet de début au sommet de fin à partir du tableau de dijkstra +# est appelé avec la liste des pères renvoyé par dijkstra +def chemin(liste_decouvre: list, sommet_fin:int) -> list: + liste_sommet = [] + i = sommet_fin + while liste_decouvre[i] != -1 : # -1 est le sommet de départ + liste_sommet.append(i) # on est sensé ajouter les elements au début de la liste. On les ajoutes à la fin, puis on retourne la liste + i = liste_decouvre[i] + liste_sommet.append(i) + liste_sommet = liste_sommet[::-1] + return(liste_sommet) + + +# Renvoie la liste des sommets à parcourir à partir du graphe d'adj, +# le sommet de deb, repéré par son indice dans la matrice et le sommet de fin, repéré de même +def plus_court_chemin_dijkstra(mat_adj: list, sommet_deb:int, sommet_fin:int) -> list: + start_dijkstra = time() + liste_dec, _, nb_sommets = dijkstra(mat_adj, sommet_deb, sommet_fin) + pcc = chemin(liste_dec,sommet_fin) + return(pcc, nb_sommets, (time() - start_dijkstra)) + + +# Algorithme A* +# [mat_adj] : matrice d'adjacence avec t[i][i] les coordonnées du point +# [h] heuristique : fonction d'estimation de la distance +# [s] sommet de depart +# [t] sommet d'arrivé +def a_etoile(mat_adj, h, s, t): + n = len(mat_adj[0]) # nb de sommets + f = FP() + f.push(s, 0) + liste_distance = [ MAX_INT for i in range(n)] + liste_distance[s] = 0 + nb = 0 + while not(f.is_empty()): + nb +=1 + v = f.pop() + if v == t : # si on a atteint le sommet voulu + return(reconstruire(liste_distance, mat_adj, t), nb) + else : # sinon on s'applique sur chaque voisins + for u in voisins_a(v, mat_adj): + if liste_distance[u] > liste_distance[v] + mat_adj[v][u] : + liste_distance[u] = liste_distance[v] + mat_adj[v][u] + f.push(u, liste_distance[u] + h(mat_adj, u, t)) + + +# voisins pour la fonction A* +# s est le sommet que l'on considère +def voisins_a(s: int, matrice_adj: list) -> list: + n = len(matrice_adj) + l = [] # liste des voisins + for i in range(n): + if i != s and matrice_adj[s][i] != -1: # si [i] et [s] sont voisin dans la matrice d'adjacence + l.append(i) + return(l) + + +# reconstuit le chemin de t a s, pour la fonction A* +# d est la liste des distances entre le sommet original et le sommet d[i] +# t est le sommet d'arrivée +def reconstruire(d: list, mat_adj: list, t: int) -> list: + r = d[t] # r est la distance restante avec le sommet de depart + chemin = [t] # on initialise le chemin avec le sommet d'arrivé + while r!=0 : # tant qu'il reste de la distance jusqu'au sommet de de depart + for u in range(len(mat_adj)): # on parcours tout les voisind de t + if u!=t : # - + if r == d[u] + mat_adj[u][t] : # si la distance restante est la distance entre le sommet u et le sommet de + chemin.append(u) # départ + la distance entre u et le sommet actuel + r = r - mat_adj[u][t] # on diminue la distance + t = u # on passe par ce sommet et on recommence l'algorithme + break + chemin.reverse() + return(chemin) + + +# Heuristique pour A* +# On utilise la distance à vol d'oiseau. +# C'est bien une heuristique acceptante. +def heuristique(mat_adj: list, sommet_act:int , sommet_fin:int) -> int: + x1,y1 = mat_adj[sommet_act][sommet_act] + x2,y2 = mat_adj[sommet_fin][sommet_fin] + dist_min = distance((x1,y1), (x2,y2)) + return (dist_min) + + +# determine le nombre de points blanc d'une droite, en pouvant avancer de d points sans reset le compte +# droite est donnée sous la forme d'un couple (a, b) +# l'image dois être l'image en noir et blanc +# [d] est la distance maximale entre deux points blancs +def nombre_points_blanc(droite: tuple, img: list, d: int = MAX_INT) -> int: + n = len(img) # dimension de l'image + m = len(img[1]) # dimension de l'image + distance = d # distance max + liste_dist = [] # liste des suites de points blanc + (a,b) = droite + nmbre_points = 0 # nombre de points blanc + for x in range (m): + y = a*x+b + if (y=0): # si on est bien dans l'image + if distance == 0 : # si on a trop avancé dans l'image sans croiser de points blancs + liste_dist.append(nmbre_points) # + nmbre_points = 0 # on reset le compte de points + distance = d # on reset la distance + if (img[int(y)][int(x)] >= BLANC): # si le pixel est blanc + nmbre_points+=1 # on incremente la valeur du nb de points + distance = d # on reset la distance + else : + distance -= 1 # on decremente la distance + liste_dist.append(nmbre_points) + return(max(liste_dist)) # valeur maximale d'une suite de points blanc + + +# fusionne les droites similaires +# liste est la liste des droites parametrée selon r et theta +# dico est un dictionnaire représentant les droites traitées et non traitées +# nb_lim est le nombre de pixel maximum de difference permettant la fusion de deux droites +# a_fact et b_fact sont les facteurs permettant de choisir les droites a prendres en comptes entre elles, en parametrage carthesien +def fusion_droites (liste: list, imageNB, dico: dict, a_fact: float, b_fact: float, nb_lim: int, precision_hough: int) -> list: + nouvelle_liste = [] + modif = False + for i in range(len(liste)): + (r1,theta1) = liste[i] + for j in range(1, len(liste)): + (r2, theta2) = liste[j] + a1, b1 = droite(r1, theta1, precision_hough) + a2, b2 = droite(r2, theta2, precision_hough) + if r1 == r2 and theta1==theta2: # on ne se considere pas soit même + pass + else : + if (dico[(r1, theta1)]!=1) and (dico[(r2, theta2)]!=1): # on a déjà traiter la droite + min_a = min(abs(a1), abs(a2)) + moy_a = abs(a1 + a2)/2 + a_lim = a_fact + a_fact * min_a**2 + if (abs(a1-a2) < a_lim): # si les droites sont suffisament proche selon les a + b_lim = b_fact + moy_a**2 * b_fact + if abs(b1 - b2) < b_lim : # si les droites sont suffisament proche selon des b + n1 = nombre_points_blanc((a1,b1), imageNB, 5) + n2 = nombre_points_blanc((a2,b2), imageNB, 5) + if (abs(n1-n2) < nb_lim): # on conserve la droite moyenne entre les deux + ntot = n1 + n2 # -- + r_moy = (r1 + r2)/2 # -- + theta_moy = (theta1 + theta2)/2 # -- + nouvelle_liste.append((r_moy, theta_moy)) # -- + elif ( n1 > n2 ): # on conserve uniquement la droite 1 + nouvelle_liste.append((r1, theta1)) # -- + else : # on conserve uniquement la droite 1 + nouvelle_liste.append((r2, theta2)) # -- + modif = True + dico[(r1, theta1)] = 1 + dico[(r2, theta2)] = 1 + if dico[(r1,theta1)]!=1: + nouvelle_liste.append((r1, theta1)) + return(list(set(nouvelle_liste)), modif, dico) + +# applique en boucle la fonction fusion_droites jusqu'à qu'il n'y ai plus de modifications +def fusion_droites_rec(liste: list, image: list , a_fact: int, b_fact: int, nb_lim: int, precision_hough: int) -> list: + dico = {} # Cas initial + for (r1,theta1) in liste: + dico[(r1,theta1)] = 0 + nv_liste, booleen, dico = fusion_droites(liste, image, dico, a_fact, b_fact, nb_lim, precision_hough) + + while booleen: # Le booleen représente si une modification a été faite ou non + dico = {} # si c'est le cas on recommence + for (r1,theta1) in nv_liste: + dico[(r1,theta1)] = 0 + nv_liste, booleen, dico = fusion_droites(nv_liste, image, dico, a_fact, b_fact, nb_lim, precision_hough) + return(nv_liste) + + +# trie une liste selon la deuxieme coordonnée +def sort_list_theta(liste: list) -> list: + liste = [(x[1], x[0]) for x in liste] + liste.sort() + return([(x[1], x[0]) for x in liste]) + + +# Permet de fusionner les intersections qui sont proches les une des autres, à partir de la matrice d'adjacence. +# distance_max est la distance à partir de laquelle on arrête de fusionner les droites. +def cluster(matrice_adj: list, distance_max: int) -> list: + for i in range(len(matrice_adj)): # on parcours l'intégralité des sommets + for j in range(i+1, len(matrice_adj)): + point1 = matrice_adj[i][i] + point2 = matrice_adj[j][j] + x1, y1 = point1 + x2, y2 = point2 + if distance(point1, point2) < distance_max and (x1,y1) != (-1, -1) and (x2,y2) != (-1, -1): # on supprime la valeur j et on introduit les anciennes valeurs dans i + matrice_adj[i][i] = (int((x1+x2)/2), int((y1+y2)/2)) # la coordonnée du point est la moyenne des deux + matrice_adj[j][j] = (-1, -1) + for k in range(len(matrice_adj)): + if k!=i and k != j: + if matrice_adj[j][k] != -1: + if matrice_adj[i][k] != -1: + matrice_adj[i][k] = (matrice_adj[i][k] + matrice_adj[j][k])/2 + matrice_adj[k][i] = (matrice_adj[k][i] + matrice_adj[k][j])/2 + else : + matrice_adj[i][k] = matrice_adj[j][k] + matrice_adj[k][i] = matrice_adj[k][j] + matrice_adj[j][k] = -1 + matrice_adj[k][j] = -1 + return(cluster(matrice_adj, distance_max)) + return(matrice_adj) + + +def matrice_recadre(points, mat): + belle_matrice = [[0 for i in range(len(points))] for i in range(len(points))] + for i in range(len(points)): + for j in range(len(points)): + belle_matrice[i][j] = mat[points[i]][points[j]] + return(belle_matrice) + + + +def main(lien: str, precision_hough:int = 1): + print("Lecture de l'image.") # uniquement a partir de plan + image = read_img(lien) + print("Conversion en niveau de gris.") + image_grayscale = color_to_grayscale(image) + print("Conversion en noir et blanc.") + image_NB = grayscale_to_black_and_white(image_grayscale) + print("Calcul de l'espace de Hough.") + espace_h = hough_c(image_NB, precision_hough) + save_hough(espace_h) + print("Récuperation des droites.") + liste_droites1 = peak_local_max(np.array(espace_h), min_distance=70, num_peaks=500, threshold_rel=0.23) + print(f"Nombre de droites : {len(liste_droites1)}.") + liste_droites1 = [list(x) for x in list(liste_droites1)] + save_with_droites(image, liste_droites1, precision_hough, "01. toutes les droites") + liste_droites2 = fusion_droites_rec(sort_list_theta(liste_droites1), image_NB, 0.5, 50, 10, precision_hough) + save_with_droites(image, liste_droites2, precision_hough, "02. premier filtrage des droites") + liste_droites3 = fusion_droites_rec(sort_list_theta(liste_droites2), image_NB, 0.3, 75, 10, precision_hough) + save_with_droites(image, liste_droites3, precision_hough, "03. deuxieme filtrage des droites") + print("Récupération de la listes des intervalles.") + liste_intervalles = intervalle(liste_droites3, image_NB, precision_hough) + save_liste_inter(liste_intervalles, image, "04. intervalles") + print("Raccorde les droites.") + liste_segments = raccordement(liste_intervalles, 28) + save_liste_inter(liste_segments, image, "05. raccordement (28)") + print("enlève les petits bouts de droite") + liste_segments = enlever_petits_intervalles_general(liste_segments) + save_liste_inter(liste_segments, image, "06. segments (28)") + liste_segments = flatten(liste_segments) + liste_intersections = intersection_general(liste_segments) + save_intersection(liste_intersections, image) + print("transformation en matrice d'intersections") + matrice_inter = matrice_intersections(liste_intersections, liste_segments) + matr_adj = mat_adjacence(matrice_inter, liste_intersections, image_NB, image) + print("clusterisation") + matrice_adj_cluster = cluster(matr_adj, 28) + save_cluster(matrice_adj_cluster, image) + save_graphe(matrice_adj_cluster, image) + #save_full_graphe(matrice_adj_cluster, image) + + + sommet_depart = 63 + sommet_arrivee = 61 + # d'autres valeurs intéressantes sont possibles : 61->73 ; 1->24 + + + chemin_dijksra, nb_sommets_dijkstra, duree_dijksra = plus_court_chemin_dijkstra(matr_adj, sommet_depart, sommet_arrivee) + + start_a_star = time() + chemin_a_star, nb_sommets_a_star = a_etoile(matr_adj, heuristique, sommet_depart, sommet_arrivee) + duree_a_star = (time() - start_a_star) + + print(f"chemin dijkra : {chemin_dijksra}, temps d'execution : {duree_dijksra}, sommets parcourus : {nb_sommets_dijkstra}") + print(f"chemin A*: {chemin_a_star}, temps d'execution : {duree_a_star}, sommets parcourus : {nb_sommets_a_star}") + save_chemin(chemin_dijksra, matrice_adj_cluster, image, "dijkstra", "g") + save_chemin(chemin_a_star, matrice_adj_cluster, image, "a star", "k") + + return(matrice_adj_cluster) + + + + +mat = main("https://server.ip/TIPE/paris2.png", 100) + + +#points = [25, 41, 3, 14, 52, 53, 54] +print(matrice_recadre([25, 41, 3, 14, 52, 53, 54], mat)) + \ No newline at end of file diff --git a/Python Files/modules.py b/Python Files/modules.py new file mode 100644 index 0000000..7fe73de --- /dev/null +++ b/Python Files/modules.py @@ -0,0 +1,19 @@ +class FP: + def __init__(self): + self.tab_elem = [] # elements + self.tab_prio = [] # priorité + + def push(self, sommet, prio): #ajoute l'élément sommet a la file de priorité + self.tab_elem.append(sommet) + self.tab_prio.append(prio) + + def pop(self): # sort l'élément de priorité minimale + indice = self.tab_prio.index(min(self.tab_prio)) + elem = self.tab_elem[indice] + self.tab_prio.pop(indice) + self.tab_elem.pop(indice) + return(elem) + + def is_empty(self): + return(len(self.tab_prio) == 0) +