Um guia passo a passo sobre como coletar e pré-processar imagens de satélite data para algoritmos de aprendizado de máquina
Este artigo é a primeira parte de uma série de duas partes que explora os usos de algoritmos de aprendizado de máquina em imagens de satélite. Aqui, vamos nos concentrar nas etapas de coleta e pré-processamento do data, enquanto a segunda parte mostrará o uso de vários algoritmos de aprendizado de máquina.
TL;DR
Este artigo abordará:
Usaremos a detecção e a classificação de campos agrícolas como exemplo. O Python e o jupyter notebook são usados para as etapas de pré-processamento. O notebook do jupyter para reproduzir as etapas está disponível no github.
Este artigo pressupõe fundamentos básicos em ciência data e python.
A visão computacional é um ramo fascinante do aprendizado de máquina que vem se desenvolvendo rapidamente nos últimos anos. Com acesso público a muitas bibliotecas de algoritmos prontos para uso (Tensorflow, OpenCv, Pytorch, Fastai ...), bem como os datasets de código aberto (CIFAR -10, Imagenet, IMDB ...) é muito fácil para um cientista data obter experiência prática nesse tópico.
Mas o senhor sabia que também pode acessar imagens de satélite de código aberto? Esses tipos de imagens podem ser usados em uma grande variedade de casos de uso (agricultura, logística, energia...). No entanto, elas representam desafios adicionais para um cientista data devido ao seu tamanho e complexidade. Escrevi este artigo para compartilhar alguns dos meus principais aprendizados ao trabalhar com elas.
Etapa 1 - Selecione a fonte do satélite aberto data
Primeiro, o senhor precisa escolher o satélite que deseja usar. Os principais recursos a serem observados aqui são :
Aqui estão os satélites cujo data está disponível gratuitamente:

Benchmark de sistemas de satélite
Como nosso objetivo é detectar e identificar culturas agrícolas, o espectro visível (que é usado para fornecer imagens coloridas padrão) deve ser bastante útil. Além disso, há correlações entre o nível de nitrogênio das plantações e as bandas espectrais NIR (infravermelho próximo). No que diz respeito às opções gratuitas, o Sentinel 2 é atualmente o melhor em termos de resolução, com 10 m para essas bandas espectrais, portanto, usaremos esse satélite.
Obviamente, a resolução de 10 m é muito menor em comparação com o estado da arte em imagens de satélite, com satélites comerciais que atingem uma resolução de 30 cm. O acesso ao satélite comercial data pode desbloquear muitos casos de uso, mas, apesar da crescente concorrência com novos participantes entrando nesse mercado, eles permanecem com preços altos e, portanto, difíceis de integrar em um caso de uso em escala.
Etapa 2 - Coletar as imagens de satélite data do Sentinel 2
Agora que selecionamos nossa fonte de satélite, a nova etapa é fazer o download das imagens. O Sentinel 2 faz parte do Programa Copernicus da Agência Espacial Europeia. Podemos acessar seu data usando o API em python do SentinelSat.
Primeiro, precisamos criar uma conta no site do Centro de Acesso Aberto do Copernicus. Como alternativa, observe que esse site também oferece uma maneira de consultar imagens com uma GUI.
Uma maneira de especificar as zonas que o senhor deseja explorar é fornecer à API um arquivo geojson, que é um json contendo a posição das coordenadas GPS (latitude e longitude) de uma zona. O Geojson pode ser criado rapidamente no seguinte site.
Dessa forma, o senhor pode consultar todas as imagens de satélite que cruzam a zona fornecida.
Aqui listamos todas as imagens disponíveis em uma determinada zona e intervalo de tempo:
api = SentinelAPI(
credenciais[“nome de usuário”],
credenciais[“senha”],
“https://scihub.copernicus.eu/dhus”
)
shape = geojson_to_wkt(read_geojson(geojson_path))
images = api.query(
forma,
date=(date(2020, 5, 1), date(2020, 5, 10)),
platformname=”Sentinel-2″,
processinglevel = “Level-2A”,
cloudcoverpercentage=(0, 30)
)
images_df = api.to_dataframe(images)
Observe o uso de argumentos:

images_df
Images_df é um pandas dataframe que contém todas as imagens que correspondem à nossa consulta. Selecionamos uma com baixa cobertura de cloud e a baixamos usando a API :
api.download(uuid)
Etapa 3 - Entenda quais bandas espectrais devem ser usadas e gerencie a quantização
Após o download do arquivo zip, podemos ver que ele contém várias imagens.
Isso ocorre porque, como dissemos na etapa 1, o Sentinel 2 captura muitas bandas espectrais e não apenas Vermelho, Verde e Azul, e cada banda é capturada em uma única imagem.
Além disso, nem todos os instrumentos têm a mesma resolução espacial, portanto, há uma subpasta para cada resolução diferente de 10, 20 e 60 metros.
Se verificarmos o GRANULE//IMG_DATA vemos que há uma subpasta para cada resolução diferente: 10, 20 e 60 metros como as bandas espectrais. Vamos usar aquelas com resolução máxima de 10 m, pois elas fornecem as bandas 2, 3, 4 e 8, que são azul, verde, vermelho e NIR. As bandas 7, 8 e 9, que são a borda vermelha da vegetação (bandas espectrais entre o vermelho e o NIR que marcam uma transição no valor de refletância da vegetação), também poderiam ser interessantes para a detecção de plantações, mas como são de resolução mais baixa, vamos deixá-las de lado agora
Aqui carregamos cada banda como uma matriz numpy 2D:
def get_band(image_folder, band, resolution=10):
subpasta = [f for f in os.listdir(image_folder + "/GRANULE") if f[0] == "L"][0]
image_folder_path = f"/GRANULE//IMG_DATA/Rm"
image_files = [im for im in os.listdir(image_folder_path) if im[-4:] == ".jp2"]
selected_file = [im for im in image_files if im.split("_")[2] == band][0]
com rasterio.open(f"/") as infile:
img = infile.read(1)
retornar img
band_dict =
para banda em ["B02", "B03", "B04", "B08"]:
band_dict[band] = get_band(image_folder, band, 10)
Um teste rápido para ter certeza de que tudo está funcionando é tentar recriar a imagem de cor verdadeira (ou seja, RGB) e exibi-la usando opencv e matplotlib:
img = cv2.merge((band_dict[“B04”], band_dict[“B03”], band_dict[“B02”]))
plt.imshow(img)

Imagem de satélite - Problema de intensidade de cor (Copernicus Sentinel data 2020)
No entanto, há um problema com a intensidade da cor. Isso ocorre porque a quantização (número de valores possíveis) difere do padrão para matplotlib, que assume um valor int 0-255 ou um valor float 0-1. As imagens padrão geralmente usam uma quantização de 8 bits (256 valores possíveis), mas as imagens do Sentinel 2 usam uma quantização de 12 bits e um reprocessamento converte os valores em um inteiro de 16 bits (65536 valores).
Se quisermos escalar um inteiro de 16 bits para um inteiro de 8 bits, deveríamos, em teoria, dividir por 256, mas isso, na verdade, resulta em uma imagem muito escura, pois não é usado todo o intervalo de valores possíveis. O valor máximo em nossa imagem é, na verdade, 16752 de 65536 e poucos pixels atingem valores superiores a 4000, portanto, a divisão por 8 proporciona uma imagem com um contraste decente
img_processed = img / 8
img_processed = img_processed.astype(int)
plt.imshow(img_processed)

Imagem de satélite - Intensidade da cor direita (Copernicus Sentinel data 2020)
Agora temos um tensor de 3 dimensões de formato (10980, 10980, 3) de números inteiros de 8 bits na forma de uma matriz numpy
Observe que :
Etapa 4 - Dividir as imagens em tamanhos prontos para aprendizado de máquina
Poderíamos tentar aplicar um algoritmo à nossa imagem atual, mas, na prática, isso não funcionaria com a maioria das técnicas devido ao seu tamanho. Mesmo com grande capacidade de computação, a maioria dos algoritmos (e especialmente a aprendizagem profunda) estaria fora de cogitação.
Portanto, precisamos dividir a imagem em fragmentos. Uma questão é como definir o tamanho do fragmento.
Decidimos dividir cada imagem em uma grade de 45 * 45 (45 sendo convenientemente um divisor de 10980)
Após a fixação do parâmetro, esta etapa é simples, usando a manipulação de matriz numpy. Armazenamos nossos valores em um dict usando tuplas (x, y) como chaves.
frag_count = 45
frag_size = int(img_processed.shape[0] / frag_count)
frag_dict =
for y, x in itertools.product(range(frag_count), range(frag_count)):
frag_dict[(x, y)] = img_processed[y*frag_size: (y+1)*frag_size,
x*frag_size: (x+1)*frag_size, :]
plt.imshow(frag_dict[(10, 10)])

Fragmento da imagem de satélite (Copernicus Sentinel data 2020)
Etapa 5 - Vincule seu data às imagens de satélite usando a conversão de GPS para UTM

Por fim, precisamos vincular nossas imagens ao data que temos nos campos agrícolas para permitir uma abordagem de aprendizado de máquina supervisionado. No nosso caso, temos o data em campos agrícolas na forma de uma lista de coordenadas de GPS (latitude, longitude), semelhante ao geojson que usamos na etapa 2, e queremos poder encontrar a posição do pixel. No entanto, para fins de clareza, começarei mostrando uma conversão de pixel em coordenadas de GPS e, em seguida, mostrarei o inverso
O metadata coletado ao solicitar a API do SentinelSat fornece as coordenadas de GPS dos cantos da imagem (coluna footprint) e queremos as coordenadas de pixels específicos. A latitude e a longitude, que são valores angulares, não evoluem linearmente em uma imagem, portanto não podemos fazer uma relação simples usando a posição do pixel. Há maneiras de resolver isso usando equações matemáticas, mas já existem soluções prontas em python.
Uma solução rápida é converter a posição do pixel em UTM (Universal Transverse Mercator), na qual uma posição é definida por uma zona, bem como uma posição (x, y) (em unidade de metro) que evolui linearmente com a imagem. Podemos então usar uma conversão de UTM para Latitude/Longitude fornecida na biblioteca utm. Para fazer isso, primeiro precisamos obter a zona UTM e a posição do canto superior esquerdo da imagem, que pode ser encontrada no metadata da imagem true color.
#Ocontendo meatadata
transform = rasterio.open(tci_file_path, driver=’JP2OpenJPEG’).transform
zone_number = int(tci_file_path.split(“/”)[-1][1:3])
letra_da_zona = tci_file_path.split(“/”)[-1][0]
utm_x, utm_y = transform[2], transform[5]
# Conversão da posição do pixel em utm
east = utm_x + pixel_column * 10
norte = utm_y + pixel_row * - 10
# Conversão de UTM em latitude e longitude
latitude, longitude = utm.to_latlon(east, north, zone_number, zone_letter)
A conversão da posição GPS em pixels é feita usando as fórmulas inversas. Nesse caso, temos de nos certificar de que a zona obtida corresponde à nossa imagem. Isso pode ser feito especificando a zona. Se o nosso objeto não estiver presente na imagem, isso resultará em um valor de pixel fora do limite.
# Conversão de latitude e longitude para UTM
east, north, zone_number, zone_letter = utm.from_latlon(
latitude, longitude, force_zone_number=número_da_zona
)
# Conversão de UTM em coluna e linha
pixe_column = round((east - utm_x) / 10)
pixel_row = round((north - utm_y) / -10)
ver bruto
Conclusão
Agora estamos prontos para usar as imagens de satélite para aprendizado de máquina!
No próximo artigo desta série de duas partes, veremos como podemos usar essas imagens para detectar e classificar superfícies de campos usando aprendizado de máquina supervisionado e não supervisionado.
Obrigado pela leitura e não hesite em Siga o blog de tecnologia do Artefact se o senhor quiser ser notificado quando o próximo artigo for lançado!

BLOG







