Upload files to "Python Files"
This commit is contained in:
parent
94ea9b1a5a
commit
4e0c175bdc
Binary file not shown.
|
@ -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<dmin :
|
||||
dmin = dist
|
||||
classe[i][j] = m
|
||||
b = True
|
||||
return(b)
|
||||
|
||||
|
||||
# les centres bougent
|
||||
def barycentre() :
|
||||
global classe, im, nb_lignes, nb_colones, centres
|
||||
dico = dict()
|
||||
for i in range(nb_classe) :
|
||||
dico[i] = []
|
||||
for i in range(nb_lignes) :
|
||||
for j in range(nb_colones) :
|
||||
dico[classe[i][j]].append(im[i,j])
|
||||
for i in range(nb_classe) :
|
||||
r, g, b = 0, 0, 0
|
||||
for e in dico[i] :
|
||||
r += e[0]
|
||||
g += e[1]
|
||||
b += e[2]
|
||||
m = len(dico[i])
|
||||
centres[i] = [r//m, g//m, b//m]
|
||||
# kmoyennes
|
||||
i=0
|
||||
while classer() :
|
||||
i+=1
|
||||
print("\nstep"+str(i))
|
||||
print(centres)
|
||||
barycentre()
|
||||
im_classes = np.array([[centres[classe[i][j]] for j in range(nb_colones)] for i in range(nb_lignes)])
|
||||
res = Image.fromarray(im_classes, mode='RGB')
|
||||
|
||||
|
||||
# affichage,sauvegarde
|
||||
|
||||
colors = [[0,0,0],[150,0,0],[0,150,0],[0,0,150],[128,128,0],[0,160,160],[192,0,192],[230,230,230]] # liste des couleurs pour bien différencier les classes
|
||||
|
||||
# Enregistrer toutes les images
|
||||
for lettre in range(nb_classe):
|
||||
im_classes = np.array([[im[i][j] if classe[i][j] == lettre else (0, 0, 0) for j in range(nb_colones)] for i in range(nb_lignes)],dtype=np.uint8)
|
||||
print(im_classes)
|
||||
res = Image.fromarray(im_classes, mode="RGB")
|
||||
res.show()
|
||||
res.save(f"./out/2_{lettre}.jpg")
|
||||
|
||||
|
||||
b = np.array([[colors[classe[i][j]] for j in range(nb_colones)] for i in range(nb_lignes)],dtype=np.uint8)
|
||||
res = Image.fromarray( b,mode="RGB")
|
||||
res.show()
|
||||
res.save("./out/out.jpg")
|
|
@ -0,0 +1,651 @@
|
|||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import imageio as io
|
||||
from time import time
|
||||
from random import choice
|
||||
from math import pi, cos, sin, floor
|
||||
from skimage.feature import peak_local_max
|
||||
from os import system
|
||||
import subprocess
|
||||
|
||||
from modules import *
|
||||
# importation les fonctions permettant d'enregistrer des images
|
||||
from images import *
|
||||
|
||||
## Variables globales
|
||||
|
||||
BLANC = 254 # définit le seuil pour la couleur blanche
|
||||
MAX_INT = 42e42
|
||||
## Liens des images sources
|
||||
|
||||
|
||||
# Fonctions
|
||||
## Fonction de base sur l'image
|
||||
|
||||
# Inverse l'image selon la coordonnée y
|
||||
def inv_img_y(img: list) -> 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<n) and (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))
|
||||
|
|
@ -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)
|
||||
|
Loading…
Reference in New Issue