Przeglądaj źródła

plein de changements, ça va plus vite

Hvrnbi 6 miesięcy temu
rodzic
commit
b8811c8ea2
1 zmienionych plików z 126 dodań i 201 usunięć
  1. 126 201
      main.py

+ 126 - 201
main.py

@@ -2,12 +2,13 @@
 
 
 from PIL import Image
 from PIL import Image
 from numpy import asarray, ndarray, array, uint8, delete
 from numpy import asarray, ndarray, array, uint8, delete
-from random import choice
+from random import choices
+from copy import deepcopy
 
 
 
 
 ### Fonctions ###
 ### Fonctions ###
 
 
-def import_image(chemin: str) -> ndarray :
+def import_image(chemin: str, resize: bool = False) -> ndarray :
     """Charge l'image donnée, la passe en nuances de gris, et la convertit en tableau (de numpy)"""
     """Charge l'image donnée, la passe en nuances de gris, et la convertit en tableau (de numpy)"""
 
 
     ## Chargement de l'image ## 
     ## Chargement de l'image ## 
@@ -32,6 +33,9 @@ def import_image(chemin: str) -> ndarray :
     # On utilise la méthode .convert() de PIL
     # On utilise la méthode .convert() de PIL
     image = image.convert("L")
     image = image.convert("L")
 
 
+    if resize:
+        image = image.resize((256, 256))
+
 
 
     ## Conversion en tableau ##
     ## Conversion en tableau ##
 
 
@@ -90,243 +94,164 @@ def visible(clou1: tuple, clou2: tuple) -> bool:
     """Renvoie True si les clous se voient, False sinon"""
     """Renvoie True si les clous se voient, False sinon"""
     return clou1[2] != clou2[2]
     return clou1[2] != clou2[2]
 
 
-def liste_clous_al(liste_clous : list, taille_l : int) -> list:
-    """On créer une liste de clou choisis aléatoirement"""
-    lst = []
-    for i in range(taille_l):
-        clou = clou_choisi_al(liste_clous)
-        lst += [clou]
-    return lst
-
-def clou_choisi_al(liste_clous : list) -> tuple:
-    """On choisie un clou aléatoirement"""
-    return choice(liste_clous)
-
 def tracer_droite(clou1: tuple, clou2: tuple, image: ndarray) -> ndarray:
 def tracer_droite(clou1: tuple, clou2: tuple, image: ndarray) -> ndarray:
     """Trace une droite d'un clou à un autre."""
     """Trace une droite d'un clou à un autre."""
     if visible(clou1, clou2):
     if visible(clou1, clou2):
-        liste_points = liste_points_traverses(clou1, clou2, image)
+        liste_points = liste_points_traverses(clou1[0], clou1[1], clou2[0], clou2[1])
         
         
         for i in range(0, len(liste_points)):
         for i in range(0, len(liste_points)):
             ligne = liste_points[i][1]
             ligne = liste_points[i][1]
             col = liste_points[i][0]
             col = liste_points[i][0]
             image = assombrir_pixel(image, ligne, col)
             image = assombrir_pixel(image, ligne, col)
-    else:
-        print("il est impossible de tracer un trait sur le même bord")
 
 
     return image
     return image
 
 
-def liste_points_traverses(clou1: tuple, clou2: tuple, image: ndarray) -> list:
-    """Renvoie les points que le segment [coul1, clou2] traverse"""
+def liste_points_traverses(x1: int, y1: int, x2: int, y2: int) -> list:
+    """Renvoie les points que le segment [(x1, y1), (x2, y2)] traverse"""
     liste_res = []
     liste_res = []
-    liste_motif = liste_points_traverses_par_motif(clou1, clou2, image)
+    a, b = calcul_droite(x1, y1, x2, y2)
 
 
-    l = get_longueur_img(image)
-    h = get_hauteur_img(image)
+    # Si l'angle entre l'axe des abscisses et la droite est compris entre -45° et 45°
+    if -1 <= a <= 1:
+        if x1 > x2:
+            # On cherche y pour chaque x, donc on commence du point dont le x est le plus proche de 0
+            x1, y1, x2, y2 = x2, y2, x1, y1
 
 
-    x1 = clou1[0]
-    x2 = clou2[0]
-    y1 = clou1[1]
-    y2 = clou2[1]
+        x = x1 + 1
+        y = y1
+        liste_res.append((x1, y))
 
 
-    # On inverse l'ordre des clous si besoin, ça pourrait être fait de façon plus propre
-    if x1 > x2:
-        clou1, clou2, x1, x2, y1, y2 = clou2, clou1, x2, x1, y2, y1
-    elif x1 == x2:
-        if y1 > y2:
-            clou1, clou2, x1, x2, y1, y2 = clou2, clou1, x2, x1, y2, y1
+        while x <= x2:
+            y = round(a * x + b)
+            liste_res.append((x, y))
+            x += 1
 
 
-    # On applique le motif depuis le point de départ, et on ajoute les points obtenus dans la liste si ils rentrent dans l'image
-    for i in range(0, len(liste_motif)):
-            if (liste_motif[i][0] + x1 < l) and (0 <= liste_motif[i][1] + y1) and (liste_motif[i][1] + y1 < h):
-                liste_res = liste_res + [(liste_motif[i][0] + x1, liste_motif[i][1] + y1)]
-        
+    else:
+        if y1 > y2:
+            # On cherche x pour chaque y, donc on commence du point dont le y est le plus proche de 0
+            x1, y1, x2, y2 = x2, y2, x1, y1
+
+        if a != 999999999 and b != 999999999:
+            x = x1
+            y = y1 + 1
+            liste_res.append((x, y1))
+            while y <= y2:
+                x = round((y - b) / a)
+                liste_res.append((x, y))
+                y += 1
+
+        # Gestion des droites verticales
+        else:
+            x = x1
+            y = y1 + 1
+            liste_res.append((x, y1))
+            for i in range(y, y2 + 1):
+                liste_res.append((x, i))
+    
     return liste_res
     return liste_res
 
 
 
 
-def liste_points_traverses_par_motif(clou1: tuple, clou2: tuple, image: ndarray) -> list:
-    """Renvoie la liste des points que le motif de la doite passant par clou1 et clou2 traverse"""
-    liste_res = []
-
-    l = get_longueur_img(image)
-    h = get_hauteur_img(image)
+def calcul_droite(x1: int, y1: int, x2: int, y2: int) -> tuple:
+    """Renvoie le coefficient directeur et l'ordonnée à l'origine de la droite passant par (x1, y1) et (x2, y2)"""
 
 
-    x1 = clou1[0]
-    x2 = clou2[0]
+    if x1 != x2:
+        a = (y2 - y1) / (x2 - x1)
+        b = y1 - x1 * a
 
 
-    # On échange les clous si besoin, pourrait toujours être fait plus proprement
-    if x1 > x2:
-        clou1, clou2 = clou2, clou1
     # Gestion des droites verticales
     # Gestion des droites verticales
-    elif x1 == x2:
-        liste_res = [(0, i) for i in range(0, h)]
+    else:
+        a = 999999999
+        b = 999999999
+
+    return (a, b)
+
+
+def liste_clous_al(liste_clous : list, taille_l : int) -> list:
+    """On créer une liste de clou choisis aléatoirement"""
+    lst_res = choices(liste_clous, k=taille_l)
+    return lst_res
 
 
-    a, b = calcul_droite(clou1, clou2)
 
 
-    y1 = clou1[1]
-    y2 = clou2[1]
+def reduction_erreur_droite(x1: int, x2: int, y1: int, y2: int, img1:  ndarray, img2: ndarray) -> float:
+    """Renvoie la différence entre l'erreur de l'image cible et de l'image actuelle, et l'erreur de l'image cible et de l'image actuelel si on traçait la droite donnée"""
+    lst_pts = liste_points_traverses(x1, x2, y1, y2)
+    err_act = 0
+    nb_pts = len(lst_pts)
 
 
-    mu = 0
+    if (x1 == 0 and x2 == 0) or (x1 == get_longueur_img(img2) and x2 == get_longueur_img(img2)) or (y1 == 0 and y2 == 0) or (y1 == get_hauteur_img(img2) and y2 == get_hauteur_img(img2)):
+        return 999999999
 
 
-    # Si la droite "descend"
-    if y1 < y2:
-        # On teste tous les pixels, ça peut être grandement optimisé
-        for x in range(0, l):
-            for y in range(0, h):
-                # On calcule un "reste" (ax + by) qui lorsqu'il se trouve entre mu et mu + max(a, b) permet de créer un motif. On dessine ici le motif à partir de l'origine
-                if (mu <= a * x + b * y) and (a * x + b * y < mu + max(a, b)):
-                    liste_res = liste_res + [(x, y)]
+    for i in range(0, nb_pts):
+        err_act += erreur_point(lst_pts[i], img1, img2)
 
 
-    # Si la droite "monte"
+    new_err = 0
+
+    for i in range(0, nb_pts):
+        new_err += erreur_point_trace(lst_pts[i], img1, img2)
+    
+    return (new_err - err_act) / nb_pts
+
+
+def erreur_point(p: tuple, img1: ndarray, img2: ndarray):
+    """Renvoie la différence entre la valeur du pixel sur l'image 1 et la valeur du pixel sur l'image 2"""
+    return abs(img1[p[1]][p[0]] - img2[p[1]][p[0]])
+
+
+def erreur_point_trace(p: tuple, img1: ndarray, img2: ndarray):
+    """Renvoie la différence entre la valeur du pixel sur l'image 1 et la valeur du pixel sur l'image 2 lorsqu'on assombrit ce pixel"""
+    if img2[p[1]][p[0]] >= 51:
+        return abs(img1[p[1]][p[0]] - img2[p[1]][p[0]] + 51)
     else:
     else:
-        for x in range(0, l):
-            for y in range(0, h):
-                # Pareil qu'au dessus, sauf que le reste vaut (ax - by) et qu'on trace le motif à partir du point en bas à gauche
-                if (mu <= a * x - b * y) and (a * x - b * y < mu + max(a,  b)):
-                    liste_res = liste_res + [(x, - y)]
+        return erreur_point(p, img1, img2)
 
 
-    return liste_res
 
 
-def calcul_droite(clou1: tuple, clou2: tuple) -> tuple:
-    """Renvoie les coordonnées du vecteur orthogonal au vecteur allant de clou1 à clou2"""
-    x1 = clou1[0]
-    x2 = clou2[0]
-    y1 = clou1[1]
-    y2 = clou2[1]
+def trouve_meilleure_droite(img1: ndarray, img2: ndarray, clou_dep: tuple, lst_clous: list) -> tuple:
+    """Renvoie le clou dont la droite le traversant lui et le clou de départ réduit le plus l'erreur entre les deux images"""
+    liste_c = liste_clous_al(lst_clous, len(lst_clous) // 2)
+    liste_red_err = []
 
 
-    a = y1 - y2
-    b = x2 - x1    
+    for i in range(0, len(liste_c)):
+        liste_red_err.append(reduction_erreur_droite(clou_dep[0], clou_dep[1], liste_c[i][0], liste_c[i][1], img1, img2))
+    
+    red_err = min(liste_red_err)
+    meilleur_clou = liste_c[liste_red_err.index(red_err)]
 
 
-    return (a, b)
+    return (meilleur_clou, red_err)
 
 
-def lst_zone(image: ndarray) -> list:
-    """Renvoie une liste avec les coordonées de chaques zones"""
-    lst = []
-    hauteur = get_hauteur_img(image)
-    longueur = get_longueur_img(image)
-    pas = 10
-    for i in range(0,longueur,pas):
-        for j in range(0,hauteur, pas):
-            lst += [(j, i)] #prends les coordonnées du pixel en haut à gauche de chaque zone
-    return lst  
-
-def erreur_moyenne_zone(image1: ndarray, image2: ndarray, zone : tuple, longueur : int)  -> float:
-    """Renvoie l'erreur moyenne pour une zone"""
-    haut = get_hauteur_img(image1)
-    long = get_longueur_img(image1)
-    coo1 = zone[0]
-    coo2 = zone[1]
-    pix1 = image1[coo1][coo2]
-    pix2 = image2[coo1][coo2]
-    diff = abs(pix1 - pix2)
-    for i in range(coo1, longueur):
-        for j in range(coo2, longueur):
-            if i != 0 and j != 0 :
-                if i > long and j > haut:
-                    pix1 = image1[i][j]
-                    pix2 = image2[i][j]
-                    diff += abs(pix1 - pix2)
-    return (diff / (longueur * longueur *255)) * 100
-
-def erreur(image1: ndarray, image2: ndarray, zones : list, longueur : int) -> float:
-    """Renvoie l'erreur moyenne entre les deux images"""
-    moy = 0
-    for i in range(len(zones)):
-        moy += erreur_moyenne_zone(image1, image2, zones[i], longueur)
-    return moy/len(zones)
-
-def teste_5_clous_al(img1: ndarray, img2: ndarray, clou_dep, l_clous_img2 :list):
-    """Calcul quel clou reduis le plus l'erreur moyenne"""
-    lst_c_al = liste_clous_al(l_clous_img2, 50)
-    zones = lst_zone(img1)
-    erreur_dep = erreur(img1, img2, zones, 10)
-    nv_erreur = erreur_dep
-    i = 0
-    clou = lst_c_al[0]
-    while i < len(lst_c_al):
-        img_temp = deepcopy(img2)
-        clou_arr = lst_c_al[i]
-        img_temp = tracer_droite(clou_dep, clou_arr, img_temp)
-        erreur_temp = erreur(img1, img_temp, zones, 10)
-        if erreur_temp < nv_erreur:
-            nv_erreur = erreur_temp
-            clou = clou_arr
-        i += 1
-    return (clou, nv_erreur)
-
-def tracer_image(img1: ndarray, img2: ndarray, clou_dep, l_clous_img2 :list):
-    """Fais un filage de la première image sur la deuxième image qui est blanche."""
-    compteur = 0
-    zones = lst_zone(img1)
-    erreur_d = erreur(img1, img2, zones, 10)
-    derniere_erreur = erreur_d
-    droites_tracees = 0
-    while compteur < 8 and droites_tracees < 100: 
-        res = teste_5_clous_al(img1, img2, clou_dep, l_clous_img2)
-        clou_arr = res[0]
-        derniere_erreur = res[1]
-        img2 = tracer_droite(clou_dep, clou_arr, img2)
-        if derniere_erreur >= erreur_d:
-            compteur += 1
-        droites_tracees = droites_tracees + 1
-        clou_dep = clou_arr
-    return img2
-
-
-
-
-
-### Tests (à nettoyer) ###
-
-image = import_image("images/cercle.jpg")
-# print(image)
-# print(len(image))
-# print(len(image[0]))
-
-image_blanche = tab_image_blanche(get_longueur_img(image), get_hauteur_img(image))
-
-image_noir = tab_image_noir(get_longueur_img(image), get_hauteur_img(image))
-
-img = Image.fromarray(uint8(image_blanche))
-
-for i in range(0, 50):
-    for j in range(0, 50):
-            image_blanche = assombrir_pixel(image_blanche, i, j)
-
-img = Image.fromarray(uint8(image_blanche))
-#img.show()
-
-#print(image_blanche)
-
-clous = generer_clous(image_blanche)
-#print(clous)
-
-test = tab_image_blanche(1000, 1000)
-c = generer_clous(test)
-
-for i in range(0, len(c)):
-    test = assombrir_pixel(test, c[i][0], c[i][1])
-test = tracer_droite(c[35], c[321], test)
-
-im_test = Image.fromarray(uint8(test))
-im_test.show()
-
-l_zones = lst_zone(image)
-# print(erreur_moyenne_zone(image, image_blanche,l_zones[500],10))
-
-img = tracer_image(image, image_blanche, (0,0,0), clous)
-
-imaaaaaage = Image.fromarray(uint8(img))
-imaaaaaage.show()
 
 
+def retrace_image(chemin_img_source: str, nom_image_sortie: str, resize: bool = False):
+    """Retrace l'image donnée avec des fils et sauvegarde dans le dossier resultat, au chemin donné"""
+    image = import_image(chemin_img_source, resize)
 
 
+    new_img = tab_image_blanche(get_longueur_img(image), get_hauteur_img(image))
 
 
-### TODO ###
+    clous = generer_clous(new_img)
 
 
-# On peut essayer de regarder des clous aléatoire plutôt que de tout tester
+    clou_dep = clous[0]
+    cpt_augmentation_err = 0
+    cpt_droites = 0
 
 
-# On regarde l'erreur sur la ligne qu'on vient de tracer, en faisant des moyennes par zones
+    while cpt_augmentation_err < 2:
+        meilleur_clou, reduction_err = trouve_meilleure_droite(image, new_img, clou_dep, clous)
+        new_img = tracer_droite(clou_dep, meilleur_clou, new_img)
+        clou_dep = meilleur_clou
+        cpt_droites += 1
 
 
-# Pas forcément la même résolution pour le calcul que pour l'image
+        if reduction_err >= 0:
+            cpt_augmentation_err += 1
+
+    print(cpt_droites)
+
+    image_res = Image.fromarray(uint8(new_img))
+    image_res.save("resultat/" + nom_image_sortie + ".png")
+
+
+
+### Tests ###
+
+retrace_image("images/losa.jpg", "losa-egzbogez")
+
+
+### TODO ###
 
 
 # Pas forcément mesurer l'erreur d'une bande de quelques pixels au bord si on met les clous dedans
 # Pas forcément mesurer l'erreur d'une bande de quelques pixels au bord si on met les clous dedans