Sur cette page se trouvent des exercices de TP sur le Chapitre 6. Ils sont classés par niveau de difficulté suivant :
Dans cet exercice, vous allez créer un dataset où certaines images contiennent votre objet et d'autres non. Vous comparerez ensuite un modèle CNN custom avec YOLO, avec et sans augmentation de données.
Objectif : Maîtriser la détection d'objets avec gestion des cas négatifs (images sans objet) et comparer les approches custom vs YOLO.
Matériel nécessaire :
Partie A : Création du dataset (100 images avec objet, 40 sans objet)
Consigne : Écrire un programme qui :
1) Prend une vidéo :
2) Extrait les frames de la vidéo.
3) Annote les images au format YOLO :
dataset/images/ et dataset/labels/class_id x_center y_center width height (normalisé 0-1)4) (optionnel) Vérifie le dataset.
Questions Partie A :
5) Pourquoi est-il crucial d'avoir des images sans l'objet dans le dataset ?
6) Que se passerait-il si on entraînait uniquement sur des images positives ?
7) Quel ratio présence/absence est optimal ? (70/30, 80/20, 50/50 ?)
Astuce Partie A :
Résultat attendu Partie A :
dataset/images/ et dataset/labels/Partie B : CNN Custom avec gestion de l'objectness
Consigne : Écrire un programme qui :
1) Implémente une architecture CNN simple pour la détection que vous devez proposer.
2) Crée un Dataset PyTorch pour charger les images et labels YOLO.
3) Implémente la loss personnalisée pour la détection que vous devez proposer.
4) Entraîne le modèle SANS augmentation pendant 100 epochs avec un early stopping d'une patience = 15.
5) Sauvegarde le meilleur modèle dans best_model_no_aug.pth.
6) Implémente une fonction d'évaluation avec métriques (TP, FP, TN, FN, IoU).
Astuce Partie B :
[has_object, x_center, y_center, width, height]. Sortie finale avec nn.Sigmoid() pour borner entre 0 et 1. Proposez une architecture simple (3-4 conv layers + pooling) avec des couches Linear dont la dernière est nn.Linear(nb_features, 5).target = torch.tensor([0.0, 0.0, 0.0, 0.0, 0.0]). Pour les images AVEC objet, parsez le fichier .txt et retournez target = torch.tensor([1.0, x_c, y_c, w, h]).mask = target_obj > 0.5 pour ne calculer loss_bbox QUE sur les images avec objet présent.torch.save(model.state_dict(), 'best_model_no_aug.pth').x_min = x_c - w/2, puis calculez l'intersection et l'union des rectangles.torch.utils.data.random_split() pour diviser le dataset de manière reproductible.Résultat attendu Partie B :
best_model_no_aug.pth)Partie C : YOLO11 sans augmentation - Comparaison
Consigne : Écrire un programme qui :
1) Crée un fichier dataset.yaml pour YOLO :
# data_cube/dataset.yaml
path: /chemin/absolu/vers/data_cube
train: images
val: images
test: images
nc: 1
names: ['nom_label']
2) Entraîne YOLO11 sur le dataset.
3) Évalue YOLO11 sur le test set avec les mêmes métriques que le CNN custom.
4) Compare les résultats dans un tableau comme par exemple :
# TODO: Afficher tableau comparatif
# +------------+---------------+-------------+
# | Métrique | CNN Custom | YOLO11 |
# +------------+---------------+-------------+
# | Accuracy | XX.XX% | XX.XX% |
# | Precision | XX.XX% | XX.XX% |
# | Recall | XX.XX% | XX.XX% |
# | Mean IoU | 0.XXXX | 0.XXXX |
# | FPS | X.X | XX.X |
# | Params | 3.3M | 2.6M |
# +------------+---------------+-------------+
Questions Partie C :
5) Quel modèle est le plus rapide en inférence ? Le plus précis ?
6) Pourquoi YOLO est-il meilleur malgré moins de paramètres ?
Astuce Partie C :
Résultat attendu Partie C :
runs/detect/yolo_cube_detect/weights/best.pt)############################
Dans cet exercice, vous allez créer un détecteur capable de distinguer deux objets différents sur la même image.
Objectif : Maîtriser la détection multi-classe et gérer plusieurs objets simultanés.
Matériel nécessaire :
Partie A : Dataset multi-objets
Consigne : Écrire un programme qui :
1) Crée un dataset augmenté avec rééquilibrage :
import torchvision.transforms as T
import random
class AugmentedYOLODataset(Dataset):
"""Dataset avec augmentation adaptée à la détection."""
def __init__(self, image_dir, label_dir, augment=False):
# TODO: Initialiser comme YOLODetectionDataset
# TODO: Ajouter transformations d'augmentation
# self.color_jitter = T.ColorJitter(brightness=0.3, contrast=0.3)
pass
def horizontal_flip(self, image, bbox):
"""Flip horizontal avec ajustement bbox."""
image = T.functional.hflip(image)
if bbox is not None:
bbox = bbox.copy() # IMPORTANT: copier !
bbox[0] = 1.0 - bbox[0] # x_center inversé
return image, bbox
def __getitem__(self, idx):
# TODO: Charger image et label
# TODO: Si augment=True:
# - 50% flip horizontal
# - 80% color jitter
# TODO: Appliquer normalisation
pass
2) Rééquilibre le dataset (dupliquer les images sans objet pour avoir 50/50).
3) Entraîne le CNN custom AVEC augmentation :
# TODO: Créer AugmentedYOLODataset avec augment=True pour train
# TODO: Entraîner avec mêmes hyperparamètres que Partie B
# TODO: Comparer train loss et val loss (vérifier overfitting réduit)
# TODO: Sauvegarder dans 'best_model_with_aug.pth'
4) Entraîne YOLO avec AVEC augmentation.
5) Compare les 4 modèles finaux :
# TODO: Charger les 4 modèles
# TODO: Évaluer sur le MÊME test set
# TODO: Afficher tableau comparatif complet
# +---------------------+---------------+---------------+-------------+-------------+
# | Métrique | CNN No Aug | CNN + Aug | YOLO11 |YOLO11 + Aug |
# +---------------------+---------------+---------------+-------------+-------------+
# | Accuracy | XX.XX% | XX.XX% | XX.XX% |XX.XX% |
# | Precision | XX.XX% | XX.XX% | XX.XX% |XX.XX% |
# | Recall | XX.XX% | XX.XX% | XX.XX% |XX.XX% |
# | Mean IoU | 0.XXXX | 0.XXXX | 0.XXXX |0.XXXX |
# | Gap Train/Val (%) | XX | XX | XX | XX |
# +---------------------+---------------+---------------+-------------+-------------+
Questions Partie D :
6) L'augmentation améliore-t-elle les performances du CNN custom ?
7) Le gap train/val est-il réduit avec l'augmentation ?
8) Pourquoi l'augmentation seule ne suffit pas Ă rattraper YOLO ?
9) Quelle est l'importance du rééquilibrage (50/50) ?
Astuce Partie D :
Résultat attendu Partie D :
############################
Dans cet exercice, vous allez créer un détecteur capable de distinguer deux objets différents sur la même image.
Objectif : Maîtriser la détection multi-classe et gérer plusieurs objets simultanés.
Matériel nécessaire :
Partie A : Dataset multi-objets
Consigne : Écrire un programme qui :
1) Capture 200 images réparties ainsi par exemple :
2) Annote le dataset :
class_id x_center y_center width height3) Organise et importe le dataset pour YOLO.
Questions Partie A :
4) Quelle est la difficulté principale de ce dataset comparé à l'exercice 1 ?
5) Comment équilibrer le dataset si un objet est plus difficile à détecter ?
6) Que se passe-t-il si les deux objets se chevauchent beaucoup ?
Astuce Partie A :
Résultat attendu Partie A :
Partie B : YOLO multi-classe
Consigne : Écrire un programme qui :
1) Crée le fichier dataset.yaml pour 2 classes :
# data_multiclass/dataset.yaml
path: /chemin/absolu/vers/data_multiclass
train: images/train
val: images/val
test: images/test
nc: 2
names: ['label1', 'label2']
2) Entraîne YOLO11 multi-classe.
3) Évalue par classe (mAP, precision, recall, etc.).
Questions Partie B :
4) Comment YOLO gère-t-il plusieurs objets de classes différentes sur une même image ?
5) Que se passe-t-il si les deux objets se chevauchent fortement ?
6) Comment améliorer la détection si objet_1 est systématiquement mieux détecté qu'objet_2 ?
7) Pourquoi utiliser NMS (Non-Maximum Suppression) ?
Astuce Partie B :
Résultat attendu Partie B :
############################
Dans cette section, il y a des exercices supplémentaires pour vous entraîner. Ils suivent le même classement de difficulté que précédemment.
Cet exercice propose de visualiser et analyser les résultats de détection de vos modèles.
Objectif : Comprendre les forces et faiblesses de vos modèles de détection à travers la visualisation.
Consignes :
1) Utiliser le modèle CNN custom ou YOLO entraîné dans l'exercice 1
2) Créer une fonction qui affiche les meilleurs résultats de détection :
def visualize_best_detections(model, test_images, test_labels, num_examples=6):
"""
Affiche les meilleures détections (IoU > 0.8)
"""
# TODO: Prédire sur test set
# TODO: Filtrer les détections avec has_object > 0.5 ET IoU > 0.8
# TODO: Afficher en grille 2x3:
# - Image originale avec bbox GT (vert)
# - Image avec bbox prédite (bleu)
# - Titre: IoU = X.XX, Conf = X.XX
pass
3) Créer une fonction pour visualiser les images sans objet (vrais négatifs) :
def visualize_empty_images(model, test_images, test_labels, num_examples=6):
"""
Affiche les images correctement identifiées comme vides
"""
# TODO: Filtrer images oĂą GT n'a pas d'objet (target[0] == 0)
# TODO: Filtrer où prédiction < 0.5 (correct)
# TODO: Afficher avec titre: "Conf vide: X.XX"
pass
4) Créer une fonction pour visualiser les erreurs :
def visualize_errors(model, test_images, test_labels, num_examples=6):
"""
Affiche les cas problématiques
"""
# TODO: Cas 1: Faux positifs (détection sur fond vide)
# TODO: Cas 2: Faux négatifs (objet non détecté)
# TODO: Cas 3: Mauvaise localisation (IoU < 0.5 mais objet détecté)
# TODO: Afficher en 3 lignes
pass
5) Comparer les visualisations entre CNN custom et YOLO11
Questions :
6) Quel type d'erreur est le plus fréquent pour chaque modèle ?
7) Dans quelles conditions les modèles ont-ils le plus de difficultés ?
8) YOLO fait-il des erreurs différentes du CNN custom ?
Astuce :
cv2.rectangle(img, (x_min, y_min), (x_max, y_max), color, 2)x_min = int((x_c - w/2) * img_width)(0, 255, 0), prédiction : bleu (255, 0, 0)(GT sans objet) AND (pred > 0.5)(GT avec objet) AND (pred < 0.5)plt.subplots() pour créer des grilles de visualisationRésultats attendus :
Dans cet exercice, vous allez implémenter l'augmentation de données adaptée à la détection et comparer les résultats.
Objectif :
Comprendre l'impact de l'augmentation sur la généralisation et réduire l'overfitting.
Consignes :
1) Créer un dataset augmenté avec rééquilibrage :
import torchvision.transforms as T
import random
class AugmentedYOLODataset(Dataset):
"""Dataset avec augmentation adaptée à la détection."""
def __init__(self, image_dir, label_dir, augment=False):
# TODO: Initialiser comme YOLODetectionDataset
# TODO: Ajouter transformations d'augmentation
# self.color_jitter = T.ColorJitter(brightness=0.3, contrast=0.3)
pass
def horizontal_flip(self, image, bbox):
"""Flip horizontal avec ajustement bbox."""
image = T.functional.hflip(image)
if bbox is not None:
bbox = bbox.copy() # IMPORTANT: copier !
bbox[0] = 1.0 - bbox[0] # x_center inversé
return image, bbox
def __getitem__(self, idx):
# TODO: Charger image et label
# TODO: Si augment=True:
# - 50% flip horizontal
# - 80% color jitter
# TODO: Appliquer normalisation
pass
2) Rééquilibrer le dataset (dupliquer les images sans objet pour avoir 50/50)
3) Entraîner le CNN custom AVEC augmentation :
# TODO: Créer AugmentedYOLODataset avec augment=True pour train
# TODO: Entraîner avec mêmes hyperparamètres que Partie B
# TODO: Comparer train loss et val loss (vérifier overfitting réduit)
# TODO: Sauvegarder dans 'best_model_with_aug.pth'
4) Entraîner YOLO AVEC augmentation
5) Comparer les 4 modèles finaux :
# TODO: Charger les 4 modèles
# TODO: Évaluer sur le MÊME test set
# TODO: Afficher tableau comparatif complet
# +---------------------+---------------+---------------+-------------+-------------+
# | Métrique | CNN No Aug | CNN + Aug | YOLO11 |YOLO11 + Aug |
# +---------------------+---------------+---------------+-------------+-------------+
# | Accuracy | XX.XX% | XX.XX% | XX.XX% |XX.XX% |
# | Precision | XX.XX% | XX.XX% | XX.XX% |XX.XX% |
# | Recall | XX.XX% | XX.XX% | XX.XX% |XX.XX% |
# | Mean IoU | 0.XXXX | 0.XXXX | 0.XXXX |0.XXXX |
# | Gap Train/Val (%) | XX | XX | XX | XX |
# +---------------------+---------------+---------------+-------------+-------------+
Questions :
6) L'augmentation améliore-t-elle les performances du CNN custom ?
7) Le gap train/val est-il réduit avec l'augmentation ?
8) Pourquoi l'augmentation seule ne suffit pas Ă rattraper YOLO ?
9) Quelle est l'importance du rééquilibrage (50/50) ?
Astuce :
Résultat attendu :
Cet exercice est une introduction au tracking d'objets en détectant frame par frame dans une vidéo.
Objectif :
Comprendre les bases de la détection vidéo et mesurer les performances en temps réel.
Qu'est-ce que le tracking ?
Le tracking (suivi d'objets) consiste à détecter et identifier le même objet à travers les frames d'une vidéo. Dans cet exercice simplifié, vous allez uniquement détecter l'objet sur chaque frame indépendamment, sans associer d'identité entre les frames.
Limitation de cette approche :
Cette approche est utile pour :
Matériel nécessaire :
Consignes :
1) Créer ou utiliser une vidéo de test (30-60 secondes) :
Scénario recommandé : - 0-10s : Aucun objet visible - 10-20s : Objet entre dans le champ, se déplace - 20-30s : Objet sort du champ - 30-40s : Objet réapparaît
2) Implémenter la détection frame par frame sur vidéo :
import cv2
from ultralytics import YOLO
import time
def detect_on_video(model_path, video_path, output_path, conf_threshold=0.5):
"""Détecte les objets sur chaque frame et sauvegarde la vidéo."""
# TODO: Charger le modèle YOLO
# TODO: Ouvrir la vidéo (fps, dimensions, nb_frames)
# TODO: Créer VideoWriter pour la sortie
# TODO: Boucle sur les frames:
# - Lire frame
# - Mesurer temps de traitement
# - Prédiction YOLO
# - Dessiner détections + info (frame, FPS)
# - Sauvegarder frame
# TODO: Libérer ressources et afficher stats
pass
3) Analyser les statistiques de détection :
import matplotlib.pyplot as plt
def plot_detection_stats(stats):
"""Visualise les statistiques."""
# TODO: Graphique 1: Détections par frame (ligne)
# TODO: Graphique 2: Distribution nb objets (histogramme)
# TODO: Graphique 3: Temps de traitement (ligne + moyenne)
# TODO: Sauvegarder la figure
pass
Questions :
4) Quel est le FPS moyen de votre système ? Est-ce suffisant pour du temps réel (>30 fps) ?
5) Pourquoi le temps de traitement varie-t-il d'une frame Ă l'autre ?
6) Comment pourriez-vous améliorer la vitesse si elle est trop lente ?
7) Quelles sont les limitations de cette approche sans identité d'objets ?
Astuce :
model.predict(frame, verbose=False) pour éviter logsfps = 1.0 / (time.time() - start_time)Pour aller plus loin : Tracking avec identités
Limitations du tracking frame par frame :
Cette approche simple ne permet pas de :
Solution : Tracking avec identités (Object ID)
Pour un vrai système de tracking, il faut :
Utilisation de YOLO pour le tracking avancé : https://docs.ultralytics.com/modes/track/
from ultralytics import YOLO
model = YOLO('yolo11n.pt')
results = model.track(source='video.mp4', persist=True) # Track avec IDs !