O Deep Learning está revolucionando a análise de imagens de satélite.
Por muito tempo reservado a grandes laboratórios ou softwares proprietários, hoje ele se abre ao mundo livre graças ao PyTorch e ao QGIS.
Este artigo explora os princípios do Deep Learning aplicado à geomática, a comparação entre os modelos da ESRI e aqueles que podem ser usados no QGIS, e conclui com um caso concreto: a detecção automática de áreas de corais a partir de imagens Sentinel-2.
Introdução ao Deep Learning aplicado à geomática
O Deep Learning (ou aprendizagem profunda) é um ramo da inteligência artificial inspirado no funcionamento do cérebro humano. Baseia-se em redes de neurônios artificiais capazes de aprender a partir de exemplos, sem que lhes sejam explicitamente ditadas todas as regras.
Ao contrário dos métodos tradicionais de classificação — nos quais os indicadores e limites são escolhidos pelo próprio usuário —, o Deep Learning descobre automaticamente as estruturas e os padrões relevantes nos dados.
No campo da geomática, essa abordagem abre perspectivas impressionantes:
- reconhecimento de áreas urbanas, agrícolas ou florestais a partir de imagens de satélite,
- detecção de mudanças ou catástrofes naturais,
- identificação de elementos específicos (estradas, telhados, corais, navios, etc.),
- segmentação precisa de paisagens a partir de imagens Sentinel, PlanetScope ou drone.
O princípio é simples: treina-se um modelo com um grande conjunto de imagens anotadas, até que ele aprenda a reproduzir a tarefa desejada (por exemplo, distinguir água, vegetação e areia). Uma vez treinado, o modelo pode ser aplicado a novas áreas, para gerar automaticamente mapas temáticos de alta resolução.
Por que “profundo”?
O termo “profundo” vem do fato de que essas redes possuem várias camadas sucessivas — às vezes dezenas.
Cada camada aprende a reconhecer padrões cada vez mais complexos:
- as primeiras camadas detectam bordas e texturas,
- as seguintes identificam formas ou estruturas,
- e as últimas compreendem objetos inteiros ou contextos.
É essa hierarquia de representações que dá ao Deep Learning seu poder, mas também seu apetite por dados e poder de computação.
Deep Learning e teledeteção
Na teledeteção, os modelos mais utilizados são os de segmentação de imagens, capazes de atribuir uma classe a cada pixel.
Arquiteturas como U-Net, DeepLab ou Mask R-CNN tornaram-se referências para a cartografia automática a partir de imagens multiespectrais.
Esses modelos são geralmente treinados com frameworks como PyTorch ou TensorFlow e, em seguida, implantados em ambientes GIS.
Os dois grandes mundos se interessaram por isso:
- ESRI, com seu formato de modelo .dlpk (Deep Learning Package) integrado ao ArcGIS Pro e ao ArcGIS Online;
- QGIS, que permite usar modelos PyTorch ou TensorFlow por meio da Caixa de Ferramentas de Processamento ou scripts Python personalizados.
O formato DLPK da ESRI
A ESRI foi uma das primeiras empresas de SIG a integrar o Deep Learning diretamente em seu ecossistema ArcGIS.
Para facilitar a troca e a implantação de modelos, a empresa criou um formato padrão: o DLPK (Deep Learning Package).
Um arquivo .dlpk não é apenas um modelo: é um conjunto completo e portátil, reunindo todos os elementos necessários para sua execução.
Ele geralmente contém:
- O modelo treinado (geralmente no formato PyTorch .pth ou TensorFlow .h5)
- Um arquivo de definição JSON descrevendo a arquitetura do modelo, os parâmetros esperados e os nomes das classes
- Metadados: tipo de entrada (raster, bloco, imagem), tamanho dos patches, número de bandas, normalização, etc.
- Amostras de treinamento (opcionais) para documentar ou retreinar o modelo.
Graças a essa organização, o ArcGIS Pro ou o ArcGIS Online sabem interpretar automaticamente o modelo, sem a necessidade de escrever código.
Ferramentas como “Classify Pixels Using Deep Learning” ou “Detect Objects Using Deep Learning” leem diretamente o .dlpk, carregam o modelo e realizam a inferência em um raster ou conjunto de imagens.
Essa abordagem “chave na mão” apresenta duas vantagens principais:
- Interoperabilidade: um modelo criado em outro lugar pode ser usado por qualquer usuário do ArcGIS, sem dependências complexas.
- Replicabilidade: os metadados garantem que o modelo seja aplicado nas mesmas condições em que foi treinado.
O reverso da moeda, é claro, é o formato fechado: o .dlpk permanece vinculado ao ecossistema ArcGIS e nem sempre é fácil de usar em outros lugares.
O equivalente em código aberto: PyTorch e QGIS
No mundo do código aberto, o QGIS não impõe um formato proprietário.
Os modelos são simplesmente salvos em seu formato nativo (geralmente .pth para PyTorch ou .pt) e executados por meio de scripts Python integrados ao Processing Toolbox.
A ideia é a mesma da ESRI:
carregamos uma imagem multiespectral, aplicamos um modelo treinado e geramos um mapa de classes ou de probabilidade.
Mas, em vez de se basear em um formato empacotado como o .dlpk, o QGIS dá total liberdade ao desenvolvedor:
- O modelo é lido com torch.load().
- As faixas de entrada podem ser selecionadas dinamicamente (por exemplo, B4-B3-B2 para RGB ou B8-B4-B3 em falsa cor).
- O script Python controla todo o fluxo de processamento: normalização, mascaramento da água (NDWI), divisão em blocos, fusão dos resultados, etc.
Essa abordagem permite máxima flexibilidade, particularmente útil para pesquisa ou experimentação.
Por exemplo, um modelo U-Net treinado no PyTorch pode ser aplicado diretamente no QGIS por meio de um script personalizado — sem depender do ArcGIS Pro.
O QGIS se torna então um verdadeiro laboratório de análise espacial com IA, onde o usuário pode:
- testar vários modelos (.pth) da comunidade,
- adaptar os pré-processamentos de acordo com a área (faixa costeira, floresta, urbana…),
- automatizar todo um fluxo por meio de algoritmos de processamento,
- e combinar os resultados com outras camadas GIS (vegetação, batimetria, ocupação do solo…).
Resumo
| Aspecto | ESRI (DLPK) | QGIS (PyTorch) |
|---|---|---|
| Formato | .dlpk (pacote completo) |
.pth ou .pt (somente modelo) |
| Interoperabilidade | Simples, mas proprietária | Livre e modificável |
| Utilização | Ferramentas integradas ArcGIS (“Classify Pixels”, “Detect Objects” | Scripts Processing Python personalizados |
| Personalização | Limitada | Total |
| Curva de aprendizagem | Mais simples para o usuário final | Mais flexível para o desenvolvedor ou pesquisador |
Exemplo prático: segmentação de corais a partir de imagens Sentinel-2 no QGIS
Depois de explorar a lógica dos modelos profundos e os formatos usados pela ESRI e pelo QGIS, vamos passar a um exemplo concreto: a análise de áreas de corais a partir de imagens de satélite Sentinel-2.
O objetivo é distinguir as áreas marinhas (fundos arenosos, ervas marinhas, corais) das áreas terrestres ou turvas, usando um modelo U-Net pré-treinado no PyTorch.
Dados utilizados
A imagem de entrada provém do Sentinel-2, missão do programa europeu Copernicus.
Estas imagens multiespectrais gratuitas oferecem uma resolução de 10 a 20 m e cobrem várias bandas no visível e no infravermelho:s gratuites offrent une résolution de 10 à 20 m et couvrent plusieurs bandes dans le visible et l’infrarouge :
FaixaNomeComprimento de onda (nm)Uso principalB2Azul490Água, turbidezB3Verde560Vegetação, meio marinhoB4Vermelho665Solo, vegetação, coraisB8NIR842Diferenciação terra/águaB11SWIR11610Umidade, areia, nuvens
| Faixa | Nome | Comprimento de onda (nm) | Uso principal |
|---|---|---|---|
| B2 | Azul | 490 | Água, turbidez |
| B3 | Verde | 560 | Vegetação, meio marinho |
| B4 | Vermelho | 665 | Solo, vegetação, corais |
| B8 | NIR | 842 | Diferenciação terra/água |
| B11 | SWIR1 | 1610 | Umidade, areia, nuvens |
A grade é resultado do processamento S2DR3 – Super-resolução Sentinel-2 a 1 m, que permite melhorar a resolução de todas as faixas das imagens Sentinel 2 para atingir cerca de 1 m (ver “Usar S2DR3 no Google Colab para o estudo dos corais nas Ilhas Maurício”). A imagem utilizada é a que serviu de exemplo no artigo citado.
O usuário simplesmente carrega o arquivo .tif multiespectral, por exemplo:
Palmar_MS.tif.
Processamento no QGIS
A análise é realizada por meio de um algoritmo Python integrado na caixa de ferramentas Processing.
Este script carrega um modelo PyTorch (unet_coraux.pth) e aplica a segmentação à imagem em várias etapas:
- Leitura da trama e normalização das bandas.
Os valores são reduzidos a uma escala compatível com o treinamento do modelo. - Mascaramento opcional das áreas terrestres.
Um índice NDWI (Normalized Difference Water Index) é calculado para isolar o mar.
Os pixels com um valor NDWI alto são considerados marinhos e são processados pelo modelo; os demais são mascarados. - Divisão em blocos (patches).
A imagem é processada em partes para evitar sobrecarga de memória.
Cada bloco é analisado de forma independente e, em seguida, os resultados são combinados. - Aplicação do modelo U-Net
O modelo realiza uma segmentação pixel a pixel: atribui a cada pixel uma probabilidade de pertencer à classe “coral” ou “não coral”.
O resultado é uma trama de saída que contém os valores de probabilidade ou de classe. - Salvar a trama de saída
O resultado é salvo no formato GeoTIFF (palmar_model_9.tif), pronto para ser sobreposto a outras camadas SIG.
Modo de funcionamento
Operações prévias
Vamos criar um script de processamento QGis. Como pré-requisito, é necessário instalar
python -m pip install torchgeo
python -m pip install torch torchvision
python -m pip install segmentation-models-pytorch
Em seguida, devemos baixar o modelo desejado. Para isso, na console python do QGis, insira o seguinte script:
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")
Neste exemplo, salvamos os modelos baixados em um diretório c:/models
Configuração do script de processamento
Em QGIS → Caixa de ferramentas de processamento → Scripts → Novo script,
# -*- 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()
Em um próximo artigo, você encontrará explicações sobre as diferentes partes do código.
O script QGIS apresentado aqui permite aplicar modelos de segmentação de imagens baseados em U‑Net salvos no formato PyTorch (.pth). Ele foi projetado para processar imagens multiespectrais, como as provenientes dos satélites Sentinel‑2, e adapta automaticamente as faixas utilizadas de acordo com o número de canais de entrada esperado pelo modelo. Por exemplo, se o modelo for treinado em imagens RGB, o script selecionará as bandas Vermelha, Verde e Azul; se o modelo esperar mais canais, ele oferecerá uma seleção manual ou utilizará todas as bandas disponíveis. O código também integra a possibilidade de ocultar áreas terrestres através do cálculo do NDWI, o que permite concentrar a segmentação em áreas aquáticas, por exemplo, para identificar corais. Na prática, qualquer modelo U-Net PyTorch corretamente salvo pode ser carregado e aplicado, desde que a arquitetura e os pesos estejam incluídos no arquivo .pth.
Utilização
Execute o script. A seguinte janela será aberta:

A seleção manual das faixas só é utilizada se o script não conseguir determinar, a partir do modelo, quais as faixas a utilizar.
Interpretação do resultado
O raster resultante do modelo representa um mapa de probabilidade:
os valores próximos de 1 indicam uma forte presença de corais, enquanto os valores próximos de 0 correspondem a áreas sem corais (areia, algas, profundidade, etc.).
Uma simbologia adequada (do azul claro ao vermelho) permite visualizar facilmente a distribuição espacial das áreas prováveis de corais.
Ao combinar este mapa com outros dados (batimetria, substrato, turbidez), torna-se possível estimar a vulnerabilidade ou a degradação dos recifes ao longo do tempo.

Vantagens desta abordagem
A integração do PyTorch no QGIS abre novas perspetivas para a cartografia ambiental assistida por inteligência artificial:
- Código aberto e reproduzível: todo o processo pode ser compartilhado, modificado ou adaptado a outras zonas costeiras.
- Autonomia local: não há necessidade do ArcGIS Pro nem de licenças caras para testar ou aplicar modelos de Deep Learning.
- Experimentação flexível: é possível testar outras arquiteturas (SegNet, DeepLabV3, etc.) ou adaptar o pré-processamento às especificidades de cada zona.
Rumo a um ecossistema de modelos abertos
A longo prazo, poderíamos imaginar uma biblioteca compartilhada de modelos ambientais de código aberto — o equivalente livre do formato DLPK — onde cada .pth seria acompanhado por seu arquivo de descrição (faixas, normalização, classes).
O QGIS poderia então oferecer uma interface para importar, testar e documentar esses modelos, facilitando sua reutilização em contextos tropicais, litorâneos ou florestais.
Conclusão
O crescimento do Deep Learning marca uma nova etapa na evolução da geomática.
Enquanto os tratamentos de imagens clássicos se baseavam em limites e índices espectrais, os modelos neuronais agora aprendem a reconhecer formas, texturas e assinaturas complexas diretamente nos pixels.
Graças a ferramentas como o ArcGIS Pro (com seus DLPK) e o QGIS (via PyTorch e scripts personalizados), esse poder se torna acessível a todos: pesquisadores, técnicos ou entusiastas da cartografia ambiental.
O exemplo apresentado aqui — a segmentação de corais a partir de imagens Sentinel-2 — ilustra o potencial dessas abordagens para a análise detalhada de ambientes costeiros e a preservação de ecossistemas marinhos.
O desafio não é mais apenas técnico, mas também coletivo: compartilhar modelos, documentar suas entradas, compartilhar métodos e tornar o Deep Learning mais transparente, reproduzível e aberto.
O futuro da teledeteção provavelmente será construído na interseção desses dois mundos — engenharia de software e conhecimento do terreno — para transformar os dados de satélite em verdadeiros indicadores do estado ecológico.