Uma pessoa vista de cima, em uma imagem térmica e parcialmente coberta pela vegetação, não se parece com a pessoa que um detector generalista aprendeu a reconhecer. A classe continua sendo person. O padrão visual não. Sensor, perspectiva, escala e pose mudam ao mesmo tempo, e o modelo responde com o vocabulário que tem: uma assinatura quente vira broccoli, um corpo alongado vira airplane, ou simplesmente nada é detectado.
Esse descompasso não é um detalhe de implementação. É o problema central de especializar um detector a um domínio operacional. Eu vi essa diferença na prática muito antes de treinar redes: fui piloto da Força Aérea Brasileira durante 16 anos, trabalhei em busca e salvamento na Amazônia e atuei com ciência de dados e planejamento de missões no Comando de Operações Aeroespaciais. Em uma busca, o tempo avança, a área se expande e a qualidade da percepção decide o que a equipe consegue priorizar.
Neste artigo, você vai acompanhar um projeto completo de YOLO para detecção de objetos em imagens térmicas: partir de um YOLOv8s treinado no COCO, medir o que ele faz no conjunto POP (Partially Occluded Person), adaptar os pesos ao domínio e inspecionar o resultado. O notebook público reúne treinamento e inferência. Os números e as figuras abaixo saem dessa execução.
Se você quer percorrer esse tipo de pipeline em projetos com dados aéreos reais, o Bootcamp de Visão Computacional Aplicada a Drones é uma especialização prática, hands-on, em Data Science e Inteligência Artificial aplicada a drones, com modelos no estado da arte.
Código do Artigo
Acesse o código-fonte deste artigo gratuitamente.
Informe seu email para acessar o código:
✓ Seu código está pronto!
Abrir no Google Colab →Por que o YOLO para detecção de objetos em imagens térmicas precisa de adaptação?
Um modelo como YOLO, treinado em conjuntos enormes de fotografias comuns, é um excelente ponto de partida. Ele já viu a classe person em contextos frequentes: perspectiva horizontal, corpo em proporções familiares, textura de roupa, objetos relativamente grandes no quadro e cenas urbanas ou cotidianas.
Uma missão de busca com drone inverte quase todas essas condições. A câmera olha do nadir. O sensor registra radiação térmica, não luz refletida. A pessoa ocupa uma fração pequena do frame. A pose pode ser deitada. Parte do corpo desaparece sob a copa. Para nós, continua sendo uma pessoa. Para o modelo, é um padrão que ele talvez nunca tenha associado a essa classe.
Por isso, a primeira pergunta não é “qual versão da YOLO eu baixo?”. É: o dado de treinamento representa a aparência da missão? A resolução preserva o alvo? As divisões evitam vazamento entre cenas do mesmo voo? Esse raciocínio é o mesmo que aparece quando você treina YOLO em um conjunto personalizado, só que aqui a mudança de domínio é mais brusca do que trocar de objeto em fotografias convencionais.
O que o conjunto POP representa?
O POP foi publicado como conjunto de dados de imagens infravermelhas para detecção de pessoas parcialmente ocluídas em ambientes complexos, com aplicação explícita em busca e salvamento. A coleta usou um DJI Matrice 30T, com gimbal no nadir, em alturas próximas de 30, 50 e 70 metros.
No arquivo oficial que o notebook baixa e verifica por SHA-256, as divisões ficam assim:
def load_coco(split):
path = POP_ROOT / split / "Annotations" / f"{split}.json"
return json.loads(path.read_text(encoding="utf8"))
summary_rows = []
for split in ("train", "val", "test"):
coco = load_coco(split)
summary_rows.append({
"split": split,
"images": len(coco["images"]),
"boxes": len(coco["annotations"]),
})
pd.DataFrame(summary_rows)
split images boxes 0 train 5548 18425 1 val 2320 6546 2 test 900 1840
São 8.768 imagens e 26.811 caixas. O ponto metodológico mais importante não é o volume: as divisões são organizadas por cena. Frames vizinhos de um mesmo trecho de voo podem ser quase idênticos. Se você embaralha arquivos ao acaso, parte do teste mede memória visual, não generalização.
Antes de qualquer treinamento, o notebook fixa alguns casos do conjunto de teste e desenha as anotações de referência. Essa ordem importa. Os exemplos não foram escolhidos depois de ver onde o modelo adaptado ficou mais convincente.

Casos fixados no conjunto de teste. Observe a escala dos alvos, as poses e a oclusão pela vegetação.
O ponto de partida funciona no domínio original?
Antes de concluir que o detector “não serve” para o termal, confirmamos que ele reconhece pessoas no tipo de imagem em que foi treinado. Carregamos o YOLOv8s com pesos do COCO e aplicamos a fotografia clássica do ônibus da Ultralytics, na mesma resolução que usaremos depois (imgsz=1280).
coco_model = YOLO("yolov8s.pt")
bus_result = coco_model.predict(
source=str(BUS_PATH),
imgsz=1280,
conf=0.25,
verbose=False,
)[0]

O modelo localiza as pessoas com confiança alta. O baseline está intacto: a arquitetura e os pesos fazem o que se espera no domínio original. O problema ainda não apareceu.
A mesma classe, outra distribuição
Agora mudamos tudo de uma vez e mantemos o mesmo detector, a mesma resolução e o mesmo limiar de confiança.
coco_demo_results = coco_model.predict(
source=[str(path) for path in DEMO_IMAGES],
imgsz=1280,
conf=0.25,
verbose=False,
)
for result, path in zip(coco_demo_results, DEMO_IMAGES):
classes = [result.names[int(class_id)] for class_id in result.boxes.cls]
print(path.name, classes if classes else "nenhuma detecção")
T1_0000_30.291.JPG ['broccoli'] T2_0000_50.15.JPG ['airplane'] T1_0076_29.613.JPG ['airplane', 'kite'] T1_0229_50.226.JPG nenhuma detecção T2_0065_50.496.JPG ['airplane', 'airplane'] T2_0194_47.334.JPG nenhuma detecção

Nenhuma das seis cenas produz person. O modelo não está “quebrado”. Ele está encaixando uma imagem fora da sua distribuição nas categorias que conhece. Uma mancha clara e compacta se aproxima de broccoli. Um corpo alongado, visto de cima, se aproxima de airplane. Em outros casos, o escore não atravessa o limiar.
Essa é a definição prática de mudança de domínio. Trocar o nome da classe no vocabulário humano não altera o espaço visual em que o detector foi treinado.
A tentação seguinte é atualizar a geração do modelo. O notebook repete as mesmas seis cenas com YOLO11s, também treinado no COCO. Nas imagens inspecionadas, a incompatibilidade permanece: a geração mais recente não remove, por si só, a distância entre fotografias terrestres e imagens térmicas aéreas.
Se você já estuda detecção de objetos, vale contrastar este resultado com a introdução ao YOLOv9: o avanço de arquitetura importa, mas não substitui dados que representem a missão. O mesmo princípio atravessa o artigo sobre Data Science para Drones, em que a modalidade e a escala do alvo definem o problema muito antes da escolha da rede.
Essa é também a diferença entre usar um modelo pronto e construir um projeto. No Bootcamp de Visão Computacional Aplicada a Drones, o caso térmico é um dos projetos da especialização. O percurso completo avança para a fusão entre o óptico e o termal, quando um único canal deixa evidência incompleta.
Converter, validar e medir a escala
O POP entrega anotações em COCO JSON. O treinamento com Ultralytics espera labels no formato YOLO: classe e caixa normalizada pelo centro, largura e altura. Uma conversão descuidada troca coordenadas, classes ou escalas e destrói o treinamento de forma silenciosa.
def yolo_line(annotation, image_width, image_height):
x, y, width, height = annotation["bbox"]
center_x = (x + width / 2) / image_width
center_y = (y + height / 2) / image_height
norm_width = width / image_width
norm_height = height / image_height
values = [center_x, center_y, norm_width, norm_height]
assert all(0 <= value <= 1 for value in values)
return "0 " + " ".join(f"{value:.6f}" for value in values)
Depois da conversão, o notebook confere as três divisões (5.548 / 2.320 / 900 imagens) e verifica as 26.811 coordenadas no intervalo [0, 1]. Só então olhamos a escala dos alvos.
boxes[["width_px", "height_px", "relative_area_pct"]].describe(
percentiles=[0.25, 0.5, 0.75, 0.9]
)
width_px height_px relative_area_pct count 26811.000000 26811.000000 26811.000000 mean 42.303085 49.242662 0.180428 50% 38.000000 44.000000 0.125351 90% 71.000000 86.000000 0.434494
A mediana é uma caixa de 38 por 44 pixels, ocupando cerca de 0,13% da imagem. Mesmo no percentil 90, a área relativa permanece abaixo de 0,5%. Se a resolução de entrada for reduzida por hábito, a informação do alvo desaparece antes de o detector começar a trabalhar.

A distribuição da altura justifica imgsz=1280: a resolução precisa conversar com o tamanho do objeto.
Transfer learning com hipóteses explícitas
A adaptação mantém a família YOLOv8s. Assim, o ganho observado vem dos dados, não de uma troca de arquitetura. Partimos dos pesos do COCO, elevamos a resolução, limitamos o número de épocas e interrompemos quando a validação deixa de melhorar.
Em imagens térmicas, também desativamos a variação de matiz e saturação. Esses augmentations fazem sentido em RGB. Aqui, eles alterariam um sinal que não está organizado como cor.
EPOCHS = 100
PATIENCE = 20
IMGSZ = 1280
BATCH = 8
model = YOLO("yolov8s.pt")
train_results = model.train(
data=str(DATA_YAML),
epochs=EPOCHS,
patience=PATIENCE,
imgsz=IMGSZ,
batch=BATCH,
optimizer="SGD",
lr0=0.001,
pretrained=True,
hsv_h=0.0,
hsv_s=0.0,
save=True,
save_period=5,
)
A execução desta configuração parou por early stopping na época 49. A melhor época pela métrica principal de validação foi a 29. O last.pt e o best.pt não são sinônimos: um guarda o estado mais recente, o outro guarda o estado que deve seguir para avaliação e inferência.
print("Última epoch registrada:", int(history.tail(1).iloc[0]["epoch"]))
print("Melhor epoch pela métrica principal:", int(history.loc[best_index, "epoch"]))
Última epoch registrada: 49 Melhor epoch pela métrica principal: 29

A melhor época ocorre antes da última registrada. Treinar é acompanhar o processo e escolher o estado que merece ser levado adiante.
No conjunto de teste completo (900 imagens, 1.840 instâncias), o checkpoint selecionado obteve mAP50 = 0,808 e mAP50-95 = 0,437, com precisão 0,82 e recall 0,72. Essa medida agregada confirma que o comportamento não depende apenas das poucas cenas da demonstração. Ela também não substitui a inspeção visual: não diz, sozinha, qual pose foi fácil nem onde apareceu um falso positivo.
Inspeção visual: a arquitetura é a mesma, os pesos não
A comparação mais honesta isola o efeito dos pesos. Mesma família YOLOv8s, mesma imagem, mesma região. À esquerda, o modelo do COCO escreve broccoli 0.33. À direita, o modelo adaptado ao POP escreve person 0.86.
YOLOv8s COCO: [('broccoli', 0.33)]
YOLOv8s POP: [('person', 0.86)]

A arquitetura não ganhou olhos novos. Ela recebeu exemplos de outro domínio. O modelo generalista responde com a experiência visual que teve. O modelo adaptado aprendeu como uma pessoa aparece naquele sensor, naquela perspectiva e naquela escala.
A mesma comparação, agora com ground truth, generalista e modelo do POP lado a lado, mostra o recorte ampliado do alvo. A cena abaixo é uma pessoa em pé; o notebook repete o painel para deslocamento e pose deitada.

Uma cena não prova superioridade universal da YOLOv8s. Ela torna visível uma afirmação mais precisa: dados representativos alteram o que o detector é capaz de associar à classe person. Uma avaliação séria combina essa leitura com a métrica do teste e com a análise dos erros restantes, sobretudo quando a oclusão aumenta e o alvo se aproxima do limite da resolução.
Se você quiser ver o mesmo tipo de raciocínio em outro domínio aéreo, o projeto mais antigo de vigilância com drones e Deep Learning mostra a outra ponta do problema: colocar o detector dentro de um fluxo que produz uma saída utilizável, não apenas uma caixa em um notebook.
Do ramo térmico ao projeto completo
O que este artigo demonstra é um recorte deliberado: uma modalidade, um domínio, um detector adaptado. Isso basta para tornar o efeito da especialização mensurável e visível. Não basta quando a vegetação apaga a silhueta no óptico ou quando o residual térmico se mistura ao terreno.
A imagem óptica oferece textura, contorno e contexto. A imagem térmica preserva contraste de temperatura quando a iluminação visível é ruim. Nenhum dos dois sensores é completo. A fusão só faz sentido se as duas observações se referem ao mesmo lugar no mesmo instante e se a arquitetura aprende o que aproveitar de cada ramo.
Esse é o passo seguinte no Bootcamp de Visão Computacional Aplicada a Drones: uma especialização prática em Data Science e Inteligência Artificial aplicada a drones, com projetos hands-on e modelos no estado da arte. Além da adaptação térmica, o treinamento cobre a fusão óptica e termal e outros problemas com dados aéreos reais. O foco não é pilotagem. É a cadeia que vai do dataset à avaliação.
Takeaways
- A classe pode ser a mesma e o domínio, outro.
personem uma fotografia terrestre não é o mesmo padrão visual quepersonem uma imagem térmica aérea, pequena e parcialmente ocluída. - Trocar a geração do detector não fecha automaticamente o gap. Nas cenas inspecionadas, YOLO11s treinado no COCO permanece incompatível com o domínio térmico, como o YOLOv8s generalista.
- O dataset precisa representar a dificuldade da missão. No POP, splits por cena, oclusão e alvos de dezenas de pixels são parte do problema, não um detalhe de formatação.
- A resolução de entrada é uma hipótese sobre o tamanho do objeto. Com mediana de 38×44 pixels e área relativa próxima de 0,13%, reduzir demais a imagem apaga o alvo antes do treino.
best.ptelast.ptrespondem a perguntas diferentes. Nesta execução, a melhor época de validação foi a 29; a última registrada foi a 49.- Métrica agregada e inspeção visual se complementam. O teste chega a mAP50 de 0,808; a cena
broccoli 0.33versusperson 0.86mostra o que essa métrica significa no quadro.
O notebook completo, com conversão, treinamento, curvas e comparação visual, está no gate no início deste artigo. Se você quiser aprofundar esse tipo de projeto em uma especialização prática, o caminho é o Bootcamp de Visão Computacional Aplicada a Drones.












