Este tutorial tem como intuito mostrar como gerar um raster binário, muito utilizado em problemas de segmentação semântica, com o python. As máscaras binárias possuem o valor de 0 para o background e 1 para a classe de interesse.

Workflow

 

0. Importar as bibliotecas


import os

import rasterio
from rasterio.plot import reshape_as_image
import rasterio.mask
from rasterio.features import rasterize

import pandas as pd
import geopandas as gpd
from shapely.geometry import mapping, Point, Polygon
from shapely.ops import cascaded_union

import numpy as np
import cv2
import matplotlib.pyplot as plt

 

1. Carregar a Imagem .tif

Para carregar a imagem .tif defina a variável raster_path como o caminho para a imagem sobre a qual será gerada a máscara binária.


raster_path = "/Users/...../imagem.tif"
with rasterio.open(raster_path, "r") as src:
    raster_img = src.read()
    raster_meta = src.meta

 

2. Carregar o Shapefile ou GeoJson

Para carregar o arquivo shapefile (.shp) ou GeoJson (.geojson) defina a variável shape_path com o local onde está localizado o arquivo.


shape_path = "/Users/.../poligono.geojson"
train_df = gpd.read_file(shape_path)



3. Verificar se os Sistemas de Cordenadas de Referência (CRS) são os mesmos

Caso os Valores sejam diferentes, é necessário reprojetar o Vetor (Shapefile ou GeoJson) ou a imagem (raster). Para isso, pode-se utilizar o software QGIS.

 
 print("CRS do Raster: {}, CRS do Vetor {}".format(train_df.crs, src.crs))

 

4. Gerar a Máscara Binária

Para gerar a máscara binária basta rodar o código abaixo. Ao final, a máscara gerada será plotada.

#Função que gera o polígono
def poly_from_utm(polygon, transform):
    poly_pts = []
    
    # Gerar um polígono a partir de multipolígonos.
    poly = cascaded_union(polygon)
    for i in np.array(poly.exterior.coords):
        
        # Transformar os polígonos para o CRS da imagem, utilizando os metadados do raster.
        poly_pts.append(~transform * tuple(i))
        
    # Gerar um objeto de polígono
    new_poly = Polygon(poly_pts)
    return new_poly

# Gerar a máscara binária

poly_shp = []
im_size = (src.meta['height'], src.meta['width'])
for num, row in train_df.iterrows():
    if row['geometry'].geom_type == 'Polygon':
        poly = poly_from_utm(row['geometry'], src.meta['transform'])
        poly_shp.append(poly)
    else:
        for p in row['geometry']:
            poly = poly_from_utm(p, src.meta['transform'])
            poly_shp.append(poly)

mask = rasterize(shapes=poly_shp,
                 out_shape=im_size)

# Plotar a máscara gerada

plt.figure(figsize=(15,15))
plt.imshow(mask)

 

5. Salvar

Para salvar, deve-se converter o arquivo para ‘uint16’ e definir o local que será salvo o arquivo.

mask = mask.astype("uint16")
arquivo_salvar = "/Users/.../mascaras/train.tif"
bin_mask_meta = src.meta.copy()
bin_mask_meta.update({'count': 1})
with rasterio.open(arquivo_salvar, 'w', **bin_mask_meta) as dst:
    dst.write(mask * 255, 1)

 

6. Definir uma Função que gera máscaras binárias

A função generate_mask(), tem como entrada os parâmentros abaixo:

raster_path = local onde a imagem .tif esta localizada;

shape_path = local onde o Shapefile ou GeoJson está localizado.

output_path = local onde será salva a máscara binária gerada.

file_name = nome do aquivo que será gerado.


def generate_mask(raster_path, shape_path, output_path, file_name):
    
    """Os CRS devem ser iguais para gerar a máscara binária
    
    raster_path = local onde a imagem .tif esta localizada;

    shape_path = local onde o Shapefile ou GeoJson está localizado.

    output_path = local onde será salva a máscara binária gerada.

    file_name = nome do aquivo que será gerado.
    
    """
    
    #Carregar o Raster
    
    with rasterio.open(raster_path, "r") as src:
        raster_img = src.read()
        raster_meta = src.meta
    
    #Carregar o shapefile ou GeoJson
    train_df = gpd.read_file(shape_path)
    
    #Verificar se o CRS é o mesmo
    if train_df.crs != src.crs:
        print(" crs do Raster : {} e Crs do Vetor : {}.\n Converta para o mesmo Sistema de Coordenadas de Referência!".format(src.crs,train_df.crs))
        
        
    #Função para gerar a máscara
    def poly_from_utm(polygon, transform):
        poly_pts = []

        poly = cascaded_union(polygon)
        for i in np.array(poly.exterior.coords):

            poly_pts.append(~transform * tuple(i))

        new_poly = Polygon(poly_pts)
        return new_poly
    
    
    poly_shp = []
    im_size = (src.meta['height'], src.meta['width'])
    for num, row in train_df.iterrows():
        if row['geometry'].geom_type == 'Polygon':
            poly = poly_from_utm(row['geometry'], src.meta['transform'])
            poly_shp.append(poly)
        else:
            for p in row['geometry']:
                poly = poly_from_utm(p, src.meta['transform'])
                poly_shp.append(poly)

    mask = rasterize(shapes=poly_shp,
                     out_shape=im_size)
    
    #Salvar
    mask = mask.astype("uint16")
    
    bin_mask_meta = src.meta.copy()
    bin_mask_meta.update({'count': 1})
    os.chdir(output_path)
    with rasterio.open(file_name, 'w', **bin_mask_meta) as dst:
        dst.write(mask * 255, 1)


 

Referências