1
0

main.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. ### Modules ###
  2. from PIL import Image
  3. from numpy import asarray, ndarray, array, uint8, delete
  4. from random import choice
  5. ### Fonctions ###
  6. def import_image(chemin: str) -> ndarray :
  7. """Charge l'image donnée, la passe en nuances de gris, et la convertit en tableau (de numpy)"""
  8. ## Chargement de l'image ##
  9. # On essaie d'ouvrir l'image avec la librairie PIL
  10. try:
  11. image = Image.open(chemin)
  12. # Gestion d'erreur si le fichier n'est pas accessible ou n'existe pas
  13. except FileNotFoundError:
  14. print("Le chemin d'accès est invalide.")
  15. # TODO Set image sur une image blanche en cas d'erreur
  16. # Gestion d'erreur dans les autres cas
  17. except:
  18. print("Une erreur s'est produite")
  19. # TODO Pareil qu'au dessus
  20. ## Conversion de l'image en nuances de gris ##
  21. # On utilise la méthode .convert() de PIL
  22. image = image.convert("L")
  23. ## Conversion en tableau ##
  24. # On utilise la fonction asarrray() de numpy
  25. image_array = asarray(image)
  26. return image_array
  27. def tab_image_blanche(longueur: int, hauteur: int) -> ndarray:
  28. """Génère un tableau contenant des pixels blancs avec les dimensions données"""
  29. # On crée autant de lignes que de pixels de hauteur, et autant de pixels que de hauteur
  30. tab = [ [255] * longueur] * hauteur
  31. return array(tab)
  32. def tab_image_noir(longueur: int, hauteur: int) -> ndarray:
  33. """Génère un tableau contenant des pixels noirs avec les dimensions données"""
  34. # On crée autant de lignes que de pixels de hauteur, et autant de pixels que de hauteur
  35. tab = [ [0] * longueur] * hauteur
  36. return array(tab)
  37. def get_longueur_img(tab_img: ndarray) -> int:
  38. """Renvoie la longueur du tableau à deux dimensions (représentant une image) donné"""
  39. return len(tab_img[0])
  40. def get_hauteur_img(tab_img: ndarray) -> int:
  41. """Renvoie la hauteur du tableau à deux dimensions (représentant une image) donné"""
  42. return len(tab_img)
  43. def assombrir_pixel(tab_img: ndarray, ligne: int, colonne: int) -> ndarray:
  44. """Réduit de 51 (parce que c'est 255/5) les valeurs des couleurs du pixel à la position donnée."""
  45. if tab_img[ligne][colonne] >= 51:
  46. tab_img[ligne][colonne] = tab_img[ligne][colonne] - 51
  47. return tab_img
  48. def generer_clous(tab_img: ndarray) -> list:
  49. """Génère un tableau qui contient des tuples qui représentent des clous : deux coordonnées et un numéro de côté -> 0 pour le haut, 1 pour la droite, 2 pour le bas, 3 pour la gauche"""
  50. liste_res = []
  51. h = get_hauteur_img(tab_img)
  52. l = get_longueur_img(tab_img)
  53. for i in range(0, l, 10):
  54. liste_res = liste_res + [(i, 0, 0)]
  55. for i in range(10, h, 10):
  56. liste_res = liste_res + [(l - 1, i, 1)]
  57. for i in range(l - 11, -1, - 10):
  58. liste_res = liste_res + [(i, h - 1, 2)]
  59. for i in range(h - 11, 0, -10):
  60. liste_res = liste_res + [(0, i, 3)]
  61. return liste_res
  62. def visible(clou1: tuple, clou2: tuple) -> bool:
  63. """Renvoie True si les clous se voient, False sinon"""
  64. return clou1[2] != clou2[2]
  65. def clou_choisi_al(liste_clous : list) -> tuple:
  66. """On choisie un clou aléatoirement"""
  67. return choice(liste_clous)
  68. def tracer_droite(clou1: tuple, clou2: tuple, image: ndarray) -> ndarray:
  69. """Trace une droite d'un clou à un autre."""
  70. if visible(clou1, clou2):
  71. liste_points = liste_points_traverses(clou1, clou2, image)
  72. for i in range(0, len(liste_points)):
  73. ligne = liste_points[i][1]
  74. col = liste_points[i][0]
  75. image = assombrir_pixel(image, ligne, col)
  76. else:
  77. print("il est impossible de tracer un trait sur le même bord")
  78. return image
  79. def liste_points_traverses(clou1: tuple, clou2: tuple, image: ndarray) -> list:
  80. """Renvoie les points que le segment [coul1, clou2] traverse"""
  81. liste_res = []
  82. liste_motif = liste_points_traverses_par_motif(clou1, clou2, image)
  83. l = get_longueur_img(image)
  84. h = get_hauteur_img(image)
  85. x1 = clou1[0]
  86. x2 = clou2[0]
  87. y1 = clou1[1]
  88. y2 = clou2[1]
  89. # On inverse l'ordre des clous si besoin, ça pourrait être fait de façon plus propre
  90. if x1 > x2:
  91. clou1, clou2, x1, x2, y1, y2 = clou2, clou1, x2, x1, y2, y1
  92. elif x1 == x2:
  93. if y1 > y2:
  94. clou1, clou2, x1, x2, y1, y2 = clou2, clou1, x2, x1, y2, y1
  95. # 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
  96. for i in range(0, len(liste_motif)):
  97. if (liste_motif[i][0] + x1 < l) and (0 <= liste_motif[i][1] + y1) and (liste_motif[i][1] + y1 < h):
  98. liste_res = liste_res + [(liste_motif[i][0] + x1, liste_motif[i][1] + y1)]
  99. return liste_res
  100. def liste_points_traverses_par_motif(clou1: tuple, clou2: tuple, image: ndarray) -> list:
  101. """Renvoie la liste des points que le motif de la doite passant par clou1 et clou2 traverse"""
  102. liste_res = []
  103. l = get_longueur_img(image)
  104. h = get_hauteur_img(image)
  105. x1 = clou1[0]
  106. x2 = clou2[0]
  107. # On échange les clous si besoin, pourrait toujours être fait plus proprement
  108. if x1 > x2:
  109. clou1, clou2 = clou2, clou1
  110. # Gestion des droites verticales
  111. elif x1 == x2:
  112. liste_res = [(0, i) for i in range(0, h)]
  113. a, b = calcul_droite(clou1, clou2)
  114. y1 = clou1[1]
  115. y2 = clou2[1]
  116. mu = 0
  117. # Si la droite "descend"
  118. if y1 < y2:
  119. # On teste tous les pixels, ça peut être grandement optimisé
  120. for x in range(0, l):
  121. for y in range(0, h):
  122. # 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
  123. if (mu <= a * x + b * y) and (a * x + b * y < mu + max(a, b)):
  124. liste_res = liste_res + [(x, y)]
  125. # Si la droite "monte"
  126. else:
  127. for x in range(0, l):
  128. for y in range(0, h):
  129. # Pareil qu'au dessus, sauf que le reste vaut (ax - by) et qu'on trace le motif à partir du point en bas à gauche
  130. if (mu <= a * x - b * y) and (a * x - b * y < mu + max(a, b)):
  131. liste_res = liste_res + [(x, - y)]
  132. return liste_res
  133. def calcul_droite(clou1: tuple, clou2: tuple) -> tuple:
  134. """Renvoie les coordonnées du vecteur orthogonal au vecteur allant de clou1 à clou2"""
  135. x1 = clou1[0]
  136. x2 = clou2[0]
  137. y1 = clou1[1]
  138. y2 = clou2[1]
  139. a = y1 - y2
  140. b = x2 - x1
  141. return (a, b)
  142. def lst_zone(image) -> list:
  143. """Renvoie une liste avec les coordonées de chaques zones"""
  144. lst = []
  145. hauteur = get_hauteur_img(image)
  146. longueur = get_longueur_img(image)
  147. pas = 10
  148. for i in range(0,longueur,pas):
  149. for j in range(0,hauteur, pas):
  150. lst += [(i,j)] # prends les coordonnées du pixel en haut à gauche de chaque zone
  151. return lst
  152. def erreur_moyenne_zone(image1, image2, zone : tuple, longueur : int) -> float:
  153. """Renvoie l'erreur moyenne pour une zone"""
  154. haut = get_hauteur_img(image1)
  155. long = get_longueur_img(image1)
  156. coo1 = zone[0]
  157. coo2 = zone[1]
  158. pix1 = image1[coo1][coo2]
  159. pix2 = image2[coo1][coo2]
  160. diff = abs(pix1 - pix2)
  161. for i in range(coo1, coo1+longueur):
  162. for j in range(coo2, coo2+longueur):
  163. if i != 0 and j != 0:
  164. if i > long and j > haut:
  165. pix1 = image1[i][j]
  166. pix2 = image2[i][j]
  167. diff += abs(pix1 - pix2)
  168. return (diff / (longueur * longueur *255)) * 100
  169. def erreur(image1, image2, zones : list) -> float:
  170. """Renvoie l'erreur moyenne entre les deux images"""
  171. moy = 0
  172. for i in range(len(zones)):
  173. moy += erreur_moyenne_zone(image1, image2,zones[i])
  174. return moy/len(zones)
  175. ### Tests (à nettoyer) ###
  176. image = import_image("images/hibou.jpg")
  177. # print(image)
  178. # print(len(image))
  179. # print(len(image[0]))
  180. image_blanche = tab_image_blanche(get_longueur_img(image), get_hauteur_img(image))
  181. image_noir = tab_image_noir(get_longueur_img(image), get_hauteur_img(image))
  182. img = Image.fromarray(uint8(image_blanche))
  183. for i in range(0, 50):
  184. for j in range(0, 50):
  185. image_blanche = assombrir_pixel(image_blanche, i, j)
  186. img = Image.fromarray(uint8(image_blanche))
  187. #img.show()
  188. #print(image_blanche)
  189. clous = generer_clous(image_blanche)
  190. #print(clous)
  191. test = tab_image_blanche(1000, 1000)
  192. c = generer_clous(test)
  193. for i in range(0, len(c)):
  194. test = assombrir_pixel(test, c[i][0], c[i][1])
  195. test = tracer_droite(c[35], c[321], test)
  196. im_test = Image.fromarray(uint8(test))
  197. im_test.show()
  198. l_zones = lst_zone(image)
  199. # print(erreur_moyenne_zone(image, image_blanche,l_zones[500],10))
  200. ### TODO ###
  201. # On peut essayer de regarder des clous aléatoire plutôt que de tout tester
  202. # On regarde l'erreur sur la ligne qu'on vient de tracer, en faisant des moyennes par zones
  203. # Pas forcément la même résolution pour le calcul que pour l'image
  204. # Pas forcément mesurer l'erreur d'une bande de quelques pixels au bord si on met les clous dedans