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() +