Filtragem inteligente no Google Earth Engine: selecionando imagens que cobrem totalmente sua região de interesse

Iago Mendes
OLAB Learning
Published in
6 min readJan 4, 2024

Por Iago Mendes, OLAB Learning

Introdução

Filtrar imagens que cobrem uma determinada região no globo é uma tarefa presente no dia-a-dia da maioria dos profissionais que trabalham com dados de sensoriamento remoto. No Google Earth Engine (GEE), existem algumas formas de realizar essa operação, cada uma adequada a diferentes necessidades e contextos.

Compreendendo o desafio

A complexidade de trabalhar com o GEE não reside apenas em processar uma enorme quantidade de dados, mas em filtrar esses dados para encontrar aqueles que de fato são relevantes para um projeto. E é aqui que reside nosso desafio: identificar e selecionar imagens que cobrem totalmente uma determinada região de interesse (ROI).

Neste post, vamos explorar uma forma menos conhecida de filtrar coleções de dados no GEE, que faz uso conjunto de redutores e filtros.

Caso de uso fictício

Antes de mergulharmos no passo a passo, vamos considerar um cenário hipotético que ilustra nosso desafio.

Imagine que estamos trabalhando em um projeto em uma região localizada no interior do estado do Amazonas, exatamente na interseção de três cenas Landsat.

ROI no centro de três cenas Landsat.

O desafio surge quando percebemos que precisamos selecionar imagens que cobrem totalmente a área do projeto.

Com este cenário em mente, vamos explorar uma abordagem que nos permite enfrentar esse desafio de maneira elegante e eficaz.

Passo a passo

Definindo a ROI

Começamos por definir nossa ROI. Isso pode ser feito usando coordenadas geográficas ou ferramentas de desenho disponíveis no GEE. Utilize o código abaixo para definir e visualizar a roi no GEE.

// Cria uma representação da ROI.
var roi = ee.Geometry.Polygon([
[
[
-64.36872874097912,
-2.079767541325792
],
[
-64.27927445549919,
-2.079767541325792
],
[
-64.27927445549919,
-1.989796173264031
],
[
-64.36872874097912,
-1.989796173264031
],
[
-64.36872874097912,
-2.079767541325792
]
]
]);

// Visualiza a ROI.
Map.centerObject(roi, 10);
Map.addLayer(roi, { color: '#A6D96A' }, 'ROI');

Acessando a coleção de imagens

O próximo passo é acessar a coleção de imagens apropriada. O GEE oferece coleções de diversos satélites, como Landsat, Sentinel, etc.

O que faremos neste tutorial pode ser replicado para qualquer coleção de imagens.

// ID da coleção Landsat 8 Level 2, Collection 2, Tier 1.
var colId = 'LANDSAT/LC08/C02/T1_L2';

// Carrega a coleção de imagens.
var images = ee.ImageCollection(colId);

Filtragem inicial dos dados

Para reduzirmos o número de imagens e otimizarmos nosso código, aplicaremos os seguintes filtros na coleção de imagens images:

  • filterBounds() para filtrar as imagens que intersectam a roi. Ou seja, imagens que têm uma sobreposição parcial ou total com a roi.
  • filterDate() para filtrar imagens que foram adquiridas dentro de um intervalo temporal — entre as datas start e end.
  • select() para selecionar as bandas VNIR-SWIR do Landsat 8 (L8).
    VNIR refere-se às bandas do espectro visível (vermelho, verde, azul) e ao infravermelho próximo — bandas B2, B3, B4 e B5 .
    SWIR refere-se às bandas do infravermelho de ondas curtas — bandas B6 e B7.
// Filtra as imagens da coleção.
var start = '2022-01-01';
var end = '2023-12-31';

images = images
.filterBounds(roi)
.filterDate(start, end)
.select('SR_B[2-7]');

Poderíamos aplicar outros tipos de filtros, como o filtro por cobertura de nuvens ou outras propriedades específicas do sensor, mas esse não é o objetivo do post.

O resultado da filtragem é uma coleção com 103 imagens, cada uma contendo seis bandas.

Utilize print(images) para visualizar o resultado.

Refinando a filtragem

Se inspecionarmos as images filtradas na etapa anterior, encontraremos diversos casos como este:

Imagem LANDSAT/LC08/C02/T1_L2/LC08_233061_20230915.

Nosso objetivo então é filtrar apenas as imagens que cobrem totalmente a ROI.

A primeira opção

Ora, filtrar a coleção de imagens com base nas propriedades WRS_ROW e WRS_PATH das imagens L8 resolveria o problema, correto? A resposta é: depende.

Pode acontecer de você estar trabalhando com imagens sem metadados, como as imagens computadas — imagens geradas a partir de operações computacionais, como o cálculo de um índice espectral. Nesses casos, precisaremos de uma alternativa.

O racional da solução

E se pudéssemos verificar se a ROI está totalmente preenchida por pixels? E se, após realizar essa verificação, pudéssemos "marcar" as imagens que cobrem totalmente a ROI?

A boa notícia é que isso é possível com a utilização do método reduceRegion() associado ao redutor ee.Reducer.allNonZero(). A aplicação dessas ferramentas no GEE nos permite fazer exatamente isso, desta forma:

function addFullCoverageFlag(image) {
// Extrai a máscara da imagem.
var mask = image.select(0).mask();

// Verifica se todos os pixels são diferentes de 0 na ROI.
var result = mask.reduceRegion({
reducer: ee.Reducer.allNonZero(),
geometry: roi,
scale: image.select(0).projection().nominalScale(),
maxPixels: 1e9
});

// Extrai o valor da flag.
var flag = result.get(image.bandNames().get(0));

// Retorna a imagem com a propriedade full_coverage adicionada a ela.
return image.set('full_coverage', flag);
}

Encapsulamos toda a lógica necessária dentro da função addFullCoverage. Vamos entendê-la:

  • A expressão var mask = image.select(0).mask() seleciona a primeira banda da imagem image e extrai a máscara dessa banda. Uma máscara é uma imagem binária , ou seja, uma imagem cujos pixels assumem apenas os valores 0 ou 1. Na região onde há informação na imagem original, o valor correspondente da máscara é 1; onde não há informação, o valor é 0. O resultado dessa operação é então atribuído à variável mask.
  • mask.reduceRegion() aplica um redutor a todos os pixels da imagem mask em uma região específica.
    reducer: ee.Reducer.allNonZero() define um redutor que retorna 1 se todos os inputs (pixels) forem diferentes de zero, caso contrário retorna 0.
    geometry: roi define roi como a região sobre a qual o redutor será aplicado.
    scale: image.select(0).projection().nominalScale() define a escala utilizada na redução. É equivalente a scale: 30, já que a resolução espacial das imagens L8 é 30 metros.
    maxPixels: 1e9 define o número máximo de pixels a serem reduzidos. Este valor (1e9) é o suficiente para cobrir uma cena L8.

Para cada imagem, o resultado da redução (result) será como este:

Como selecionamos apenas uma banda em image.select(0).mask(), o resultado será um Dictionary com apenas um par de chave-valor.

Em uma redução, o número de pares chave-valor resultante será igual ao número de bandas da imagem de entrada.

Na sequência, utilizamos o método get(). Como argumento para este método, fornecemos image.bandNames().get(0), que corresponde ao nome da primeira banda da imagem image. Este nome da banda é, por sua vez, usado como chave do para obter o valor correspondente. O resultado dessa operação é então atribuído à variável flag.

Por fim, a função retorna a image de entrada com a propriedade full_coverage adicionada a ela.

Propriedade full_coverage adicionada à imagem.

O fluxo completo

Podemos escalar o processo de marcação das imagens com map(). Ao aplicarmos map(addFullCoverageFlag) à nossa coleção de imagens, estamos instruindo o sistema a aplicar a função addFullCoverageFlag a cada imagem da coleção.

Dica: No GEE, a função map() é utilizada quando precisamos executar um processo em cada elemento de uma coleção.

Finalizamos o processo com o uso do método filter, combinado ao filtro ee.Filter.eq('full_coverage', 1). Isso nos permite selecionar apenas as imagens cuja propriedade full_coverage é igual a 1. Em outras palavras, filtramos para obter somente as imagens que cobrem totalmente a ROI.

// ID da coleção Landsat 8 Level 2, Collection 2, Tier 1.
var colId = 'LANDSAT/LC08/C02/T1_L2';

// Carrega a coleção de imagens.
var images = ee.ImageCollection(colId);

// Filtra as imagens da coleção.
var start = '2022-01-01';
var end = '2023-12-31';

images = images
.filterBounds(roi)
.filterDate(start, end)
.select('SR_B[2-7]')
.map(addFullCoverageFlag)
.filter(ee.Filter.eq('full_coverage', 1));

Conclusão

Com esses passos, você pode otimizar seu fluxo de trabalho no Google Earth Engine, focando apenas em imagens que são mais relevantes para a sua análise.

Dúvidas? Sinta-se à vontade para deixar suas perguntas nos comentários.

Script | Siga @olablearning

--

--