Le Deep Learning révolutionne l’analyse d’images satellitaires.
Longtemps réservé aux grands laboratoires ou aux logiciels propriétaires, il s’ouvre aujourd’hui au monde libre grâce à PyTorch et QGIS.
Cet article explore les principes du Deep Learning appliqué à la géomatique, la comparaison entre les modèles d’ESRI et ceux exploitables dans QGIS, et se conclut par un cas concret : la détection automatique des zones coralliennes à partir d’images Sentinel-2.
Introduction au Deep Learning appliqué à la géomatique
Le Deep Learning (ou apprentissage profond) est une branche de l’intelligence artificielle inspirée du fonctionnement du cerveau humain. Il repose sur des réseaux de neurones artificiels capables d’apprendre à partir d’exemples, sans qu’on leur dicte explicitement toutes les règles.
Contrairement aux méthodes de classification traditionnelles — où l’on choisit soi-même les indicateurs et seuils — le Deep Learning découvre automatiquement les structures et motifs pertinents dans les données.
Dans le domaine de la géomatique, cette approche ouvre des perspectives impressionnantes :
- reconnaissance des zones urbaines, agricoles ou forestières à partir d’images satellites,
- détection de changements ou de catastrophes naturelles,
- identification d’éléments précis (routes, toits, coraux, navires, etc.),
- segmentation fine des paysages à partir d’images Sentinel, PlanetScope ou drone.
Le principe est simple : on entraîne un modèle sur un grand jeu d’images annotées, jusqu’à ce qu’il apprenne à reproduire la tâche souhaitée (par exemple, distinguer l’eau, la végétation et le sable). Une fois entraîné, le modèle peut être appliqué sur de nouvelles zones, pour générer automatiquement des cartes thématiques à haute résolution.
Pourquoi « profond » ?
Le terme « deep » vient du fait que ces réseaux possèdent de nombreuses couches successives — parfois des dizaines.
Chaque couche apprend à reconnaître des motifs de plus en plus complexes :
- les premières couches détectent des bords et textures,
- les suivantes identifient des formes ou structures,
- et les dernières comprennent des objets entiers ou contextes.
C’est cette hiérarchie de représentations qui donne au Deep Learning sa puissance, mais aussi son appétit en données et en puissance de calcul.
Deep Learning et télédétection
Dans la télédétection, les modèles les plus utilisés sont ceux de segmentation d’images, capables d’attribuer une classe à chaque pixel.
Des architectures comme U-Net, DeepLab ou Mask R-CNN sont devenues des références pour la cartographie automatique à partir d’imagerie multispectrale.
Ces modèles sont généralement entraînés avec des frameworks tels que PyTorch ou TensorFlow, puis déployés dans les environnements SIG.
Les deux grands mondes s’y sont intéressés :
- ESRI, avec son format de modèle .dlpk (Deep Learning Package) intégré à ArcGIS Pro et ArcGIS Online ;
- QGIS, qui permet d’utiliser des modèles PyTorch ou TensorFlow via le Processing Toolbox ou des scripts Python personnalisés.
Le format DLPK d’ESRI
ESRI a été l’un des premiers acteurs SIG à intégrer le Deep Learning directement dans son écosystème ArcGIS.
Pour faciliter l’échange et le déploiement de modèles, la société a créé un format standard : le DLPK (Deep Learning Package).
Un fichier .dlpk n’est pas seulement un modèle : c’est un ensemble complet et portable, regroupant tous les éléments nécessaires à son exécution.
Il contient généralement :
- Le modèle entraîné (souvent au format PyTorch
.pthou TensorFlow.h5) - Un fichier de définition JSON décrivant l’architecture du modèle, les paramètres attendus et les noms des classes
- Des métadonnées : type d’entrée (raster, tuile, image), taille des patches, nombre de bandes, normalisation, etc.
- Des échantillons d’entraînement (optionnels) pour documenter ou réentraîner le modèle.
Grâce à cette organisation, ArcGIS Pro ou ArcGIS Online savent automatiquement interpréter le modèle, sans qu’il soit nécessaire d’écrire du code.
Les outils comme “Classify Pixels Using Deep Learning” ou “Detect Objects Using Deep Learning” lisent directement le .dlpk, chargent le modèle, et effectuent l’inférence sur un raster ou un jeu d’images.
Cette approche « clé en main » présente deux avantages majeurs :
- Interopérabilité : un modèle formé ailleurs peut être utilisé par tout utilisateur ArcGIS, sans dépendances complexes.
- Réplicabilité : les métadonnées assurent que le modèle est appliqué dans les mêmes conditions que lors de son entraînement.
Le revers de la médaille, bien sûr, est la fermeture du format : le .dlpk reste lié à l’écosystème ArcGIS, et n’est pas toujours simple à exploiter ailleurs.
Le pendant open source : PyTorch et QGIS
Du côté du monde libre, QGIS n’impose pas de format propriétaire.
Les modèles sont simplement sauvegardés sous leur format natif (souvent .pth pour PyTorch ou .pt), et exécutés via des scripts Python intégrés au Processing Toolbox.
L’idée est la même que chez ESRI :
on charge une image multispectrale, on applique un modèle entraîné, et on génère une carte de classes ou de probabilité.
Mais au lieu de s’appuyer sur un format packagé comme le .dlpk, QGIS laisse la liberté totale au développeur :
- Le modèle est lu avec
torch.load(). - Les bandes d’entrée peuvent être sélectionnées dynamiquement (par exemple B4-B3-B2 pour le RGB, ou B8-B4-B3 en fausse couleur).
- Le script Python contrôle tout le flux de traitement : normalisation, masquage de l’eau (NDWI), découpage en blocs, fusion des résultats, etc.
Cette approche permet une souplesse maximale, particulièrement utile pour la recherche ou l’expérimentation.
Par exemple, un modèle U-Net entraîné sous PyTorch peut être appliqué directement dans QGIS via un script personnalisé — sans dépendre d’ArcGIS Pro.
QGIS devient alors un véritable laboratoire d’analyse spatiale avec IA, où l’utilisateur peut :
- tester plusieurs modèles (
.pth) issus de la communauté, - adapter les prétraitements selon la zone (bande côtière, forêt, urbain…),
- automatiser tout un flux via les algorithmes Processing,
- et combiner les résultats avec d’autres couches SIG (végétation, bathymétrie, occupation du sol…).
En résumé
| Aspect | ESRI (DLPK) | QGIS (PyTorch) |
|---|---|---|
| Format | .dlpk (package complet) |
.pth ou .pt (modèle seul) |
| Interopérabilité | Simple, mais propriétaire | Libre et modifiable |
| Utilisation | Outils intégrés ArcGIS (“Classify Pixels”, “Detect Objects”) | Scripts Processing Python personnalisés |
| Personnalisation | Limitée | Totale |
| Courbe d’apprentissage | Plus simple pour l’utilisateur final | Plus flexible pour le développeur ou chercheur |
Exemple pratique : segmentation des coraux à partir d’images Sentinel-2 dans QGIS
Après avoir exploré la logique des modèles profonds et les formats utilisés par ESRI et QGIS, passons à un exemple concret : l’analyse des zones coralliennes à partir d’images satellites Sentinel-2.
L’objectif est de distinguer les zones marines (fonds sableux, herbiers, coraux) des zones terrestres ou turbidées, grâce à un modèle U-Net pré-entraîné sous PyTorch.
Données utilisées
L’image d’entrée provient de Sentinel-2, mission du programme européen Copernicus.
Ces images multispectrales gratuites offrent une résolution de 10 à 20 m et couvrent plusieurs bandes dans le visible et l’infrarouge :
| Bande | Nom | Longueur d’onde (nm) | Usage principal |
|---|---|---|---|
| B2 | Bleu | 490 | Eau, turbidité |
| B3 | Vert | 560 | Végétation, milieu marin |
| B4 | Rouge | 665 | Sol, végétation, coraux |
| B8 | NIR | 842 | Différenciation terre/eau |
| B11 | SWIR1 | 1610 | Humidité, sable, nuages |
Le raster est issu du traitement S2DR3 – Super-résolution Sentinel-2 à 1 m qui permet d’améliorer la résolution de toutes les bandes des images Sentinel 2 pour atteindre eniron 1m ( Voir « Utiliser S2DR3 dans Google Colab pour l’étude des coraux à Maurice« ). L’image utilisée est celle qui a servi comme exemple dans l’article cité.
L’utilisateur charge simplement le fichier .tif multispectral, par exemple :Palmar_MS.tif.
Traitement dans QGIS
L’analyse s’effectue grâce à un algorithme Python intégré dans la boîte à outils Processing.
Ce script charge un modèle PyTorch (unet_coraux.pth) et applique la segmentation sur l’image en plusieurs étapes :
- Lecture du raster et normalisation des bandes
Les valeurs sont ramenées à une échelle compatible avec l’entraînement du modèle. - Masquage optionnel des zones terrestres
Un indice NDWI (Normalized Difference Water Index) est calculé pour isoler la mer.
Les pixels à forte valeur NDWI sont considérés comme marins et traités par le modèle ; les autres sont masqués. - Découpage en blocs (patchs)
L’image est traitée par portions pour éviter la surcharge mémoire.
Chaque bloc est analysé indépendamment, puis les résultats sont fusionnés. - Application du modèle U-Net
Le modèle effectue une segmentation pixel par pixel : il attribue à chaque pixel une probabilité d’appartenir à la classe “corail” ou “non-corail”.
Le résultat est un raster de sortie contenant les valeurs de probabilité ou de classe. - Sauvegarde du raster de sortie
Le résultat est enregistré au format GeoTIFF (palmar_model_9.tif), prêt à être superposé avec d’autres couches SIG.
Mode opératoire
Opérations préalables
Nous allons créer un script de traitement QGis. Comme préalable il faut installer
python -m pip install torchgeo
python -m pip install torch torchvision
python -m pip install segmentation-models-pytorch
Puis nous devons télécharger le modèle souhaité. Pour cela, dans la console python de QGis entrez le script suivant:
import segmentation_models_pytorch as smp
import torch
# U-Net RGB pré-entraîné
model = smp.Unet(
encoder_name="resnet34",
encoder_weights="imagenet",
in_channels=3,
classes=1 # corail/non-corail
)
# Sauvegarde pour QGIS
torch.save(model, "c:/models/unet_coraux.pth")
Dans cet exemple on sauvegarde les modèles téléchargés dans un répertoire c:/models
Mise en place du script de traitement
Dans QGIS → Boîte à outils de traitement → Scripts → Nouveau script, collez :
# -*- coding: utf-8 -*-
"""
Algorithme QGIS : Segmentation Coraux (U-Net) avec option masquage terrestre
Compatible QGIS 3.44+
"""
from qgis.core import (
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingParameterRasterLayer,
QgsProcessingParameterFile,
QgsProcessingParameterEnum,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterBoolean,
)
from qgis.PyQt.QtCore import QCoreApplication
import torch
import torch.serialization
import numpy as np
from osgeo import gdal
from scipy.signal import windows
class SegmentationCoraux(QgsProcessingAlgorithm):
"""Segmentation des images Sentinel-2 à 1m avec un modèle U-Net"""
BAND_OPTIONS = ["RGB (4-3-2)", "Fausse couleur (8-4-3)"]
def initAlgorithm(self, config=None):
self.addParameter(
QgsProcessingParameterRasterLayer(
"raster_input",
self.tr("Image Sentinel-2 (GeoTIFF multibandes)")
)
)
self.addParameter(
QgsProcessingParameterEnum(
"band_set",
self.tr("Sélection manuelle des bandes"),
options=self.BAND_OPTIONS,
defaultValue=0
)
)
self.addParameter(
QgsProcessingParameterFile(
"model_path",
self.tr("Modèle PyTorch (.pth)"),
extension="pth"
)
)
self.addParameter(
QgsProcessingParameterRasterDestination(
"output_raster",
self.tr("Raster de sortie (masque)")
)
)
# --- Nouvelle option : masquage terrestre ---
self.addParameter(
QgsProcessingParameterBoolean(
"mask_land",
self.tr("Masquer les zones terrestres (NDWI)"),
defaultValue=True
)
)
def processAlgorithm(self, parameters, context, feedback):
model_path = self.parameterAsFile(parameters, "model_path", context)
mask_land = self.parameterAsBool(parameters, "mask_land", context)
band_set = self.parameterAsEnum(parameters, "band_set", context)
feedback.pushInfo("Chargement du modèle PyTorch...")
try:
from segmentation_models_pytorch.decoders.unet.model import Unet
torch.serialization.add_safe_globals([Unet])
except Exception as e:
feedback.pushInfo(f"Avertissement : impossible d’ajouter Unet aux classes sûres : {e}")
model = torch.load(model_path, map_location=torch.device('cpu'), weights_only=False)
model.eval()
raster_input = self.parameterAsRasterLayer(parameters, "raster_input", context)
ds = gdal.Open(raster_input.source())
feedback.pushInfo(f"Ouverture du raster : {raster_input.source()}")
nrows, ncols = ds.RasterYSize, ds.RasterXSize
# --- Détecter automatiquement les canaux d'entrée ---
conv1_weights = model.encoder.conv1.weight.data
n_channels = conv1_weights.shape[1]
feedback.pushInfo(f"Le modèle attend {n_channels} canaux d'entrée.")
if n_channels == 3:
# Analyser l’importance des canaux
channel_mean = conv1_weights.abs().mean(dim=(0, 2, 3)).numpy()
sorted_idx = list(np.argsort(-channel_mean))
feedback.pushInfo(f"Classement des canaux par importance : {sorted_idx}")
band_selection = [4, 3, 2] # RGB classique
feedback.pushInfo(f"Utilisation automatique des bandes : {band_selection} (RGB)")
else:
band_selection = [4, 3, 2] if band_set == 0 else [8, 4, 3]
feedback.pushInfo(f"Utilisation manuelle des bandes : {band_selection}")
# --- Lecture des bandes sélectionnées ---
img_list = []
for b in band_selection:
band = ds.GetRasterBand(b)
arr = band.ReadAsArray().astype(np.float32)
img_list.append(arr)
img = np.stack(img_list, axis=0)
# Normalisation
feedback.pushInfo("Normalisation des valeurs...")
img = (img - img.min()) / (img.max() - img.min() + 1e-6)
# --- Calcul NDWI si option activée ---
if mask_land and ds.RasterCount >= 8:
try:
B3 = ds.GetRasterBand(3).ReadAsArray().astype(np.float32)
B8 = ds.GetRasterBand(8).ReadAsArray().astype(np.float32)
ndwi = (B3 - B8) / (B3 + B8 + 1e-6)
water_mask = ndwi > 0
feedback.pushInfo("NDWI calculé : les zones terrestres seront exclues.")
except Exception as e:
water_mask = np.ones((nrows, ncols), dtype=bool)
feedback.pushInfo(f"NDWI non calculable ({e}), toutes les zones seront traitées.")
else:
water_mask = np.ones((nrows, ncols), dtype=bool)
feedback.pushInfo("Masquage terrestre désactivé.")
# --- Traitement par blocs ---
block_size = 2048
overlap = 512
output_mask = np.zeros((nrows, ncols), dtype=np.float32)
weight = np.zeros((nrows, ncols), dtype=np.float32)
total_blocks = ((nrows // (block_size - overlap) + 1) *
(ncols // (block_size - overlap) + 1))
done = 0
feedback.pushInfo("Début du traitement par blocs...")
for y in range(0, nrows, block_size - overlap):
for x in range(0, ncols, block_size - overlap):
if feedback.isCanceled():
break
block = img[:, y:y+block_size, x:x+block_size]
if block.size == 0:
continue
mask_block_water = water_mask[y:y+block_size, x:x+block_size]
if not mask_block_water.any():
continue
# ======= BLOC ORIGINAL PRÉSERVÉ =======
with torch.no_grad():
block_tensor = torch.from_numpy(block).unsqueeze(0)
pred = model(block_tensor)
mask_block = pred.squeeze().numpy()
h, w = mask_block.shape
mask_block *= mask_block_water[:h, :w]
# Fenêtre Hanning
win_y = windows.hann(h)[:, None]
win_x = windows.hann(w)[None, :]
weight_block = win_y * win_x
output_mask[y:y+h, x:x+w] += mask_block * weight_block
weight[y:y+h, x:x+w] += weight_block
done += 1
progress = int(100 * done / total_blocks)
feedback.setProgress(progress)
# ======= FIN DU BLOC ORIGINAL =======
# Fusion finale
output_mask /= np.maximum(weight, 1e-6)
output_mask = np.clip(output_mask, 0, 1)
# --- Sauvegarde GeoTIFF ---
feedback.pushInfo("Sauvegarde du masque final...")
driver = gdal.GetDriverByName("GTiff")
out_path = self.parameterAsOutputLayer(parameters, "output_raster", context)
out_ds = driver.Create(out_path, ncols, nrows, 1, gdal.GDT_Float32)
out_ds.SetGeoTransform(ds.GetGeoTransform())
out_ds.SetProjection(ds.GetProjection())
out_ds.GetRasterBand(1).WriteArray(output_mask)
out_ds.FlushCache()
feedback.pushInfo("✅ Segmentation terminée avec succès.")
return {"output_raster": out_path}
# --- Métadonnées ---
def name(self):
return "segmentation_coraux"
def displayName(self):
return self.tr("Segmentation des images Sentinel 2 à 1m (U-Net)")
def group(self):
return self.tr("Deep Learning")
def groupId(self):
return "deeplearning"
def tr(self, string):
return QCoreApplication.translate("SegmentationCoraux", string)
def createInstance(self):
return SegmentationCoraux()
Vous trouverez dans un prochain article les explications des différentes parties du code.
Le script QGIS présenté ici permet d’appliquer des modèles de segmentation d’images basés sur U‑Net sauvegardés au format PyTorch (.pth). Il est conçu pour traiter des images multispectrales, comme celles issues des satellites Sentinel‑2, et adapte automatiquement les bandes utilisées selon le nombre de canaux d’entrée attendu par le modèle. Par exemple, si le modèle est entraîné sur des images RGB, le script sélectionnera les bandes Rouge, Vert et Bleu ; si le modèle attend plus de canaux, il propose une sélection manuelle ou utilise toutes les bandes disponibles. Le code intègre également la possibilité de masquer les zones terrestres grâce au calcul du NDWI, ce qui permet de concentrer la segmentation sur les zones d’eau, par exemple pour identifier les coraux. En pratique, tout modèle U‑Net PyTorch correctement sauvegardé peut être chargé et appliqué, à condition que l’architecture et les poids soient inclus dans le fichier .pth.
Utilisation
Exécutez le script. La fenêtre suivante s’ouvre:

La sélection manuelle des bandes n’est utilisée que si le script n’arrive pas à déterminer, à partir du modèle, quelles sont les bandes à utiliser.
Interprétation du résultat
Le raster issu du modèle représente une carte de probabilité :
les valeurs proches de 1 indiquent une forte présence de coraux, tandis que celles proches de 0 correspondent à des zones non coralliennes (sable, algues, profondeur, etc.).
Une symbologie adaptée (du bleu clair au rouge) permet de visualiser facilement la répartition spatiale des zones coralliennes probables.
En combinant cette carte avec d’autres données (bathymétrie, substrat, turbidité), il devient possible d’estimer la vulnérabilité ou la dégradation des récifs au fil du temps.

Avantages de cette approche
L’intégration de PyTorch dans QGIS ouvre de nouvelles perspectives pour la cartographie environnementale assistée par intelligence artificielle :
- Open source et reproductible : tout le processus peut être partagé, modifié ou adapté à d’autres zones côtières.
- Autonomie locale : pas besoin d’ArcGIS Pro ni de licence coûteuse pour tester ou appliquer des modèles de Deep Learning.
- Expérimentation souple : on peut tester d’autres architectures (SegNet, DeepLabV3, etc.) ou adapter le prétraitement aux spécificités de chaque zone.
Vers un écosystème de modèles ouverts
À terme, on pourrait imaginer une bibliothèque partagée de modèles environnementaux open source — l’équivalent libre du format DLPK — où chaque .pth serait accompagné de son fichier de description (bandes, normalisation, classes).
QGIS pourrait alors proposer une interface pour importer, tester et documenter ces modèles, facilitant leur réutilisation dans les contextes tropicaux, littoraux ou forestiers.
Conclusion
L’essor du Deep Learning marque une nouvelle étape dans l’évolution de la géomatique.
Alors que les traitements d’images classiques reposaient sur des seuils et des indices spectrales, les modèles neuronaux apprennent désormais à reconnaître les formes, les textures et les signatures complexes directement dans les pixels.
Grâce à des outils comme ArcGIS Pro (avec ses DLPK) et QGIS (via PyTorch et des scripts personnalisés), cette puissance devient accessible à tous : chercheurs, techniciens, ou passionnés de cartographie environnementale.
L’exemple présenté ici — la segmentation des coraux à partir d’images Sentinel-2 — illustre le potentiel de ces approches pour l’analyse fine des milieux côtiers et la préservation des écosystèmes marins.
L’enjeu n’est plus seulement technique, mais aussi collectif : mutualiser les modèles, documenter leurs entrées, partager les méthodes et rendre le Deep Learning plus transparent, reproductible et ouvert.
Le futur de la télédétection se construira probablement à la croisée de ces deux mondes — l’ingénierie logicielle et la connaissance du terrain — pour transformer les données satellitaires en véritables indicateurs d’état écologique.