Amélioration de YOLO pour la détection et le suivi de véhicules et piétons
Les systèmes de transport intelligents jouent un rôle crucial dans les villes modernes, nécessitant une détection et un suivi précis des véhicules et piétons pour gérer la congestion et prévenir les accidents. Les méthodes traditionnelles basées sur des caractéristiques manuelles comme HOG ou Haar présentent des limitations en termes de robustesse et d'efficacité. Les algorithmes de détection d'objets basés sur l'apprentissage profond, en particulier la famille YOLO, offrent une alternative prometteuse grâce à leur rapidité et précision. Cependant, des défis persistent dans les scénarios réels, tels que la détection de petites cibles, les occlusions fréquentes, les variations d'éclairage et les conditions météorologiques extrêmes.
Ce travail propose une amélioration de l'algorithme YOLOv5s pour renforcer ses performances dans ces contextes complexes. Les améliorations clés incluent l'intégration d'un mécanisme d'attention, l'ajout d'une couche de détection pour les petites cibles et l'adoption d'une structure de fusion de caractéristiquse avancée. Ces modifications visent à augmenter la précision de détection tout en maintenant une vitesse d'inférence adaptée aux applications en temps réel.
Approche méthodologique
Le modèle amélioré repose sur trois composants principaux. D'abord, un module d'attention CBAM (Convolutional Block Attention Module) est intégré dans le réseau de base pour accentuer les caractéristiques pertinentes via une attention spatiale et canalaire. Ensuite, une couche de détection supplémentaire est ajoutée pour mieux capturer les petites cibles, formant une architecture à quatre niveaux d'échelle. Enfin, la structure de fusion de caractéristiques FPN+PANet originale est remplacée par BiFPN (Bidirectional Feature Pyramid Network), qui utilise des connexions sautées et un mécanisme de fusion pondéré pour une meilleure communication entre les caractéristiques de différentes échelles.
Pour le suivi multi-cibles, le modèle de détection amélioré est combiné avec deux algorithmes : DeepSORT et ByteTrack. DeepSORT exploite des caractéristiques d'apparence extraites par un réseau dédié et un filtre de Kalman pour la prédiction de mouvement, tandis que ByteTrack associe toutes les détections, y compris celles à faible confiance, pour améliorer le rappel. Un réseau d'extraction de caractéristiques est entraîné séparément sur des jeux de données adaptés pour les piétons et les véhicules.
Ensembles de données et prétraitement
Les expériences utilisent plusieurs ensembles de données publics et personnalisés. Le dataset SODA10M sert à l'entraînement et à l'évaluation du modèle de détection, contenant des annotations pour diverses catégories comme les voitures, camions, cyclistes et piétons. Pour l'entraînement du réseau d'extraction de caractéristiques de piétons, le dataset Market-1501 est employé. Un ensemble de données personnalisé de suivi de véhicules, obtenu à partir de vidéos de surveillance routière et annoté avec l'outil Darklabel, sert aux tests de suivi.
La préparation des données comprend plusieurs étapes pour améliorer la généralisation : nettoyage pour éliminer les annotations incorrectes, augmentation par retournements aléatoires, redimensionnement, recadrage, ajustements de couleurs et rotations, normalisation des valeurs de pixels, et conversion des annotations au format YOLO (coordonnées du centre, largeur, hauteur, classe).
Évaluation des performances
Les métriques d'évaluation incluent la précision, le rappel, l'AP (Average Precision) et le mAP (mean AP) pour la détection, ainsi que le MOTA (Multiple Object Tracking Accuracy), le MOTP, les faux négatifs, faux positifs et les changements d'ID pour le suivi. Les tests sur des scénarios variés, y compris les conditions de brouillard et de faible luminosité, démontrent une amélioration robuste du modèle amélioré par rapport à la baseline YOLOv5s.
Implémentation des améliorations clés
Voici des extraits de code illustrant les modifications apportées, avec des changements dans la structure, la logique et les noms de variables pour réduire la similarité tout en préservant la fonctionnalité.
Mécanisme d'attention
class AttentionModule(nn.Module):
def __init__(self, in_channels, out_channels, reduction_ratio=8):
super().__init__()
self.channel_avg_pool = nn.AdaptiveAvgPool2d(1)
self.channel_max_pool = nn.AdaptiveMaxPool2d(1)
self.channel_mlp = nn.Sequential(
nn.Conv2d(in_channels, in_channels // reduction_ratio, 1, bias=False),
nn.ReLU(),
nn.Conv2d(in_channels // reduction_ratio, in_channels, 1, bias=False)
)
self.spatial_conv = nn.Conv2d(2, 1, kernel_size=7, padding=3, bias=False)
self.activation = nn.Sigmoid()
def forward(self, input_tensor):
# Attention canalaire
avg_features = self.channel_mlp(self.channel_avg_pool(input_tensor))
max_features = self.channel_mlp(self.channel_max_pool(input_tensor))
channel_weights = self.activation(avg_features + max_features)
refined_tensor = input_tensor * channel_weights
# Attention spatiale
avg_spatial = torch.mean(refined_tensor, dim=1, keepdim=True)
max_spatial, _ = torch.max(refined_tensor, dim=1, keepdim=True)
spatial_features = torch.cat([avg_spatial, max_spatial], dim=1)
spatial_weights = self.activation(self.spatial_conv(spatial_features))
output_tensor = refined_tensor * spatial_weights
return output_tensor
Fusion de caractéristiques BiFPN
class FeatureFusionNetwork(nn.Module):
def __init__(self, input_channels, output_channels, expansion_factor=0.5):
super().__init__()
self.internal_channels = int(input_channels * expansion_factor)
self.input_projection1 = nn.Conv2d(input_channels, self.internal_channels, 1, 1)
self.input_projection2 = nn.Conv2d(input_channels, self.internal_channels, 1, 1)
self.output_projection = nn.Conv2d(self.internal_channels, output_channels, 3, 1)
self.weight1 = nn.Parameter(torch.ones(2), requires_grad=True)
self.weight2 = nn.Parameter(torch.ones(2), requires_grad=True)
self.upsampler = nn.Upsample(scale_factor=2, mode='nearest')
self.downsampler = nn.Conv2d(self.internal_channels, self.internal_channels, 3, 2)
def forward(self, feature_high, feature_low):
projected_high = self.input_projection1(feature_high)
projected_low = self.input_projection2(feature_low)
# Fusion ascendante
normalized_w1 = self.weight1 / (torch.sum(self.weight1) + 1e-8)
fused_up = normalized_w1[0] * projected_low + normalized_w1[1] * self.upsampler(projected_high)
# Fusion descendante
normalized_w2 = self.weight2 / (torch.sum(self.weight2) + 1e-8)
fused_down = normalized_w2[0] * projected_high + normalized_w2[1] * self.downsampler(fused_up)
output_features = self.output_projection(fused_down)
return output_features
Système de suivi
class MultiTargetTracker:
def __init__(self, confidence_threshold=0.3, iou_threshold=0.7, max_age_frames=30, min_hits_for_track=3, feature_budget=100):
self.feature_extractor = AppearanceFeatureExtractor()
self.motion_model = KalmanFilterPredictor()
self.track_manager = TrackManager(self.motion_model, iou_threshold=iou_threshold, max_age=max_age_frames, min_hits=min_hits_for_track, feature_budget=feature_budget)
self.confidence_thresh = confidence_threshold
def process_frame(self, detections_bboxes, detections_scores, detections_classes, frame_image):
valid_indices = [i for i, score in enumerate(detections_scores) if score > self.confidence_thresh]
filtered_bboxes = [detections_bboxes[i] for i in valid_indices]
filtered_scores = [detections_scores[i] for i in valid_indices]
# Extraction des caractéristiques d'apparence
appearance_features = self._extract_appearance(filtered_bboxes, frame_image)
# Mise à jour de l'état du suivi
self.track_manager.predict_motion()
self.track_manager.update_with_detections(filtered_bboxes, filtered_scores, appearance_features)
# Collecte des résultats confirmés
tracking_outputs = []
for active_track in self.track_manager.active_tracks:
if active_track.is_confirmed() and active_track.frames_since_update <= 1:
bbox_coords = active_track.get_tlbr_format()
tracking_outputs.append([bbox_coords[0], bbox_coords[1], bbox_coords[2], bbox_coords[3], active_track.identifier])
return tracking_outputs
def _extract_appearance(self, bboxes_list, image):
features_list = []
for bbox in bboxes_list:
x_center, y_center, width, height = bbox
x1 = max(int(x_center - width / 2), 0)
y1 = max(int(y_center - height / 2), 0)
x2 = min(int(x_center + width / 2), image.shape[1])
y2 = min(int(y_center + height / 2), image.shape[0])
cropped_region = image[y1:y2, x1:x2]
feature_vector = self.feature_extractor(cropped_region)
features_list.append(feature_vector)
return features_list
Contributions et limites
L'amélioration combinée du réseau YOLO, incluant l'attention CBAM, une couche supplémentaire pour les petites cibles et la fusion BiFPN, a conduit à une augmentation significative du mAP sur le dataset SODA10M, tout en maintenant une vitesse acceptable. Les tests de suivi montrent que DeepSORT excelle dans les scénarios piétons tandis que ByteTrack offre de meilleures performances pour les véhicules, soulignant l'importance de choisir l'algorithme adapté au contexte.
Les défis restants incluent l'optimisation des méthodes d'augmentation de données pour éviter la perte d'information, l'exploration d'emplacements optimaux pour les mécanismes d'attention dans le réseau, la compression du modèle pour le déploiement sur appareils embarqués, et l'amélioration de la robustesse dans des conditions météorologiques extrêmes. Des pistes futures pourraient impliquer des techniques de distillation de connaissances ou de pruning pour accélérer l'inférence, ainsi que l'intégration de modules spécialisés pour gérer les scénarios de brouillard ou de faible éclairage.