diff --git "a/M\303\251thode traditionnelle/mes_fonctions.py" "b/M\303\251thode traditionnelle/mes_fonctions.py" new file mode 100644 index 0000000000000000000000000000000000000000..61135ae11934060b27ca4b5c249ce1b42529dc35 --- /dev/null +++ "b/M\303\251thode traditionnelle/mes_fonctions.py" @@ -0,0 +1,396 @@ +# -*- coding: utf-8 -*- +""" +Created on Thu May 25 14:03:53 2023 + +@author: maxamed.nuurmaxa +""" + + + +from skimage.metrics import structural_similarity as compare_ssim +import argparse +import imutils +import cv2 +import matplotlib.pyplot as plt +import os +import numpy as np + + +#%% Récupérer le chemin des images + +def get_chemin_images(): + # Récupérer le chemin absolu complet vers le dossier contenant le fichier Python en cours d'exécution + script_dir = os.path.dirname(os.path.abspath(__file__)) + + # Obtenez le chemin complet du dossier "Images" + chemins_dossier_image = os.path.join(script_dir, "T3") # T1 # T2 # T3 + + # Vérifiez si le chemin correspond à un dossier + if os.path.isdir(chemins_dossier_image): + # Obtenir la liste des éléments dans le dossier "Images" + elements_dossier_image = os.listdir(chemins_dossier_image) + + # Parcourir les éléments + liste_chemin_images = [] + for element in elements_dossier_image: + chemin_element = os.path.join(chemins_dossier_image, element) + print("Nom de l'élément :", element) + print("Chemin de l'élément :", chemin_element) + print() + liste_chemin_images.append(chemin_element) + return liste_chemin_images + else: + print("Le chemin spécifié ne correspond pas à un dossier.") + + +#%% Lire les images + + +def redimensionner_images(imageA, imageB): + # Récupérer les dimensions de l'imageA + hauteurA, largeurA, _ = imageA.shape # dernière valeur canal de couleur + + # Redimensionner l'imageB pour qu'elle ait les mêmes dimensions que l'imageA + imageB = cv2.resize(imageB, (largeurA, hauteurA)) + + # Convertir les images en niveaux de gris + grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY) + grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY) + + # Redimensionner les images en niveaux de gris pour qu'elles aient les mêmes dimensions + grayB = cv2.resize(grayB, (largeurA, hauteurA)) + + return imageA, imageB, grayA, grayB + + +#%% Afficher les images + +def afficher_image(image): + # Création d'une figure et d'un seul sous-graphique + fig, ax = plt.subplots(1, 1) + + # Affichage de l'image + ax.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) + ax.set_title("Resultat") + + # Suppression des axes + ax.axis('off') + + # Affichage de la figure + plt.show() + +def afficher_images(imageA, imageB): + # Affichage des images sur un même graphique + fig, axes = plt.subplots(1, 2) + + # Affichage de l'image A + axes[0].imshow(cv2.cvtColor(imageA, cv2.COLOR_BGR2RGB)) + axes[0].set_title("Image Référence") + axes[0].axis('off') + # Affichage de l'image B + axes[1].imshow(cv2.cvtColor(imageB, cv2.COLOR_BGR2RGB)) + axes[1].set_title("Image Erreur") + axes[1].axis('off') + # Ajustement des espacements entre les sous-graphiques + plt.tight_layout() + + # Affichage du graphique + + # plt.axis("off") + plt.show() + +def afficher_histogramme(image_gris): + nbBins = 256 + rangePV = [0, 255] # Valeurs possibles des pixels + + plt.figure(figsize=(9, 9)) + plt.title("Histogramme de l'image en niveaux de gris") + plt.xlabel("Bins") + plt.ylabel("# de Pixels") + plt.hist(image_gris.ravel(), nbBins, rangePV) + plt.grid() + plt.show() + +def superposer_images(imageA, imageB, alpha=0.3): + # Convertir les images en format RGBA avec un canal alpha + imageA = cv2.cvtColor(imageA, cv2.COLOR_BGR2RGBA) + imageB = cv2.cvtColor(imageB, cv2.COLOR_BGR2RGBA) + + # Créer une copie de l'image B avec une opacité réduite + imageB_transparent = imageB.copy() + imageB_transparent[:, :, 3] = int(alpha * 255) + + # Superposer les images + image_superposee = cv2.addWeighted(imageA, 1 - alpha, imageB_transparent, alpha, 0) + + # Afficher l'image superposée + plt.imshow(image_superposee) + plt.axis('off') + plt.show() + +#%% Application de filtres + +def filtrer_et_seuiller(image): + # Appliquer le filtre médian avec une taille de noyau de 9x9 + filtre_median = cv2.medianBlur(image, 9) + + # Appliquer le flou gaussien avec une taille de noyau de 15x15 + gaussian = cv2.GaussianBlur(filtre_median, (15, 15), 0) + + # Définir les valeurs de seuil inférieure et supérieure + seuil_inf = 70 + seuil_sup = 90 + + # Appliquer un seuillage binaire inversé pour obtenir une image où seuls les pixels entre les seuils sont conservés + _, threshold = cv2.threshold(gaussian, seuil_inf, 255, cv2.THRESH_BINARY_INV) + + # Les pixels en dehors de l'intervalle de seuil sont définis à 0 (noir) + threshold[threshold != 255] = 0 + + return threshold + + + +#%% Contour et rogner + +def trouver_et_rogner_contour(image, imageA): + # Appliquer une opération de morphologie pour améliorer les contours + kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) + closed = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel) + + # Trouver les contours dans l'image fermée + contours, _ = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + # Tri des contours par aire (surface) + contours = sorted(contours, key=cv2.contourArea, reverse=True) + + # Sélectionner le plus grand contour + top_contour = contours[:1] + + # Dessiner le plus grand contour sur une nouvelle image + top_contour_image = np.zeros_like(imageA) + image_contour = cv2.drawContours(top_contour_image, top_contour, -1, (0, 255, 0), 5) + + # Obtenir les coordonnées du contour + x, y, w, h = cv2.boundingRect(top_contour[0]) + + # Extraire la région d'intérêt (ROI) de l'image en utilisant les coordonnées du contour + cropped_image = imageA[y:y+h, x:x+w] + + return image_contour, cropped_image, x, y, w, h, top_contour, top_contour_image + +#%% Essaies 1 meilleure correspondance + +# def feature_extraction(image): +# # Créer un détecteur de points d'intérêt ORB +# orb = cv2.ORB_create() + +# # Trouver les points d'intérêt et les descripteurs dans l'image +# keypoints, descriptors = orb.detectAndCompute(image, None) + +# return keypoints, descriptors + +# def feature_matching(descriptors_A, descriptors_B): +# # Créer un matcher descripteur par force brute (brute force) +# bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) + +# # Effectuer une correspondance des descripteurs entre les deux images +# matches = bf.match(descriptors_A, descriptors_B) + +# # Trier les correspondances par distance croissante +# matches = sorted(matches, key=lambda x: x.distance) + +# return matches + +# imageA = cropped_image_A +# imageB = cropped_image_B + + + +# # Extraire les points caractéristiques et les descripteurs des deux images +# keypoints_A, descriptors_A = feature_extraction(cropped_image_A_1) +# keypoints_B, descriptors_B = feature_extraction(cropped_image_B_1) + +# # Effectuer une correspondance des descripteurs +# matches = feature_matching(descriptors_A, descriptors_B) + +# # Extraire les coordonnées des points correspondants +# points_A = np.float32([keypoints_A[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2) +# points_B = np.float32([keypoints_B[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2) + +# ransacReprojThreshold = 12 + +# # Estimer la transformation géométrique en utilisant RANSAC +# transformation_matrix, mask = cv2.findHomography(points_B, points_A, cv2.RANSAC, ransacReprojThreshold) + +# # Appliquer la transformation à l'image B pour l'aligner sur l'image A +# aligned_image_B = cv2.warpPerspective(cropped_image_B_1, transformation_matrix, (cropped_image_A_1.shape[1], cropped_image_A_1.shape[0])) + +# # Calculer le décalage entre les deux images +# decalage_x = transformation_matrix[0, 2] +# decalage_y = transformation_matrix[1, 2] + +# # Appliquer la translation à l'image B pour aligner sa position sur l'image A +# translation_matrix = np.float32([[1, 0, decalage_x], [0, 1, decalage_y]]) +# translated_image_B = cv2.warpAffine(cropped_image_B_1, translation_matrix, (cropped_image_A_1.shape[1], cropped_image_A_1.shape[0])) + +#%% Essai 2 meilleures correspondance + + +def feature_matching(descriptors_A, descriptors_B): + # Créer un matcher descripteur par force brute (brute force) + bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) + + # Effectuer une correspondance des descripteurs entre les deux images + matches = bf.match(descriptors_A, descriptors_B) + + # Trier les correspondances par distance croissante + matches = sorted(matches, key=lambda x: x.distance) + + return matches + +def apply_affine_transform(image, transformation_matrix): + rows, cols = image.shape[:2] + print(rows, cols) + + transformed_image = cv2.warpAffine(image, transformation_matrix, (cols, rows)) + + return transformed_image + +def find_best_correspondence(imageA, imageB): + + if len(imageA.shape) == 3 and imageA.shape[2] == 3: + # L'image A est en couleur, convertir en niveaux de gris + grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY) + else: + # L'image A est déjà en niveaux de gris + grayA = imageA + + if len(imageB.shape) == 3 and imageB.shape[2] == 3: + # L'image B est en couleur, convertir en niveaux de gris + grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY) + else: + # L'image B est déjà en niveaux de gris + grayB = imageB + # Extraire les points caractéristiques et les descripteurs des deux images + orb = cv2.ORB_create() + keypoints_A, descriptors_A = orb.detectAndCompute(grayA, None) + keypoints_B, descriptors_B = orb.detectAndCompute(grayB, None) + + # Effectuer une correspondance des descripteurs + matches = feature_matching(descriptors_A, descriptors_B) + + # Extraire les coordonnées des points correspondants + points_A = np.float32([keypoints_A[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2) + points_B = np.float32([keypoints_B[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2) + + # Estimer une transformation affine en utilisant RANSAC + transformation_matrix, _ = cv2.estimateAffine2D(points_B, points_A, method=cv2.RANSAC) + + # Appliquer la transformation affine à l'image B + transformed_imageB = apply_affine_transform(imageB, transformation_matrix) + + return transformed_imageB + + +#%% Difference entre les deux images + +def process_images(imageA, imageB): + if len(imageA.shape) == 3 and imageA.shape[2] == 3: + # L'image A est en couleur, convertir en niveaux de gris + gray_cropped_imageA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY) + else: + # L'image A est déjà en niveaux de gris + gray_cropped_imageA = imageA + + if len(imageB.shape) == 3 and imageB.shape[2] == 3: + # L'image B est en couleur, convertir en niveaux de gris + gray_cropped_imageB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY) + else: + # L'image B est déjà en niveaux de gris + gray_cropped_imageB = imageB + # Créer une matrice de taille (taille_noyau_median, taille_noyau_median) pour le filtre médian + taille_noyau_median = 11 + + Filtre_median_grayA = cv2.medianBlur(gray_cropped_imageA, taille_noyau_median) + Filtre_median_grayB = cv2.medianBlur(gray_cropped_imageB, taille_noyau_median) + + # Afficher les images après le filtre médian + afficher_images(Filtre_median_grayA, Filtre_median_grayB) + + kernel_size_gaussien = (71, 71) + + gaussian_grayA = cv2.GaussianBlur(Filtre_median_grayA, kernel_size_gaussien, 0) + gaussian_grayB = cv2.GaussianBlur(Filtre_median_grayB, kernel_size_gaussien, 0) + + # Afficher les images après le filtre gaussien + afficher_images(gaussian_grayA, gaussian_grayB) + + # Calcul du score SSIM et de la différence entre les images + (score, diff) = compare_ssim(gaussian_grayA, gaussian_grayB, full=True) + + # Mise à l'échelle de la différence entre 0 et 255 pour pouvoir l'afficher + diff = (diff * 255).astype("uint8") + + # Affichage du score SSIM + print("SSIM: {}".format(score)) + return diff + +#%% Traitement + +def seuillage_diff_image(diff, best_correspondence_A, best_correspondence_B, pourcentage_marge=5, nombre_objet=3, seuil_taille=0.03): + # Seuillage de la carte de différence en une image binaire + thresh = cv2.threshold(diff, 200, 255, cv2.THRESH_BINARY_INV)[1] + + # Recherche des contours dans l'image seuillée + cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + cnts = imutils.grab_contours(cnts) + + # Tri des contours par aire (surface) + cnts = sorted(cnts, key=cv2.contourArea, reverse=True) + + # Créer une image vierge de la même taille que diff + contour_image = np.zeros_like(diff) + + # Obtenir les dimensions de l'image + height, width = best_correspondence_A.shape[:2] + + # Calcul de la marge pour exclure les contours proches des bords + marge = int(min(height, width) * (pourcentage_marge / 100)) + + # Créer une nouvelle liste pour stocker les contours valides + cnts_valides = [] + + # Parcourir tous les contours + for contour in cnts: + # Obtenir le rectangle englobant du contour + x, y, w, h = cv2.boundingRect(contour) + + # Vérifier si le contour touche les bords de l'image + if x > marge and y > marge and x + w < width - marge and y + h < height - marge: + # Ajouter le contour à la liste des contours valides + cnts_valides.append(contour) + + # Trier les contours valides par aire (en ordre décroissant) + cnts_valides = sorted(cnts_valides, key=cv2.contourArea, reverse=True) + + # Rogner les objets inférieurs à un air de 3% + hauteur, largeur, _ = best_correspondence_B.shape + cnts_valides_rogner = cnts_valides[:nombre_objet] + aire_totale = hauteur * largeur + seuil_taille = seuil_taille * aire_totale # Seuil de taille à 3% de l'aire totale + + cnts_valides_filtres = [] + for contour in cnts_valides_rogner: + aire_contour = cv2.contourArea(contour) + if aire_contour > seuil_taille: + cnts_valides_filtres.append(contour) + + # Dessiner les rectangles autour des objets filtrés + for c in cnts_valides_filtres: + (x, y, w, h) = cv2.boundingRect(c) + cv2.rectangle(best_correspondence_B, (x, y), (x + w, y + h), (0, 0, 255), 2) + + return best_correspondence_B + +#%%