diff --git "a/R\303\251seau_siamois/Model_siamois_finale.py" "b/R\303\251seau_siamois/Model_siamois_finale.py"
new file mode 100644
index 0000000000000000000000000000000000000000..f95deebf8dcea4719da241975f5a0153e8f360e4
--- /dev/null
+++ "b/R\303\251seau_siamois/Model_siamois_finale.py"
@@ -0,0 +1,1013 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jun 26 20:45:01 2023
+
+@author: maxamed.nuurmaxa
+"""
+
+# import the necessary packages
+import os
+
+from tensorflow.keras.models import Model
+from tensorflow.keras.layers import Input
+from tensorflow.keras.layers import Conv2D
+from tensorflow.keras.layers import Dense
+from tensorflow.keras.layers import Dropout
+from tensorflow.keras.layers import GlobalAveragePooling2D
+from tensorflow.keras.layers import MaxPooling2D
+from tensorflow.keras.layers import BatchNormalization
+
+from tensorflow.keras.layers import Layer
+
+from keras.optimizers import Adam, RMSprop
+
+from keras_preprocessing.image import ImageDataGenerator
+from keras_preprocessing.image import load_img, img_to_array
+
+import tensorflow.keras.backend as K
+
+
+# import the necessary packages
+from tensorflow.keras.datasets import mnist
+from imutils import build_montages
+import numpy as np
+import cv2
+import csv
+
+import matplotlib as plt
+
+# import the necessary packages
+from tensorflow.keras.models import Model
+from tensorflow.keras.layers import Dense
+from tensorflow.keras.layers import Input
+from tensorflow.keras.layers import Lambda
+from tensorflow.keras.datasets import mnist
+import numpy as np
+
+import matplotlib.pyplot as plt
+
+from PIL import Image
+
+from matplotlib.colors import Normalize
+
+
+#%% Charger le dossier contenant les images 
+
+# Chemin du dossier courant (où se trouve le fichier Python)
+dossier_courant = os.path.dirname(os.path.abspath(__file__))
+
+# Chemin du dossier "Images"
+dossier_images = os.path.join(dossier_courant, r"Images")
+
+# Chemin des sous-dossiers
+chemin_reference = os.path.join(dossier_images, r"Reference")
+chemin_modifier = os.path.join(dossier_images, r"Modifier")
+
+# Liste pour les images de "Reference"
+images_reference = []
+
+# Liste pour les images de chaque sous-dossier de "Modifier"
+images_modifier = []
+
+# Parcours des images dans le dossier "Reference"
+for fichier in os.listdir(chemin_reference):
+    chemin_image = os.path.join(chemin_reference, fichier)
+    if os.path.isfile(chemin_image):
+        images_reference.append(chemin_image)
+
+# Charger les images du sous-dossier "modifier"
+for nom_fichier in os.listdir(chemin_modifier):
+    chemin_image = os.path.join(chemin_modifier, nom_fichier)
+    if os.path.isfile(chemin_image) and nom_fichier.endswith((".jpg", ".jpeg", ".png")):
+        images_modifier.append(chemin_image)
+
+# Affichage des listes d'images
+print("Images de Reference :")
+for image in images_reference:
+    pass
+    # print(image)
+
+print("\nImages de Modifier :")
+for i, images_sous_dossier in enumerate(images_modifier):
+    # print(f"Sous-dossier {i+1}:")
+    for image in images_sous_dossier:
+        pass
+        # print(image)
+
+
+
+#%% Créer des paires similaires d'entraînement
+
+from keras.preprocessing.image import ImageDataGenerator
+from keras.utils import array_to_img, img_to_array, load_img
+
+valeur_augmentation = 0.04
+
+# Chemin vers l'image
+image_path = images_modifier[0]
+
+# Charger l'image avec cv2
+image_cv2 = cv2.imread(image_path, cv2.IMREAD_COLOR)
+
+width, height, _  = image_cv2.shape
+
+target_size = (150, 200)
+
+
+# Création d'une instance de ImageDataGenerator pour augmenter les images d'entraînement
+datagen  = ImageDataGenerator(
+    rescale=1./255,
+    rotation_range=valeur_augmentation,
+    width_shift_range=valeur_augmentation,
+    height_shift_range=valeur_augmentation,
+    # shear_range=valeur_augmentation,
+    zoom_range=valeur_augmentation,
+    # brightness_range=[0.0,valeur_augmentation],
+    # horizontal_flip=True,
+    # vertical_flip=True
+    
+    
+)
+
+
+
+# Liste pour stocker les paires d'images de référence avec transformations et les labels
+reference_pairs = []
+reference_labels = []
+
+# Nombre de paires d'images souhaité
+num_pairs = 152
+
+# Nombre de transformations souhaité pour chaque image de référence
+# num_transformations = 2
+
+
+
+# Parcourir chaque image de référence
+for reference_image_path in images_reference:
+    
+    # Charger l'image de référence
+    reference_img = cv2.imread(reference_image_path, cv2.IMREAD_GRAYSCALE)
+    
+    # reference_img_resize = reference_img.resize(target_size) # avec keras
+    reference_img = cv2.resize(reference_img, target_size,  interpolation=cv2.INTER_AREA)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(reference_img)
+    biggest = np.amax(reference_img)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.01  # Valeur gamma supérieure à 1
+    reference_img = np.power(reference_img / float(biggest), gamma) * biggest
+    reference_img = reference_img.astype(np.uint8)
+    
+    # Appliquer le filtre médian à image_keras avec une taille de noyau de 9x9
+    filtre_median_reference_img = cv2.medianBlur(reference_img, 3)
+    
+    # Appliquer le flou gaussien à image_cv2 avec une taille de noyau de 15x15
+    gaussian_reference_img = cv2.GaussianBlur(filtre_median_reference_img, (5, 5), 0)
+
+    # Expansion des dimensions et création du tableau d'images de référence
+    reference_img_array_expand = gaussian_reference_img.reshape((1,) + reference_img.shape + (1,))    # Générer les paires d'images avec transformations
+
+    for _ in range(num_pairs):
+        # Générer les transformations aléatoires pour l'image de référence et l'image modifiée
+        transformed_reference_img_array = next(datagen.flow(reference_img_array_expand, batch_size=1))
+        transformed_img_array = next(datagen.flow(reference_img_array_expand, batch_size=1))
+        
+        # Ajouter la paire d'images à la liste
+        reference_pairs.append((transformed_img_array[0], transformed_reference_img_array[0]))
+        reference_labels.append(0)  # Ajouter le label 0 pour les paires d'images de référence
+
+        num_pairs -= 1
+        if num_pairs <= 0:
+            break
+
+    if num_pairs <= 0:
+        break
+
+
+#%% Créer des paires d'entraînement avec une erreur 
+
+import random
+
+
+
+# Liste pour stocker les paires d'images modifiées avec transformations et les labels
+modified_pairs = []
+modified_labels = []
+
+# Nombre de paires d'images souhaité
+num_pairs = 152
+
+# Nombre de transformations souhaité pour chaque image de référence
+num_transformations = 1
+
+
+# Parcourir les deux listes simultanément
+for modified_image_path, reference_image_path in zip(images_modifier, images_reference):
+    # Vérifier si le nombre de paires d'images générées atteint le nombre souhaité
+    if len(modified_pairs) >= num_pairs:
+        break
+
+    # Charger l'image modifiée en niveaux de gris
+    modified_img = cv2.imread(modified_image_path, cv2.IMREAD_GRAYSCALE)
+    modified_img = cv2.resize(modified_img, target_size, interpolation=cv2.INTER_AREA)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(modified_img)
+    biggest = np.amax(modified_img)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.4  # Valeur gamma supérieure à 1
+    modified_img = np.power(modified_img / float(biggest), gamma) * biggest
+    modified_img = modified_img.astype(np.uint8)
+    
+    filtre_median_modified_img = cv2.medianBlur(modified_img, 3)
+    gaussian_modified_img = cv2.GaussianBlur(filtre_median_modified_img, (5, 5), 0)
+    
+    
+    modified_img_array_expand = gaussian_modified_img.reshape((1,) + modified_img.shape + (1,))    # Générer les paires d'images avec transformations
+    
+    # Charger l'image de référence en niveaux de gris
+    reference_img = cv2.imread(reference_image_path, cv2.IMREAD_GRAYSCALE)
+    reference_img = cv2.resize(reference_img, target_size, interpolation=cv2.INTER_AREA)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(reference_img)
+    biggest = np.amax(reference_img)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.01  # Valeur gamma supérieure à 1
+    reference_img = np.power(reference_img / float(biggest), gamma) * biggest
+    reference_img = reference_img.astype(np.uint8)
+    
+    filtre_median_reference_img = cv2.medianBlur(reference_img, 3)
+    gaussian_reference_img = cv2.GaussianBlur(filtre_median_reference_img, (5, 5), 0)
+    
+
+    reference_img_array_expand = gaussian_reference_img.reshape((1,) + reference_img.shape + (1,))    # Générer les paires d'images avec transformations
+
+    for _ in range(num_transformations):
+        # Appliquer les transformations sur l'image modifiée et l'image de référence
+        transformed_modified_img_array = next(datagen.flow(modified_img_array_expand, batch_size=1))
+        transformed_reference_img_array = next(datagen.flow(reference_img_array_expand, batch_size=1))
+
+        # Créer une liste temporaire avec les deux images
+        pair = [transformed_modified_img_array[0], transformed_reference_img_array[0]]
+
+        # Inverser aléatoirement la position des images dans la liste pair
+        random.shuffle(pair)
+
+        # Ajouter la paire d'images inversée à la liste modified_pairs
+        modified_pairs.append(tuple(pair))
+        modified_labels.append(1)  # Ajouter le label 1 pour les paires d'images modifiées
+
+        # Vérifier si le nombre de paires d'images générées atteint le nombre souhaité
+        if len(modified_pairs) >= num_pairs:
+            break
+
+    # Vérifier si le nombre de paires d'images générées atteint le nombre souhaité
+    if len(modified_pairs) >= num_pairs:
+        break
+
+#%% Créer le jeux de données d'entraînement
+
+train_data = []
+for pair in reference_pairs:
+    train_data.append((pair, 0))  # Ajouter une paire d'image de référence avec le label 0
+
+for pair in modified_pairs:
+    train_data.append((pair, 1))  # Ajouter une paire d'image modifiée avec le label 1
+
+#%% Melange les données d'entraînement
+import random
+
+# Mélanger les données
+random.seed(42)
+random.shuffle(train_data)
+
+#%% Séparéer le jeux de donnée d'entraînement en deux branches pour le modèle 
+
+# Convertir les listes en tableaux numpy
+X_train_1 = np.array([x[0][0] for x in train_data]) # Les données d'entrée de la première entrée du modèle
+X_train_2 = np.array([x[0][1] for x in train_data]) # Les données d'entrée de la deuxième entrée du modèle
+y_train = np.array([x[1] for x in train_data]) # Les étiquettes de sortie de l'ensemble d'apprentissage
+plt.show()
+
+#%% Afficher une paire similaire et une paire différence d'entraînement
+
+import matplotlib.pyplot as plt
+
+# Visualiser les deux premières paires d'images
+fig, axs = plt.subplots(2, 2, figsize=(10, 10))
+
+indice_image_1 = 120
+indice_image_2 = indice_image_1 + 1
+
+# Première paire
+axs[0, 0].imshow(X_train_1[indice_image_1], cmap='gray')
+axs[0, 0].axis('off')
+axs[0, 0].set_title('X_train_1 - Paire 1')
+axs[0, 1].imshow(X_train_2[indice_image_2], cmap='gray')
+axs[0, 1].axis('off')
+axs[0, 1].set_title('X_train_2 - Paire 1')
+
+# Deuxième paire
+axs[1, 0].imshow(X_train_1[indice_image_2+2], cmap='gray')
+axs[1, 0].axis('off')
+axs[1, 0].set_title('X_train_1 - Paire 2')
+axs[1, 1].imshow(X_train_2[indice_image_2 + 2], cmap='gray')
+axs[1, 1].axis('off')
+axs[1, 1].set_title('X_train_2 - Paire 2')
+
+plt.tight_layout()
+plt.show()
+
+
+#%% Créer des paires similaires de validation
+
+
+# Liste pour stocker les paires d'images de référence avec transformations et les labels
+reference_pairs = []
+reference_labels = []
+
+# Nombre de paires d'images souhaité
+num_pairs = 152
+
+# Nombre de transformations souhaité pour chaque image de référence
+# num_transformations = 2
+
+# Parcourir chaque image de référence
+for reference_image_path in images_reference:
+    
+    # Charger l'image de référence
+    reference_img = cv2.imread(reference_image_path, cv2.IMREAD_GRAYSCALE)
+    
+    # reference_img_resize = reference_img.resize(target_size) # avec keras
+    reference_img = cv2.resize(reference_img, target_size,  interpolation=cv2.INTER_AREA)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(reference_img)
+    biggest = np.amax(reference_img)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.3  # Valeur gamma supérieure à 1
+    reference_img = np.power(reference_img / float(biggest), gamma) * biggest
+    reference_img = reference_img.astype(np.uint8)
+    
+    # Appliquer le filtre médian à image_keras avec une taille de noyau de 9x9
+    filtre_median_reference_img = cv2.medianBlur(reference_img, 3)
+    
+    # Appliquer le flou gaussien à image_cv2 avec une taille de noyau de 15x15
+    gaussian_reference_img = cv2.GaussianBlur(filtre_median_reference_img, (5, 5), 0)
+
+
+    reference_img_array_expand = gaussian_reference_img.reshape((1,) + reference_img.shape + (1,))    # Générer les paires d'images avec transformations
+
+    for _ in range(num_pairs):
+        # Générer les transformations aléatoires pour l'image de référence et l'image modifiée
+        transformed_reference_img_array = next(datagen.flow(reference_img_array_expand, batch_size=1))
+        transformed_img_array = next(datagen.flow(reference_img_array_expand, batch_size=1))
+        
+        # Ajouter la paire d'images à la liste
+        reference_pairs.append((transformed_img_array[0], transformed_reference_img_array[0]))
+        reference_labels.append(0)  # Ajouter le label 0 pour les paires d'images de référence
+
+        num_pairs -= 1
+        if num_pairs <= 0:
+            break
+
+    if num_pairs <= 0:
+        break
+
+
+#%% Créer des paires de Validation avec une erreur
+
+import random
+
+# Liste pour stocker les paires d'images modifiées avec transformations et les labels
+modified_pairs = []
+modified_labels = []
+
+# Nombre de paires d'images souhaité
+num_pairs = 152
+
+# Nombre de transformations souhaité pour chaque image de référence
+num_transformations = 1
+
+# Parcourir les deux listes simultanément
+for modified_image_path, reference_image_path in zip(images_modifier, images_reference):
+    # Vérifier si le nombre de paires d'images générées atteint le nombre souhaité
+    if len(modified_pairs) >= num_pairs:
+        break
+
+    # Charger l'image modifiée en niveaux de gris
+    modified_img = cv2.imread(modified_image_path, cv2.IMREAD_GRAYSCALE)
+    modified_img = cv2.resize(modified_img, target_size, interpolation=cv2.INTER_AREA)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(modified_img)
+    biggest = np.amax(modified_img)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.4  # Valeur gamma supérieure à 1
+    modified_img = np.power(modified_img / float(biggest), gamma) * biggest
+    modified_img = modified_img.astype(np.uint8)
+    
+    filtre_median_modified_img = cv2.medianBlur(modified_img, 3)
+    gaussian_modified_img = cv2.GaussianBlur(filtre_median_modified_img, (5, 5), 0)
+    
+    
+    modified_img_array_expand = gaussian_modified_img.reshape((1,) + modified_img.shape + (1,))    # Générer les paires d'images avec transformations
+    
+    # Charger l'image de référence en niveaux de gris
+    reference_img = cv2.imread(reference_image_path, cv2.IMREAD_GRAYSCALE)
+    reference_img = cv2.resize(reference_img, target_size, interpolation=cv2.INTER_AREA)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(reference_img)
+    biggest = np.amax(reference_img)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.01  # Valeur gamma supérieure à 1
+    reference_img = np.power(reference_img / float(biggest), gamma) * biggest
+    reference_img = reference_img.astype(np.uint8)
+    
+    filtre_median_reference_img = cv2.medianBlur(reference_img, 3)
+    gaussian_reference_img = cv2.GaussianBlur(filtre_median_reference_img, (5, 5), 0)
+    
+
+    reference_img_array_expand = gaussian_reference_img.reshape((1,) + reference_img.shape + (1,))    # Générer les paires d'images avec transformations
+
+    for _ in range(num_transformations):
+        # Appliquer les transformations sur l'image modifiée et l'image de référence
+        transformed_modified_img_array = next(datagen.flow(modified_img_array_expand, batch_size=1))
+        transformed_reference_img_array = next(datagen.flow(reference_img_array_expand, batch_size=1))
+
+        # Créer une liste temporaire avec les deux images
+        pair = [transformed_modified_img_array[0], transformed_reference_img_array[0]]
+
+        # Inverser aléatoirement la position des images dans la liste pair
+        random.shuffle(pair)
+
+        # Ajouter la paire d'images inversée à la liste modified_pairs
+        modified_pairs.append(tuple(pair))
+        modified_labels.append(1)  # Ajouter le label 1 pour les paires d'images modifiées
+
+        # Vérifier si le nombre de paires d'images générées atteint le nombre souhaité
+        if len(modified_pairs) >= num_pairs:
+            break
+
+    # Vérifier si le nombre de paires d'images générées atteint le nombre souhaité
+    if len(modified_pairs) >= num_pairs:
+        break
+#%% Créer le jeux de données de validation
+
+valid_data = []
+for pair in reference_pairs:
+    valid_data.append((pair, 0))  # Ajouter une paire d'image de référence avec le label 0
+
+for pair in modified_pairs:
+    valid_data.append((pair, 1))  # Ajouter une paire d'image modifiée avec le label 1
+
+#%% Melange les données de validation
+import random
+
+# Mélanger les données
+random.seed(42)
+random.shuffle(valid_data)
+# random.shuffle(valid_data)
+
+#%% Séparéer le jeux de donnée de validation en deux branches pour le modèle
+
+
+X_valid_1 = np.array([x[0][0] for x in valid_data]) # Les données d'entrée de la première entrée du modèle pour l'ensemble de validation
+X_valid_2 = np.array([x[0][1] for x in valid_data]) # Les données d'entrée de la deuxième entrée du modèle pour l'ensemble de validation
+y_valid = np.array([x[1] for x in valid_data]) # Les étiquettes de sortie pour l'ensemble de validation
+
+
+#%% Afficher une paire similaire et une paire différence de validation
+
+import matplotlib.pyplot as plt
+
+# Visualiser les deux premières paires d'images
+fig, axs = plt.subplots(2, 2, figsize=(10, 10))
+
+# Première paire
+axs[0, 0].imshow(X_valid_1[0])
+axs[0, 0].axis('off')
+axs[0, 0].set_title('X_valid_1 - Paire 1')
+axs[0, 1].imshow(X_valid_2[0])
+axs[0, 1].axis('off')
+axs[0, 1].set_title('X_valid_2 - Paire 1')
+
+# Deuxième paire
+axs[1, 0].imshow(X_valid_1[1])
+axs[1, 0].axis('off')
+axs[1, 0].set_title('X_valid_1 - Paire 2')
+axs[1, 1].imshow(X_valid_2[1])
+axs[1, 1].axis('off')
+axs[1, 1].set_title('X_valid_2 - Paire 2')
+
+plt.tight_layout()
+plt.show()
+
+
+#%% Fonction qui créer le modèle et une fonction qui calcul la distance
+
+
+def build_siamese_model(inputShape, embeddingDim=48):
+    # spécifie les entrées pour le réseau extracteur de caractéristiques
+    # applique les couches partagées aux entrées de chaque branche
+    l0a = Input(inputShape, name="l0a")  # Entrée A
+    
+    l1a = Conv2D(64, (2, 2), padding="valid", activation="relu", name='l1a')(l0a)  # Couche de convolution partagée 1
+    l1a_mp = MaxPooling2D(pool_size=(2, 2), name='l1a_mp')(l1a)  # Couche de pooling partagée 1
+    l1a_dp = Dropout(0.3)(l1a_mp)  # Couche de dropout partagée
+    # l1a_bm = BatchNormalization()(l1a_dp)
+    
+    l2a = Conv2D(64, (2, 2), padding="valid", activation="relu", name='l2a')(l1a_dp)  # Couche de convolution partagée 2
+    l2a_mp = MaxPooling2D(pool_size=(2,2), name='l2a_mp')(l2a)  # Couche de pooling partagée 2
+    l2a_dp = Dropout(0.3)(l2a_mp)  # Couche de dropout partagée
+    # l2a_bm = BatchNormalization()(l2a_dp)
+
+    # l3a = Conv2D(8, (2, 2), padding="same", activation="relu", name='l3a')(l2a_dp)  # Couche de convolution partagée 3
+    # l3a_dp = Dropout(0.3)(l3a)  # Couche de dropout partagée
+    # l3a_mp = MaxPooling2D(pool_size=(2,2), name='l3a_mp')(l3a_dp)  # Couche de pooling partagée 2
+    
+    
+    # l4a = Conv2D(8, (2, 2), padding="same", activation="relu", name='l4a')(l3a_mp)  # Couche de convolution partagée 3
+    # l4a_dp = Dropout(0.3)(l4a)  # Couche de dropout partagée
+    # l4a_mp = MaxPooling2D(pool_size=2, name='l4a_mp')(l4a_dp)  # Couche de pooling partagée 2
+    
+
+    pooledOutputX = GlobalAveragePooling2D()(l2a_dp)  # Pooling global pour la sortie X
+    outputsX = Dense(embeddingDim)(pooledOutputX)  # Couche dense pour la sortie X
+
+    # crée le modèle
+    model = Model(inputs=l0a, outputs=outputsX)
+    
+    return model
+
+def euclidean_distance(vectors):
+    # décompose les vecteurs en listes distinctes
+    (featsA, featsB) = vectors  # Vecteurs A et B
+    # calcule la somme des distances au carré entre les vecteurs
+    sumSquared = K.sum(K.square(featsA - featsB), axis=1, keepdims=True)  # Somme des distances au carré
+    # retourne la distance euclidienne entre les vecteurs
+    return K.sqrt(K.maximum(sumSquared, K.epsilon()))  # Distance euclidienne
+
+#%% Creation du modele
+
+input_shape=(target_size[1], target_size[0], 1)
+
+img_empty = Input(shape=input_shape)
+img_full = Input(shape=input_shape)
+
+featureExtractor = build_siamese_model(input_shape)
+featsA = featureExtractor(img_empty)
+featsB = featureExtractor(img_full)
+
+distance = Layer(trainable=False)(euclidean_distance)([featsA, featsB])
+
+outputs = Dense(1, activation="sigmoid")(distance)
+model = Model(inputs=[img_empty, img_full], outputs=outputs)
+
+model.summary()
+
+
+#%% Entraîner le modèle
+
+batch_size = 1
+
+EPOCHS = 1
+
+# model.compile(loss="binary_crossentropy", optimizer=RMSprop(learning_rate=0.01), metrics=["accuracy"]) # .RMSprop(learning_rate=0.001)  "adam"
+model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"]) # .RMSprop(learning_rate=0.001)  "adam"
+# 
+history = model.fit([X_train_1, X_train_2], y_train, batch_size=batch_size, epochs=EPOCHS, validation_data=([X_valid_1, X_valid_2], y_valid))
+train_loss = history.history['loss'] # valeurs de la perte sur les données d'entraînement à chaque époque
+val_loss = history.history['val_loss'] # valeurs de la perte sur les données de validation à chaque époque
+train_acc = history.history['accuracy'] # valeurs de la précision sur les données d'entraînement à chaque époque
+val_acc = history.history['val_accuracy'] # valeurs de la précision sur les données de validation à chaque époque
+
+#%% Graphique d'entraînement 
+# Courbes de perte
+plt.figure(figsize=(10, 5))
+plt.plot(epochs, train_loss, 'b-', label='Perte d\'entraînement')
+plt.plot(epochs, val_loss, 'r-', label='Perte de validation')
+plt.title('Courbes de perte')
+plt.xlabel('Époque')
+plt.ylabel('Perte')
+plt.legend()
+plt.grid()
+plt.show()
+
+# Courbes de précision
+plt.figure(figsize=(10, 5))
+plt.plot(epochs, train_acc, 'b-', label='Précision d\'entraînement')
+plt.plot(epochs, val_acc, 'r-', label='Précision de validation')
+plt.title('Courbes de précision')
+plt.xlabel('Époque')
+plt.ylabel('Précision')
+plt.legend()
+plt.grid()
+plt.show()
+
+
+
+#%% Nom du modele et sauvegarde après entraînement
+
+directory = os.path.dirname(os.path.abspath(__file__))
+name = "Siamois_gris_v3" #  Z:/MT3/TB/Siamois/
+# name = "model_siamois_good_v4_3" #  Z:/MT3/TB/Siamois/
+model_name = os.path.join(directory,name)
+
+#%% Sauvegarder le modèle
+
+model = model.save(model_name)
+
+#%% Charger le modèle
+
+import tensorflow as tf 
+
+# Charger le modèle entraîné
+model = tf.keras.models.load_model(model_name)
+
+#%% Algorithme GradCam
+
+import tensorflow as tf
+
+def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):
+    # First, we find the nested model within "model (Functional)"
+    nested_model = None
+    for layer in model.layers:
+        if "Functional" == layer.__class__.__name__:
+            nested_model = layer
+            break
+
+    if nested_model is None:
+        raise ValueError("Could not find nested model within 'model (Functional)'")
+
+    # Then, we create a model that maps the input image to the activations
+    # of the last conv layer as well as the output predictions
+    grad_model = tf.keras.models.Model(
+        [nested_model.inputs], [nested_model.get_layer(last_conv_layer_name).output, nested_model.output]
+    )
+
+    # Then, we compute the gradient of the top predicted class for our input image
+    # with respect to the activations of the last conv layer
+    with tf.GradientTape() as tape:
+        last_conv_layer_output, preds = grad_model(img_array)
+        if pred_index is None:
+            pred_index = tf.argmax(preds[0])
+        class_channel = preds[:, pred_index]
+
+    # This is the gradient of the output neuron (top predicted or chosen)
+    # with regard to the output feature map of the last conv layer
+    grads = tape.gradient(class_channel, last_conv_layer_output)
+    print('grads : ' , grads.shape)
+    # This is a vector where each entry is the mean intensity of the gradient
+    # over a specific feature map channel
+    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))
+    print('pooled_grads : ', pooled_grads.shape)
+
+    # We multiply each channel in the feature map array
+    # by "how important this channel is" with regard to the top predicted class
+    # then sum all the channels to obtain the heatmap class activation
+    last_conv_layer_output = last_conv_layer_output[0]
+    heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]
+    heatmap = tf.squeeze(heatmap)
+
+    # For visualization purpose, we will also normalize the heatmap between 0 & 1
+    heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)
+    return heatmap.numpy()
+
+
+#%% Evaluer le modèle sur des paires d'images d'évaluation
+
+
+import imutils
+
+target_size = (150, 200)
+
+last_conv_layer_name = "l1a"
+
+chemins_images_empty = []
+chemins_images_full = []
+
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\\Changement de luminosite\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\Changement de luminosite\\Reference\\"
+
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\\Hauteur\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\Hauteur\\Reference\\"
+# 
+
+
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\\Photo_Normal\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\Photo_Normal\\Reference\\"
+# 
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\\Photo_Normal\\Normale\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\Photo_Normal\\Normale\\Reference\\"
+
+
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Images\\\Orientation\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Images\\Orientation\\Reference\\"
+
+
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Images\\\Position\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Images\\Position\\Reference\\"
+
+
+# chemin_dossier_empty = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\\Last\\Modifier"
+# chemin_dossier_full = r"Z:\\MT3\\TB\\Siamois_5\\Analyse\\Rapport\\Last\\Reference\\"
+
+# chemin_dossier_empty = r"z:\\mt3\\tb\\siamois\\Images_donnees\\DatasetCNNmini\\V\\EMPTY\\"
+# chemin_dossier_full = r"z:\\mt3\\tb\\siamois\\Images_donnees\\DatasetCNNmini\\V\\FULL\\"
+
+chemin_dossier_empty = r"z:\\mt3\\tb\\Siamois_6\\Analyses_carte_v2\\Modifier\\"
+chemin_dossier_full = r"z:\\mt3\\tb\\Siamois_6\\Analyses_carte_v2\\Reference\\"
+
+nombre_image = 3
+
+
+# Charger les images du sous-dossier "empty"
+for nom_fichier in os.listdir(chemin_dossier_empty):
+    chemin_image = os.path.join(chemin_dossier_empty, nom_fichier)
+    if os.path.isfile(chemin_image) and nom_fichier.endswith((".jpg", ".jpeg", ".png")):
+        chemins_images_empty.append(chemin_image)
+
+# Charger les images du sous-dossier "full"
+for nom_fichier in os.listdir(chemin_dossier_full):
+    chemin_image = os.path.join(chemin_dossier_full, nom_fichier)
+    if os.path.isfile(chemin_image) and nom_fichier.endswith((".jpg", ".jpeg", ".png")):
+        chemins_images_full.append(chemin_image)
+
+for i,(chemin_image_empty, chemin_image_full) in enumerate(zip(chemins_images_empty, chemins_images_full)):
+    # Charger les images empty et full
+    if i >= nombre_image:
+        break
+    image_empty = cv2.imread(chemin_image_empty, cv2.IMREAD_GRAYSCALE)
+    orig_empty = cv2.imread(chemin_image_empty)
+    orig_empty = cv2.cvtColor(orig_empty, cv2.COLOR_BGR2RGB)
+    
+    orig_empty = cv2.resize(orig_empty, target_size, interpolation=cv2.INTER_AREA)
+    # Créer une copie des images à des fins de visualisation
+    orig_empty = orig_empty.copy()
+    
+    image_full = cv2.imread(chemin_image_full, cv2.IMREAD_GRAYSCALE)
+    # Créer une copie des images à des fins de visualisation
+    orig_full = cv2.imread(chemin_image_full)
+    orig_full = cv2.cvtColor(orig_full, cv2.COLOR_BGR2RGB)
+    orig_full = cv2.resize(orig_full, target_size, interpolation=cv2.INTER_AREA)
+    orig_full = orig_full.copy()
+    
+
+    image_empty = cv2.resize(image_empty, target_size, interpolation=cv2.INTER_AREA)
+    image_full = cv2.resize(image_full, target_size, interpolation=cv2.INTER_AREA)
+    
+    # Appliquer le filtre médian à image_keras avec une taille de noyau de 9x9
+    image_empty = cv2.medianBlur(image_empty,1)
+    image_full = cv2.medianBlur(image_full, 1)
+
+    
+    # Appliquer le flou gaussien à image_cv2 avec une taille de noyau de 15x15
+    image_empty = cv2.GaussianBlur(image_empty, (1, 1), 0)
+    image_full = cv2.GaussianBlur(image_full, (1, 1), 0)
+
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(image_empty)
+    biggest = np.amax(image_empty)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 0.9 # Valeur gamma supérieure à 0,9   , 1,0   ,  1,1 et 1,4
+    image_empty = np.power(image_empty / float(biggest), gamma) * biggest
+    image_empty = image_empty.astype(np.uint8)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(image_full)
+    biggest = np.amax(image_full)
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.01  # Valeur gamma supérieure à 1
+    image_full = np.power(image_full / float(biggest), gamma) * biggest
+    image_full = image_full.astype(np.uint8)
+    
+    # Ajouter une dimension de canal aux images
+    image_empty = np.expand_dims(image_empty, axis=-1)
+    image_full = np.expand_dims(image_full, axis=-1)
+    
+    # Ajouter une dimension de lot (batch) aux images
+    image_empty = np.expand_dims(image_empty, axis=0)
+    image_full = np.expand_dims(image_full, axis=0)
+    
+    # Mettre à l'échelle les valeurs de pixels dans la plage [0, 1]
+    image_empty = image_empty / 255.0
+    image_full = image_full / 255.0
+    
+    heatmap_1 = make_gradcam_heatmap(image_empty, model, last_conv_layer_name)
+
+    heatmap_2 = make_gradcam_heatmap(image_full, model, last_conv_layer_name)
+    
+    # Redimensionner la heatmap pour qu'elle corresponde à la taille de l'image
+    heatmap_resized_1 = cv2.resize(heatmap_1, (image_empty.shape[2], image_empty.shape[1]))
+
+    heatmap_resized_2 = cv2.resize(heatmap_2, (image_empty.shape[2], image_empty.shape[1]))
+    
+    intensity_min = np.min(heatmap_resized_1)
+    intensity_max = np.max(heatmap_resized_1)
+    heatmap_resized_1_cnt = np.interp(heatmap_resized_1, (intensity_min, intensity_max), (0, 255)).astype(np.uint8)
+    heatmap_resized_1_cnt = heatmap_resized_1_cnt.astype(np.uint8)
+
+    # heatmap_3 = heatmap_resized_1 - heatmap_resized_2
+    edges = cv2.Canny(heatmap_resized_1_cnt, 250, 255) # 200 LES AUTREA IMAGES
+    # Trouver les contours dans l'image edges
+    cnts, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+    
+    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
+    
+    image_empty = image_empty.astype(np.uint8)
+    
+    
+    orig_empty_2 =orig_empty.copy()
+    
+    # Dessiner les contours sur l'image heatmap
+    cv2.drawContours(orig_empty, cnts[0:4], -1, (255, 0, 0), thickness=1)
+    
+    # Utiliser votre modèle siamois pour effectuer des prédictions sur la paire d'images,
+    # indiquant si les images appartiennent à la même classe ou non
+    preds = model.predict([image_empty, image_full])
+    proba = preds[0][0]
+    
+    plt.figure(figsize=(12, 6))
+    
+    plt.subplot(1, 4, 1)
+    plt.imshow(np.squeeze(orig_full))
+    plt.axis('off')
+    plt.title("Image de référence ")
+    
+    plt.subplot(1, 4, 2)
+    plt.imshow(np.squeeze(orig_empty_2))
+    plt.axis('off')
+    plt.title("Image avec une erreur")
+    
+    plt.subplot(1, 4, 3)
+    plt.imshow(heatmap_resized_1, cmap='jet')
+    plt.axis('off')
+    plt.title("Carte activation. Différence : {:.2f}".format(proba))
+    
+    # Ajouter la quatrième image
+    plt.subplot(1, 4, 4)
+    plt.imshow(np.squeeze(orig_empty))
+    plt.axis('off')
+    plt.title("Résultat")
+    
+    plt.tight_layout()
+    plt.show()
+
+#%% Paires Similaire
+
+chemins_images_empty = []
+chemins_images_full = []
+
+chemin_dossier_empty = r"z:\\mt3\\tb\\siamois\\Images_donnees\\DatasetCNNmini\\Image_ref\\EMPTY\\"
+chemin_dossier_full = r"z:\\mt3\\tb\\siamois\\Images_donnees\\DatasetCNNmini\\Image_ref\\FULL\\"
+
+nombre_image = 3
+
+
+# Charger les images du sous-dossier "empty"
+for nom_fichier in os.listdir(chemin_dossier_empty):
+    chemin_image = os.path.join(chemin_dossier_empty, nom_fichier)
+    if os.path.isfile(chemin_image) and nom_fichier.endswith((".jpg", ".jpeg", ".png")):
+        chemins_images_empty.append(chemin_image)
+
+# Charger les images du sous-dossier "full"
+for nom_fichier in os.listdir(chemin_dossier_full):
+    chemin_image = os.path.join(chemin_dossier_full, nom_fichier)
+    if os.path.isfile(chemin_image) and nom_fichier.endswith((".jpg", ".jpeg", ".png")):
+        chemins_images_full.append(chemin_image)
+
+
+
+for i,(chemin_image_empty, chemin_image_full) in enumerate(zip(chemins_images_empty, chemins_images_full)):
+    # Charger les images empty et full
+    if i >= nombre_image:
+        break
+    image_empty = cv2.imread(chemin_image_empty, cv2.IMREAD_GRAYSCALE)
+    orig_empty = cv2.imread(chemin_image_empty)
+    orig_empty = cv2.cvtColor(orig_empty, cv2.COLOR_BGR2RGB)
+    
+    orig_empty = cv2.resize(orig_empty, target_size, interpolation=cv2.INTER_AREA)
+    # Créer une copie des images à des fins de visualisation
+    orig_empty = orig_empty.copy()
+    
+    image_full = cv2.imread(chemin_image_full, cv2.IMREAD_GRAYSCALE)
+    # Créer une copie des images à des fins de visualisation
+    orig_full = cv2.imread(chemin_image_full)
+    orig_full = cv2.cvtColor(orig_full, cv2.COLOR_BGR2RGB)
+    orig_full = cv2.resize(orig_full, target_size, interpolation=cv2.INTER_AREA)
+    orig_full = orig_full.copy()
+    
+
+    image_empty = cv2.resize(image_empty, target_size, interpolation=cv2.INTER_AREA)
+    image_full = cv2.resize(image_full, target_size, interpolation=cv2.INTER_AREA)
+    
+    # Appliquer le filtre médian à image_keras avec une taille de noyau de 9x9
+    image_empty = cv2.medianBlur(image_empty,1)
+    image_full = cv2.medianBlur(image_full, 1)
+
+    
+    # Appliquer le flou gaussien à image_cv2 avec une taille de noyau de 15x15
+    image_empty = cv2.GaussianBlur(image_empty, (1, 1), 0)
+    image_full = cv2.GaussianBlur(image_full, (1, 1), 0)
+
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(image_empty)
+    biggest = np.amax(image_empty)
+    
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 0.9 # Valeur gamma supérieure à 0,9   , 1,0   ,  1,1 et 1,4
+    image_empty = np.power(image_empty / float(biggest), gamma) * biggest
+    image_empty = image_empty.astype(np.uint8)
+    
+    # Calculer la valeur minimale et maximale de l'image
+    smallest = np.amin(image_full)
+    biggest = np.amax(image_full)
+    # Appliquer la transformation gamma pour augmenter le contraste noir
+    gamma = 1.01  # Valeur gamma supérieure à 1
+    image_full = np.power(image_full / float(biggest), gamma) * biggest
+    image_full = image_full.astype(np.uint8)
+    
+    # Ajouter une dimension de canal aux images
+    image_empty = np.expand_dims(image_empty, axis=-1)
+    image_full = np.expand_dims(image_full, axis=-1)
+    
+    # Ajouter une dimension de lot (batch) aux images
+    image_empty = np.expand_dims(image_empty, axis=0)
+    image_full = np.expand_dims(image_full, axis=0)
+    
+    # Mettre à l'échelle les valeurs de pixels dans la plage [0, 1]
+    image_empty = image_empty / 255.0
+    image_full = image_full / 255.0
+    
+    heatmap_1 = make_gradcam_heatmap(image_empty, model, last_conv_layer_name)
+
+    heatmap_2 = make_gradcam_heatmap(image_full, model, last_conv_layer_name)
+    
+    # Redimensionner la heatmap pour qu'elle corresponde à la taille de l'image
+    heatmap_resized_1 = cv2.resize(heatmap_1, (image_empty.shape[2], image_empty.shape[1]))
+
+    heatmap_resized_2 = cv2.resize(heatmap_2, (image_empty.shape[2], image_empty.shape[1]))
+    
+    intensity_min = np.min(heatmap_resized_1)
+    intensity_max = np.max(heatmap_resized_1)
+    heatmap_resized_1_cnt = np.interp(heatmap_resized_1, (intensity_min, intensity_max), (0, 255)).astype(np.uint8)
+    heatmap_resized_1_cnt = heatmap_resized_1_cnt.astype(np.uint8)
+
+    # heatmap_3 = heatmap_resized_1 - heatmap_resized_2
+    edges = cv2.Canny(heatmap_resized_1_cnt, 250, 255) # 200 LES AUTREA IMAGES
+    # Trouver les contours dans l'image edges
+    cnts, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+    
+    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
+    
+    image_empty = image_empty.astype(np.uint8)
+    
+    
+    orig_empty_2 =orig_empty.copy()
+    
+    # Dessiner les contours sur l'image heatmap
+    cv2.drawContours(orig_empty, cnts[0:4], -1, (255, 0, 0), thickness=1)
+    
+    # Utiliser votre modèle siamois pour effectuer des prédictions sur la paire d'images,
+    # indiquant si les images appartiennent à la même classe ou non
+    preds = model.predict([image_empty, image_full])
+    proba = preds[0][0]
+    
+    plt.figure(figsize=(12, 6))
+    
+    plt.subplot(1, 4, 1)
+    plt.imshow(np.squeeze(orig_full))
+    plt.axis('off')
+    plt.title("Image de référence ")
+    
+    plt.subplot(1, 4, 2)
+    plt.imshow(np.squeeze(orig_empty_2))
+    plt.axis('off')
+    plt.title("Image avec une erreur")
+    
+    plt.subplot(1, 4, 3)
+    plt.imshow(heatmap_resized_1, cmap='jet')
+    plt.axis('off')
+    plt.title("Carte activation. Différence : {:.2f}".format(proba))
+    
+    # Ajouter la quatrième image
+    plt.subplot(1, 4, 4)
+    plt.imshow(np.squeeze(orig_empty))
+    plt.axis('off')
+    plt.title("Résultat")
+    
+    plt.tight_layout()
+    plt.show()
+