Mise à jour bayesian (fonctionnel), histogramme + output
This commit is contained in:
		
							parent
							
								
									ecd9ca1040
								
							
						
					
					
						commit
						0d15b16e9f
					
				
							
								
								
									
										68
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								main.py
									
									
									
									
									
								
							| @ -1,31 +1,55 @@ | |||||||
| import time | import os | ||||||
| from src.classifiers.bayesian import BayesianClassifier | import cv2 | ||||||
| from src.pipeline import ObjectDetectionPipeline | from src.pipeline import ObjectDetectionPipeline | ||||||
|  | from src.classifiers.bayesian import BayesianClassifier | ||||||
| 
 | 
 | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     # Chemin de l'image à traiter |     # Chemin vers le modèle entraîné | ||||||
|     image_path = "data/page.png" |  | ||||||
| 
 |  | ||||||
|     # Initialisation du pipeline avec le chemin de l'image |  | ||||||
|     pipeline = ObjectDetectionPipeline(image_path) |  | ||||||
| 
 |  | ||||||
|     # Initialisation et chargement du modèle Bayésien |  | ||||||
|     bayesian_model = BayesianClassifier() |  | ||||||
|     model_path = "models/bayesian_model.pth" |     model_path = "models/bayesian_model.pth" | ||||||
|     pipeline.load_model(model_path, bayesian_model) | 
 | ||||||
|  |     # Chargement du modèle bayésien | ||||||
|  |     print(f"Chargement du modèle bayésien depuis {model_path}") | ||||||
|  |     bayesian_model = BayesianClassifier() | ||||||
|  |     try: | ||||||
|  |         bayesian_model.load_model(model_path) | ||||||
|  |         print(f"Modèle bayésien chargé depuis {model_path}") | ||||||
|  |     except Exception as e: | ||||||
|  |         print(f"Erreur lors du chargement du modèle : {e}") | ||||||
|  |         exit(1) | ||||||
|  | 
 | ||||||
|  |     # Chemin de l'image de test | ||||||
|  |     image_path = "data/page.png" | ||||||
|  |     if not os.path.exists(image_path): | ||||||
|  |         print(f"L'image de test {image_path} n'existe pas.") | ||||||
|  |         exit(1) | ||||||
|  | 
 | ||||||
|  |     # Initialisation du dossier de sortie | ||||||
|  |     output_dir = "output" | ||||||
|  |     if not os.path.exists(output_dir): | ||||||
|  |         os.makedirs(output_dir) | ||||||
|  | 
 | ||||||
|  |     # Initialisation de la pipeline | ||||||
|  |     print("Initialisation de la pipeline...") | ||||||
|  |     pipeline = ObjectDetectionPipeline(image_path=image_path, model=bayesian_model, output_dir=output_dir) | ||||||
| 
 | 
 | ||||||
|     # Chargement de l'image |     # Chargement de l'image | ||||||
|     pipeline.load_image() |     print("Chargement de l'image...") | ||||||
|  |     try: | ||||||
|  |         pipeline.load_image() | ||||||
|  |     except FileNotFoundError as e: | ||||||
|  |         print(e) | ||||||
|  |         exit(1) | ||||||
| 
 | 
 | ||||||
|     # Mesure du temps d'exécution pour la détection et classification |     # Détection et classification des objets | ||||||
|     start_time = time.time() |     print("Détection et classification des objets...") | ||||||
|     class_counts, detected_objects = pipeline.detect_and_classify_objects() |     try: | ||||||
|     end_time = time.time() |         class_counts, detected_objects = pipeline.detect_and_classify_objects() | ||||||
|  |     except Exception as e: | ||||||
|  |         print(f"Erreur lors de la détection/classification : {e}") | ||||||
|  |         exit(1) | ||||||
| 
 | 
 | ||||||
|     # Résultats |     # Sauvegarde et affichage des résultats | ||||||
|     print(f"Temps d'exécution: {end_time - start_time:.2f} secondes") |     print("Sauvegarde et affichage des résultats...") | ||||||
|     print("Comptage des classes :", class_counts) |  | ||||||
|     print("Nombre d'objets détectés :", len(detected_objects)) |  | ||||||
| 
 |  | ||||||
|     # Affichage des résultats |  | ||||||
|     pipeline.display_results(class_counts, detected_objects) |     pipeline.display_results(class_counts, detected_objects) | ||||||
|  | 
 | ||||||
|  |     print(f"Les résultats ont été sauvegardés dans le dossier : {output_dir}") | ||||||
|  | |||||||
| @ -1 +1,6 @@ | |||||||
| opencv-python pytorch tensorflow numpy pandas matplotlib | opencv-python | ||||||
|  | torch | ||||||
|  | tensorflow | ||||||
|  | numpy | ||||||
|  | pandas | ||||||
|  | matplotlib | ||||||
| @ -5,66 +5,69 @@ import torch | |||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
| import matplotlib.pyplot as plt | import matplotlib.pyplot as plt | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class BayesianClassifier: | class BayesianClassifier: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         """ |         self.feature_means = {} | ||||||
|         Initialisation du classificateur Bayésien avec les paramètres nécessaires. |         self.feature_variances = {} | ||||||
|         """ |         self.class_priors = {} | ||||||
|         self.feature_means = {}  # Moyennes des caractéristiques pour chaque classe |         self.classes = [] | ||||||
|         self.feature_variances = {}  # Variances des caractéristiques pour chaque classe |  | ||||||
|         self.class_priors = {}  # Probabilités a priori pour chaque classe |  | ||||||
|         self.classes = []  # Liste des classes disponibles |  | ||||||
| 
 | 
 | ||||||
|     def extract_features(self, image): |         # Initialize HOG descriptor with standard parameters | ||||||
|         """ |         self.hog = cv2.HOGDescriptor( | ||||||
|         Extraire les caractéristiques d'une image donnée (Histogramme des Gradients Orientés - HOG). |             _winSize=(28, 28), | ||||||
| 
 |             _blockSize=(8, 8), | ||||||
|         :param image: Image en entrée |             _blockStride=(4, 4), | ||||||
|         :return: Tableau des caractéristiques normalisées |             _cellSize=(8, 8), | ||||||
|         """ |             _nbins=9 | ||||||
|         # Conversion de l'image en niveaux de gris si nécessaire |  | ||||||
|         if len(image.shape) == 3 and image.shape[2] == 3: |  | ||||||
|             gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |  | ||||||
|         else: |  | ||||||
|             gray_image = image |  | ||||||
| 
 |  | ||||||
|         # Binarisation de l'image |  | ||||||
|         binary_image = cv2.adaptiveThreshold( |  | ||||||
|             gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 2 |  | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         # Extraction des contours |     def extract_features(self, image): | ||||||
|         contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |         try: | ||||||
|  |             # Convert image to grayscale | ||||||
|  |             if len(image.shape) == 3 and image.shape[2] == 3: | ||||||
|  |                 gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | ||||||
|  |             else: | ||||||
|  |                 gray_image = image | ||||||
| 
 | 
 | ||||||
|         features = [] |             # Apply adaptive thresholding | ||||||
|         for contour in contours: |             binary_image = cv2.adaptiveThreshold( | ||||||
|             if cv2.contourArea(contour) < 22: |                 gray_image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 2 | ||||||
|                 continue |             ) | ||||||
| 
 | 
 | ||||||
|             x, y, width, height = cv2.boundingRect(contour) |             # Find contours | ||||||
|             letter_image = gray_image[y:y + height, x:x + width] |             contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | ||||||
|             letter_image = cv2.resize(letter_image, (28, 28)) |             if not contours: | ||||||
|  |                 print("No contours found.") | ||||||
|  |                 return np.array([]) | ||||||
| 
 | 
 | ||||||
|             # Calcul des caractéristiques HOG |             features = [] | ||||||
|             hog = cv2.HOGDescriptor() |             for contour in contours: | ||||||
|             hog_features = hog.compute(letter_image) |                 if cv2.contourArea(contour) < 22: | ||||||
|  |                     continue | ||||||
| 
 | 
 | ||||||
|             features.append(hog_features.flatten()) |                 x, y, w, h = cv2.boundingRect(contour) | ||||||
|  |                 letter_image = gray_image[y:y + h, x:x + w] | ||||||
|  |                 letter_image = cv2.resize(letter_image, (28, 28)) | ||||||
| 
 | 
 | ||||||
|         features = np.array(features) |                 # Compute HOG features | ||||||
|  |                 hog_features = self.hog.compute(letter_image) | ||||||
|  |                 features.append(hog_features.flatten()) | ||||||
| 
 | 
 | ||||||
|         # Normalisation des caractéristiques |             features = np.array(features) | ||||||
|         norms = np.linalg.norm(features, axis=1, keepdims=True) |             if features.size == 0: | ||||||
|         features = features / np.where(norms > 1e-6, norms, 1) |                 print("No features extracted.") | ||||||
|  |                 return np.array([]) | ||||||
| 
 | 
 | ||||||
|         return features |             norms = np.linalg.norm(features, axis=1, keepdims=True) | ||||||
|  |             features = features / np.where(norms > 1e-6, norms, 1) | ||||||
|  | 
 | ||||||
|  |             return features | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"Error in extract_features: {e}") | ||||||
|  |             return np.array([]) | ||||||
| 
 | 
 | ||||||
|     def train(self, dataset_path): |     def train(self, dataset_path): | ||||||
|         """ |  | ||||||
|         Entraîner le classificateur avec un catalogue d'images organisées par classe. |  | ||||||
| 
 |  | ||||||
|         :param dataset_path: Chemin vers le dossier contenant les images classées |  | ||||||
|         """ |  | ||||||
|         class_features = defaultdict(list) |         class_features = defaultdict(list) | ||||||
|         total_images = 0 |         total_images = 0 | ||||||
| 
 | 
 | ||||||
| @ -74,17 +77,24 @@ class BayesianClassifier: | |||||||
|                 if class_name not in self.classes: |                 if class_name not in self.classes: | ||||||
|                     self.classes.append(class_name) |                     self.classes.append(class_name) | ||||||
| 
 | 
 | ||||||
|                 for image_name in os.listdir(class_folder_path): |                 for img_name in os.listdir(class_folder_path): | ||||||
|                     image_path = os.path.join(class_folder_path, image_name) |                     img_path = os.path.join(class_folder_path, img_name) | ||||||
|                     if os.path.isfile(image_path): |                     if os.path.isfile(img_path): | ||||||
|                         image = cv2.imread(image_path) |                         try: | ||||||
|                         if image is not None: |                             image = cv2.imread(img_path) | ||||||
|                             features = self.extract_features(image) |                             if image is not None: | ||||||
|                             for feature in features: |                                 features = self.extract_features(image) | ||||||
|                                 class_features[class_name].append(feature) |                                 if features.size > 0: | ||||||
|                             total_images += 1 |                                     for feature in features: | ||||||
|  |                                         class_features[class_name].append(feature) | ||||||
|  |                                     total_images += 1 | ||||||
|  |                                 else: | ||||||
|  |                                     print(f"No features extracted for {img_path}") | ||||||
|  |                             else: | ||||||
|  |                                 print(f"Failed to load image: {img_path}") | ||||||
|  |                         except Exception as e: | ||||||
|  |                             print(f"Error processing {img_path}: {e}") | ||||||
| 
 | 
 | ||||||
|         # Calcul des moyennes, variances et probabilités a priori |  | ||||||
|         for class_name in self.classes: |         for class_name in self.classes: | ||||||
|             if class_name in class_features: |             if class_name in class_features: | ||||||
|                 features = np.array(class_features[class_name]) |                 features = np.array(class_features[class_name]) | ||||||
| @ -94,50 +104,7 @@ class BayesianClassifier: | |||||||
| 
 | 
 | ||||||
|         print("Training completed for classes:", self.classes) |         print("Training completed for classes:", self.classes) | ||||||
| 
 | 
 | ||||||
|     def predict(self, image): |  | ||||||
|         """ |  | ||||||
|         Prédire la classe d'une image donnée. |  | ||||||
| 
 |  | ||||||
|         :param image: Image à classer |  | ||||||
|         :return: Classe prédite |  | ||||||
|         """ |  | ||||||
|         rotation_weights = { |  | ||||||
|             0: 1.0, |  | ||||||
|             90: 0.5, |  | ||||||
|             180: 0.5, |  | ||||||
|             270: 0.5 |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         posterior_probabilities = {} |  | ||||||
| 
 |  | ||||||
|         for rotation, weight in rotation_weights.items(): |  | ||||||
|             k = rotation // 90 |  | ||||||
|             rotated_image = np.rot90(image, k) |  | ||||||
|             features = self.extract_features(rotated_image) |  | ||||||
| 
 |  | ||||||
|             for class_name in self.classes: |  | ||||||
|                 mean = self.feature_means[class_name] |  | ||||||
|                 variance = self.feature_variances[class_name] |  | ||||||
|                 prior = self.class_priors[class_name] |  | ||||||
| 
 |  | ||||||
|                 likelihood = -0.5 * np.sum((features - mean) ** 2 / variance) + np.log(2 * np.pi * variance) |  | ||||||
|                 posterior = likelihood + np.log(prior) |  | ||||||
| 
 |  | ||||||
|                 weighted_posterior = posterior * (1 - weight * 0.5) |  | ||||||
| 
 |  | ||||||
|                 if class_name not in posterior_probabilities: |  | ||||||
|                     posterior_probabilities[class_name] = weighted_posterior |  | ||||||
|                 else: |  | ||||||
|                     posterior_probabilities[class_name] = max(posterior_probabilities[class_name], weighted_posterior) |  | ||||||
| 
 |  | ||||||
|         return max(posterior_probabilities, key=posterior_probabilities.get) |  | ||||||
| 
 |  | ||||||
|     def save_model(self, model_path): |     def save_model(self, model_path): | ||||||
|         """ |  | ||||||
|         Sauvegarder le modèle Bayésien dans un fichier. |  | ||||||
| 
 |  | ||||||
|         :param model_path: Chemin du fichier de sauvegarde |  | ||||||
|         """ |  | ||||||
|         model_data = { |         model_data = { | ||||||
|             "feature_means": self.feature_means, |             "feature_means": self.feature_means, | ||||||
|             "feature_variances": self.feature_variances, |             "feature_variances": self.feature_variances, | ||||||
| @ -147,37 +114,51 @@ class BayesianClassifier: | |||||||
|         if not os.path.exists(os.path.dirname(model_path)): |         if not os.path.exists(os.path.dirname(model_path)): | ||||||
|             os.makedirs(os.path.dirname(model_path)) |             os.makedirs(os.path.dirname(model_path)) | ||||||
|         torch.save(model_data, model_path) |         torch.save(model_data, model_path) | ||||||
|         print("Model saved in {}".format(model_path)) |         print(f"Model saved to {model_path}") | ||||||
| 
 | 
 | ||||||
|     def load_model(self, model_path): |     def load_model(self, model_path): | ||||||
|         """ |  | ||||||
|         Charger un modèle Bayésien sauvegardé. |  | ||||||
| 
 |  | ||||||
|         :param model_path: Chemin du fichier de modèle |  | ||||||
|         """ |  | ||||||
|         if os.path.exists(model_path): |         if os.path.exists(model_path): | ||||||
|             model_data = torch.load(model_path) |             model_data = torch.load(model_path, weights_only=False) | ||||||
|             self.feature_means = model_data["feature_means"] |             self.feature_means = model_data["feature_means"] | ||||||
|             self.feature_variances = model_data["feature_variances"] |             self.feature_variances = model_data["feature_variances"] | ||||||
|             self.class_priors = model_data["class_priors"] |             self.class_priors = model_data["class_priors"] | ||||||
|             self.classes = model_data["classes"] |             self.classes = model_data["classes"] | ||||||
|             print("Model loaded from {}".format(model_path)) |             print(f"Model loaded from {model_path}") | ||||||
|         else: |         else: | ||||||
|             print("Model does not exist: {}".format(model_path)) |             print(f"No model found at {model_path}.") | ||||||
|  | 
 | ||||||
|  |     def predict(self, image): | ||||||
|  |         try: | ||||||
|  |             features = self.extract_features(image) | ||||||
|  |             if features.size == 0: | ||||||
|  |                 print("Empty features, skipping prediction.") | ||||||
|  |                 return None | ||||||
|  | 
 | ||||||
|  |             posteriors = {} | ||||||
|  |             for class_name in self.classes: | ||||||
|  |                 mean = self.feature_means[class_name] | ||||||
|  |                 variance = self.feature_variances[class_name] | ||||||
|  |                 prior = self.class_priors[class_name] | ||||||
|  | 
 | ||||||
|  |                 likelihood = -0.5 * np.sum(((features - mean) ** 2) / variance + np.log(2 * np.pi * variance)) | ||||||
|  |                 posterior = likelihood + np.log(prior) | ||||||
|  |                 posteriors[class_name] = posterior | ||||||
|  | 
 | ||||||
|  |             return max(posteriors, key=posteriors.get) | ||||||
|  |         except Exception as e: | ||||||
|  |             print(f"Error in prediction: {e}") | ||||||
|  |             return None | ||||||
| 
 | 
 | ||||||
|     def visualize(self): |     def visualize(self): | ||||||
|         """ |  | ||||||
|         Visualiser les moyennes des caractéristiques pour chaque classe. |  | ||||||
|         """ |  | ||||||
|         if not self.classes: |         if not self.classes: | ||||||
|             print("No classes to visualize") |             print("No classes to visualize.") | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         for class_name in self.classes: |         for class_name in self.classes: | ||||||
|             mean_features = self.feature_means[class_name] |             mean_features = self.feature_means[class_name] | ||||||
| 
 | 
 | ||||||
|             plt.figure(figsize=(10, 4)) |             plt.figure(figsize=(10, 4)) | ||||||
|             plt.title("Mean features for class: {}".format(class_name)) |             plt.title(f"Mean features for class: {class_name}") | ||||||
|             plt.plot(mean_features) |             plt.plot(mean_features) | ||||||
|             plt.xlabel("Feature Index") |             plt.xlabel("Feature Index") | ||||||
|             plt.ylabel("Mean Value") |             plt.ylabel("Mean Value") | ||||||
|  | |||||||
							
								
								
									
										162
									
								
								src/pipeline.py
									
									
									
									
									
								
							
							
						
						
									
										162
									
								
								src/pipeline.py
									
									
									
									
									
								
							| @ -3,182 +3,106 @@ import os | |||||||
| from matplotlib import pyplot as plt | from matplotlib import pyplot as plt | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class ObjectDetectionPipeline: | class ObjectDetectionPipeline: | ||||||
|     def __init__(self, image_path, model=None): |     def __init__(self, image_path, model=None, output_dir="output", min_contour_area=50, binary_threshold=127): | ||||||
|         """ |         """ | ||||||
|         Initialisation de la pipeline de détection d'objets avec le chemin de l'image et le modèle. |         Initialisation de la pipeline de détection d'objets. | ||||||
| 
 | 
 | ||||||
|         :param image_path: Chemin de l'image à traiter |         :param image_path: Chemin de l'image à traiter | ||||||
|         :param model: Modèle de classification à utiliser (par défaut, aucun modèle n'est chargé) |         :param model: Modèle de classification à utiliser | ||||||
|  |         :param output_dir: Dossier où les résultats seront sauvegardés | ||||||
|  |         :param min_contour_area: Aire minimale des contours à prendre en compte | ||||||
|  |         :param binary_threshold: Seuil de binarisation pour les canaux | ||||||
|         """ |         """ | ||||||
|         self.image_path = image_path |         self.image_path = image_path | ||||||
|         self.image = None |         self.image = None | ||||||
|         self.binary_image = None |         self.binary_image = None | ||||||
|         self.model = model  # Le modèle personnalisé à utiliser |         self.model = model | ||||||
|  |         self.output_dir = output_dir | ||||||
|  |         self.min_contour_area = min_contour_area | ||||||
|  |         self.binary_threshold = binary_threshold | ||||||
| 
 | 
 | ||||||
|     def load_model(self, model_path: str, instance_classifier=None): |         if not os.path.exists(self.output_dir): | ||||||
|         """ |             os.makedirs(self.output_dir) | ||||||
|         Charger un modèle pré-entraîné ou un classifieur bayésien. |  | ||||||
| 
 |  | ||||||
|         :param model_path: Chemin du fichier du modèle |  | ||||||
|         :param instance_classifier: Instance du classifieur à utiliser |  | ||||||
|         """ |  | ||||||
|         if os.path.exists(model_path): |  | ||||||
|             self.model = instance_classifier  # Créer une instance du classifieur |  | ||||||
|             print(f"Chargement du modèle bayésien depuis {model_path}") |  | ||||||
|             self.model.load_model(model_path)  # Charger les paramètres du modèle |  | ||||||
|             print(f"Modèle bayésien chargé depuis {model_path}") |  | ||||||
|         else: |  | ||||||
|             print(f"Aucun modèle trouvé à {model_path}. Un nouveau modèle sera créé.") |  | ||||||
| 
 | 
 | ||||||
|     def load_image(self): |     def load_image(self): | ||||||
|         """ |         """Charge l'image spécifiée.""" | ||||||
|         Charger l'image spécifiée par le chemin d'accès. |  | ||||||
| 
 |  | ||||||
|         :return: Image chargée |  | ||||||
|         """ |  | ||||||
|         self.image = cv2.imread(self.image_path) |         self.image = cv2.imread(self.image_path) | ||||||
|         if self.image is None: |         if self.image is None: | ||||||
|             raise FileNotFoundError(f"L'image {self.image_path} est introuvable.") |             raise FileNotFoundError(f"L'image {self.image_path} est introuvable.") | ||||||
|         return self.image |         return self.image | ||||||
| 
 | 
 | ||||||
|     def preprocess_image(self): |     def preprocess_image(self): | ||||||
|         """ |         """Prétraite l'image pour la préparer à l'inférence.""" | ||||||
|         Prétraiter l'image pour la préparer à l'inférence. |  | ||||||
| 
 |  | ||||||
|         :return: Image binarisée |  | ||||||
|         """ |  | ||||||
|         # Binarisation de l'image par canaux |  | ||||||
|         channels = cv2.split(self.image) |         channels = cv2.split(self.image) | ||||||
|         binary_images = [] |         binary_images = [] | ||||||
| 
 | 
 | ||||||
|         for channel in channels: |         for channel in channels: | ||||||
|             _, binary_channel = cv2.threshold(channel, 127, 255, cv2.THRESH_BINARY_INV) |             _, binary_channel = cv2.threshold(channel, self.binary_threshold, 255, cv2.THRESH_BINARY_INV) | ||||||
|             binary_images.append(binary_channel) |             binary_images.append(binary_channel) | ||||||
| 
 | 
 | ||||||
|         # Fusionner les canaux binarisés |  | ||||||
|         binary_image = cv2.bitwise_or(binary_images[0], binary_images[1]) |         binary_image = cv2.bitwise_or(binary_images[0], binary_images[1]) | ||||||
|         binary_image = cv2.bitwise_or(binary_image, binary_images[2]) |         binary_image = cv2.bitwise_or(binary_image, binary_images[2]) | ||||||
|         self.binary_image = binary_image |         self.binary_image = binary_image | ||||||
|         return binary_image |         return binary_image | ||||||
| 
 | 
 | ||||||
|     def detect_and_classify_objects(self): |     def detect_and_classify_objects(self): | ||||||
|         """ |         """Détecte et classe les objets présents dans l'image.""" | ||||||
|         Détecter et classer les objets présents dans l'image en fonction des contours détectés. |  | ||||||
| 
 |  | ||||||
|         :return: Dictionnaire des classes détectées et objets détectés |  | ||||||
|         """ |  | ||||||
|         if self.model is None: |         if self.model is None: | ||||||
|             print("Aucun modèle de classification fourni.") |             raise ValueError("Aucun modèle de classification fourni.") | ||||||
|             return {} |  | ||||||
| 
 | 
 | ||||||
|         self.binary_image = self.preprocess_image() |         self.binary_image = self.preprocess_image() | ||||||
| 
 |  | ||||||
|         # Trouver les contours dans l'image binarisée |  | ||||||
|         contours, _ = cv2.findContours(self.binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |         contours, _ = cv2.findContours(self.binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | ||||||
| 
 | 
 | ||||||
|         class_counts = defaultdict(int) |         class_counts = defaultdict(int) | ||||||
|         detected_objects = [] |         detected_objects = [] | ||||||
| 
 | 
 | ||||||
|         for contour in contours: |         for contour in contours: | ||||||
|             if cv2.contourArea(contour) < 50: |             if cv2.contourArea(contour) < self.min_contour_area: | ||||||
|                 continue |                 continue | ||||||
| 
 | 
 | ||||||
|             x, y, w, h = cv2.boundingRect(contour) |             x, y, w, h = cv2.boundingRect(contour) | ||||||
|             letter_image = self.image[y:y + h, x:x + w] |             letter_image = self.image[y:y + h, x:x + w] | ||||||
| 
 | 
 | ||||||
|             # Prédiction avec le modèle |  | ||||||
|             predicted_class = self.model.predict(letter_image) |             predicted_class = self.model.predict(letter_image) | ||||||
|  |             if predicted_class is None: | ||||||
|  |                 print("Skipping object with invalid prediction.") | ||||||
|  |                 continue | ||||||
| 
 | 
 | ||||||
|             # Incrémenter le comptage de la classe prédite |  | ||||||
|             class_counts[predicted_class] += 1 |             class_counts[predicted_class] += 1 | ||||||
| 
 |  | ||||||
|             # Ajouter les coordonnées et la classe prédite |  | ||||||
|             detected_objects.append((x, y, w, h, predicted_class)) |             detected_objects.append((x, y, w, h, predicted_class)) | ||||||
| 
 | 
 | ||||||
|         return dict(sorted(class_counts.items())), detected_objects |         return dict(sorted(class_counts.items())), detected_objects | ||||||
| 
 | 
 | ||||||
|     def display_results(self, class_counts, detected_objects): |     def save_results(self, class_counts, detected_objects): | ||||||
|         """ |         """Sauvegarde les résultats de détection et classification.""" | ||||||
|         Afficher les résultats de la détection et classification. |         # Sauvegarder l'image binaire | ||||||
| 
 |         binary_output_path = os.path.join(self.output_dir, "binary_image.jpg") | ||||||
|         :param class_counts: Dictionnaire des classes détectées et leurs occurrences |         cv2.imwrite(binary_output_path, self.binary_image) | ||||||
|         :param detected_objects: Liste des objets détectés avec leurs coordonnées et classes prédites |  | ||||||
|         """ |  | ||||||
|         self.display_binary_image() |  | ||||||
|         self.display_image_with_classes(detected_objects) |  | ||||||
|         self.display_image_with_annotations(detected_objects) |  | ||||||
|         self.display_classes_count(class_counts) |  | ||||||
| 
 |  | ||||||
|     def display_binary_image(self): |  | ||||||
|         """ |  | ||||||
|         Afficher l'image binaire résultant du prétraitement. |  | ||||||
|         """ |  | ||||||
|         plt.figure(figsize=(self.binary_image.shape[1] / 100, self.binary_image.shape[0] / 100)) |  | ||||||
|         plt.imshow(self.binary_image, cmap='gray') |  | ||||||
|         plt.axis('off') |  | ||||||
|         plt.show() |  | ||||||
| 
 |  | ||||||
|     def display_image_with_classes(self, detected_objects): |  | ||||||
|         """ |  | ||||||
|         Afficher l'image avec les classes prédites annotées. |  | ||||||
| 
 |  | ||||||
|         :param detected_objects: Liste des objets détectés avec leurs coordonnées et classes prédites |  | ||||||
|         """ |  | ||||||
|         image_with_classes_only = self.image.copy() |  | ||||||
| 
 | 
 | ||||||
|  |         # Sauvegarder l'image annotée | ||||||
|  |         annotated_image = self.image.copy() | ||||||
|         for (x, y, w, h, predicted_class) in detected_objects: |         for (x, y, w, h, predicted_class) in detected_objects: | ||||||
|             if predicted_class[-1] == "_": |             cv2.rectangle(annotated_image, (x, y), (x + w, y + h), (0, 255, 0), 2) | ||||||
|                 text = predicted_class.split("_")[0].upper() |             cv2.putText(annotated_image, str(predicted_class), (x, y - 10), | ||||||
|             else: |  | ||||||
|                 text = predicted_class.lower() |  | ||||||
| 
 |  | ||||||
|             cv2.rectangle(image_with_classes_only, (x, y), (x + w, y + h), (255, 255, 255), -1) |  | ||||||
|             font_scale = 0.7 |  | ||||||
|             font_thickness = 2 |  | ||||||
|             text_size = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, font_thickness)[0] |  | ||||||
|             text_x = x + (w - text_size[0]) // 2 |  | ||||||
|             text_y = y + (h + text_size[1]) // 2 |  | ||||||
| 
 |  | ||||||
|             cv2.putText(image_with_classes_only, text, (text_x, text_y), |  | ||||||
|                         cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0), font_thickness) |  | ||||||
| 
 |  | ||||||
|         fig = plt.figure(figsize=(image_with_classes_only.shape[1] / 100, image_with_classes_only.shape[0] / 100)) |  | ||||||
|         plt.imshow(cv2.cvtColor(image_with_classes_only, cv2.COLOR_BGR2RGB)) |  | ||||||
|         plt.axis('off') |  | ||||||
|         plt.show() |  | ||||||
| 
 |  | ||||||
|     def display_image_with_annotations(self, detected_objects): |  | ||||||
|         """ |  | ||||||
|         Afficher l'image avec les annotations des objets détectés (rectangles et classes). |  | ||||||
| 
 |  | ||||||
|         :param detected_objects: Liste des objets détectés avec leurs coordonnées et classes prédites |  | ||||||
|         """ |  | ||||||
|         image_with_annotations = self.image.copy() |  | ||||||
|         for (x, y, w, h, predicted_class) in detected_objects: |  | ||||||
|             if predicted_class[-1] == "_": |  | ||||||
|                 text = predicted_class.split("_")[0].upper() |  | ||||||
|             else: |  | ||||||
|                 text = predicted_class.lower() |  | ||||||
| 
 |  | ||||||
|             cv2.rectangle(image_with_annotations, (x, y), (x + w, y + h), (0, 255, 0), 2) |  | ||||||
|             cv2.putText(image_with_annotations, text, (x, y - 10), |  | ||||||
|                         cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) |                         cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) | ||||||
|  |         annotated_output_path = os.path.join(self.output_dir, "annotated_image.jpg") | ||||||
|  |         cv2.imwrite(annotated_output_path, annotated_image) | ||||||
| 
 | 
 | ||||||
|         fig = plt.figure(figsize=(image_with_annotations.shape[1] / 100, image_with_annotations.shape[0] / 100)) |         # Sauvegarder les classes et leurs occurrences | ||||||
|         plt.imshow(cv2.cvtColor(image_with_annotations, cv2.COLOR_BGR2RGB)) |         results_text_path = os.path.join(self.output_dir, "results.txt") | ||||||
|         plt.axis('off') |         with open(results_text_path, "w") as f: | ||||||
|         plt.show() |             for class_name, count in class_counts.items(): | ||||||
|  |                 f.write(f"{class_name}: {count}\n") | ||||||
| 
 | 
 | ||||||
|     def display_classes_count(self, class_counts): |     def display_results(self, class_counts, detected_objects): | ||||||
|         """ |         """Affiche et sauvegarde les résultats.""" | ||||||
|         Afficher le nombre d'objets détectés par classe. |         self.save_results(class_counts, detected_objects) | ||||||
| 
 | 
 | ||||||
|         :param class_counts: Dictionnaire des classes détectées et leurs occurrences |  | ||||||
|         """ |  | ||||||
|         plt.figure(figsize=(10, 5)) |         plt.figure(figsize=(10, 5)) | ||||||
|         plt.bar(class_counts.keys(), class_counts.values()) |         plt.bar(class_counts.keys(), class_counts.values()) | ||||||
|         plt.xlabel("Classes") |         plt.xlabel("Classes") | ||||||
|         plt.ylabel("Nombre de lettres") |         plt.ylabel("Nombre d'objets") | ||||||
|         plt.title("Classes détectées et leur nombre") |         plt.title("Distribution des classes détectées") | ||||||
|         plt.show() |         plt.show() | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user