5. Capítulo 5 · Random Forest para detectar agua#

5.1. Introducción#

En este capítulo vamos a aplicar aprendizaje automático a imágenes satelitales para predecir superficies de agua. Utilizaremos datos satelitales de Sentinel-2 para procesar una colección de imágenes correspondientes a una región de estudio y aplicaremos un modelo de Random Forest para clasificar los píxeles en dos categorías: agua y no agua. Es decir, emplearemos una clasificación binaria.

En Google Earth Engine (GEE) podes explorar imagenes Sentinel haciendo click en Datasets (ver fig. Fig. 5.1) para acceder al catálogo de datos y luego hacer en la opción de menú Sentinel (ver fig. Fig. 5.2) y encontrarás todos los productos satelitales Sentinel.

_images/datasetsGoogle.png

Fig. 5.1 GEE: Opción de menú Datasets#

_images/sentinelGoogle.png

Fig. 5.2 GEE: Opción de menú Datasets:Sentinel#

Sentinel-2 es propiedad de la Agencia Espacial Europea [European Space Agency, 2015]. En Google Earth Engine tenemos acceso a colecciones de imágenes satelitales Sentinel-1, Sentinel-2, Sentinel-3 y Sentinel-5 [Google Earth Engine, 2025]. Nos vamos a centrar en Sentinel-2, cuyos datos son muy populares.

Se aplican en diversas áreas de monitoreo de la superficie terrestre, incluida la gestión de desastres, la gestión de emergencias, la cuantificación de tendencia de urbanización y la cobertura del suelo, entre otras. En este campo, los enfoques de [Longépé et al., 2011] han mostrado cómo el SAR es clave para la detección y monitoreo de inundaciones.

_images/PosIT-1.png

Fig. 5.3 Algunas aplicaciones de Sentinel-2#

Bien creamos un nuevo script denominado A001_RandomForest_CuerposDeAguaComahue. Nuestra área de estudio será la región del Comahue principalmente la provincia del Neuquén y la de Rio Negro. Vamos a crear nuestra región de estudio, nuestra región de interés o ROI.

_images/km.png

Fig. 5.4 Sobre el area de estudio#

_images/p_3.png

Fig. 5.5 Etapa: Clasificar la imagen#

Como no estamos interesados en los límites provinciales precisos de la provincia, debido a que queremos abarcar los rios que componen los limites naturales provinciales, no utilizaremos los límites administrativos exactos disponibles en la IDE de la provincia de Neuquén y Rio Negro, o en el Instituto Geográfico Nacional [(Argentina), 2025], sino que utilizaremos el conjunto de datos de Capas de Unidades Administrativas Globales [Nations, 2025] de Google Earth Engine.

Esto nos permitirá aprender un poco más sobre este conjunto de datos de Unidades Administrativas Globales:

En el buscador de dataset buscamos FAO GAUL: Global Administrative Unit Layers 2015, First-Level Administrative Units

_images/PosIT-2.png

Fig. 5.6 Sobre Global Administrative Unit Layers#

Las Capas de Unidades Administrativas Globales (GAUL) recopilan y difunden la mejor información disponible sobre unidades administrativas para todos los países del mundo, contribuyendo a la estandarización del conjunto de datos espaciales que representan las unidades administrativas.

Llamando a esta colección de imágenes, tendremos los límites de administración para todos los países.

var roi = ee.FeatureCollection("FAO/GAUL/2015/level1");

Si visualizamos el esquema de tabla (ver fig. Fig. 5.7) de este conjunto de datos, existen algunos atributos que utilizaremos para filtrar:

  • ADM0_CODE tiene el código de pais, y ADM0_NAME tiene el nombre de pais,

  • ADM1_CODE tiene el código de unidades administrativas de primer nivel y ADM1_NAME tiene el nombre de unidades administrativas de primer nivel.

_images/FAOSchema.png

Fig. 5.7 El esquema de Global Administrative Unit Layers#

Utilizando ADM0_NAME filtraremos Argentina y utilizando y ADM1_NAME filtraremos la unidad administrativa de la provincia del Neuquén y la Provincia de Rio Negro:

Primero mostramos como ADM0_NAME nos permite filtrar por nombre de país en este caso, Argentina. Luego mostramos como utilizando ADM1_NAME podemos filtrar las provincias de Rio Negro y Neuquén. Cuando agregamos esta capa al mapa la llamamos Comahue.

En ambos casos se inspeccionan datos utilizando el inspector de Google Earth Engine para ver como están escritos los nombres de provincias, por ejemplo Neuquén no esta con acento.

// Definir roi (region de interés o estudio): Prov. Neuquén y Rio Negro
var roi = ee.FeatureCollection("FAO/GAUL/2015/level1")
  .filter(ee.Filter.eq('ADM0_NAME', 'Argentina'))
  .filter(ee.Filter.inList('ADM1_NAME', ['Neuquen', 'Rio Negro']));

5.1.1. Center Object#

Centramos nuestra zona de estudio utilizando la función centerObject, haciendo variar el nivel de zoom.

La funcion centerObjet es una función que te ayuda a cambiar la etiqueta de zoom. Según la documentación el nivel de zoom es un valor entre 0 y 24. Podes ajustar el nivel de zoom. Si no ajustás el parametro de zoom, no se ampliará el área que te interesa.

Map.addLayer(roi, {}, 'Comahue');
Map.centerObject(roi,5);

5.1.2. Buffer#

Sin embargo antes de mostrar la región de estudio al mapa aplicaremos un buffer de 5 km para incluir los límites naturales completos que comprenden rios que separan provincias.

Se puede crear un buffer para un FeatureCollection usando el método .map() para aplicar una operación a cada Feature en el FeatureCollection de unidades administrativas. Acá estamos seleccionando dos unidades administrativas, que son cada una de las provincias.

// Crear un buffer de 5 km (5.000 metros)
var roi = roi.map(function(feature){
  return feature.buffer(5000);
});
Map.addLayer(roi, {}, 'Comahue');
Map.centerObject(roi,5);

Esta es nuestra área de estudio o roi.

5.1.3. Imágenes Sentinel-2#

A continuación buscamos Sentinel-2 y seleccionamos las colecciones de imágenes Harmonized sentinel-2 MSI (MultiSpectralInstrument) level 1-c. Copiamos su identificación que utilizarmeos en la defición de una colección de imágenes sentinel denominada image.

Filtraremos imágenes sentinel del año 2024. Utilizaremos datos de todo el año 2024, es decir, fecha de comienzo: 1 de enero de 2024 y fecha de finalización el 1 de enero de 2025 (esta ultima fecha se excluye) como podemos comprobar en la documentación de FilterData, un método que se aplica a un FeatureColection (start date es inclusive pero end date es exclusive).

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01');

5.1.4. Filtro de nubes#

Vamos a usar un filtro de nubes aquí. Cuando se utilizan datos satelitales, generalmente hay contaminación por nubes. Deseamos utilizar la mayor cantidad posible de imágenes libres de nubes. Entonces, aquí vamos a aplicar un filtro para seleccionar imágenes con menos de un 10% de nubes.

Esto se logra utilizando metadatos de la imagen satelital, si consultmaos las propiedades de la imagen, existe un atributo: CLOUDY_PIXEL_PERCENTAGE que representa el Porcentaje de píxeles nublados específicos. Esto nos permite utilizar una imagen de nubes en la que la contaminación de las nubes es menor al 10 %. Podes ajustar este parámetro en función del área de estudio según tu criterio de análisis.

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10));

5.1.5. FilterBounds()#

filterBounds(roi) tiene un propósito: Filtrar las imágenes en la colección para incluir solo aquellas imágenes que intersectan con la región de interés (roi). Limita la cantidad de imágenes en la colección para que solo contenga aquellas que cubren total o parcialmente la región definida por roi. Esto reduce el número de imágenes procesadas y mejora la eficiencia.

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(roi):

Lo que necesitamos es una sola imagen. Por lo tanto, necesitamos agregar todas las imágenes mediante un reductor o una agregación estadística. En este caso, vamos a utilizar la mediana. Se suele recomendar la mediana como mínimo mejor que la media. Se evitará incluir algunos valores atípicos, por lo que se utilizará el valor de la mediana. De esta manera, se evita incluir valores atípicos en el proceso de agregación estadística. En este caso, utilizamos la mediana.

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(roi)
  .median();

Luego recortamos la imagen en función la región de estudio con clipToCollection(roi). El propósito es recortar la imagen resultante al contorno exacto de la colección o región (roi). Tras combinar las imágenes (en este caso, usando .median()), recortamos la salida final para que coincida exactamente con el contorno de roi.

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(roi)
  .median()
  .clipToCollection(roi);

Ahora tenemos una imagen Sentinel más limpia, que está lista para nuestra clasificación de aprendizaje automático.

Cargamos nuestra imagen, a nuestro mapa con addLayer, si la imagen sentinel no cuenta con parámetros de visualización se verá negra. Podemos importar los parámetros de visualización , utilizando la configuración de la capa, seleccionando las 3 bandas RGB, bandas B4, B3 y B2 y un stretch de 98% que permite visualizar los valores de estas tres bandas. Aplicamos e importamos esta configuración. Hacemos una prueba con estos parámetros y ya visualizamos la colección de imágenes.

En su defecto podemos definir nuestros propios parámetros con una variable visParams, para ello, creamos un parámetro de visualización.

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(roi)
  .median()
  .clipToCollection(roi);

var visParams = {bands: ['B4','B3','B2'], min:0, max: 3000};
  
Map.addLayer(image, visParams, "Sentinel 2024");

// Map.addLayer(image, {bands: ['B5','B4','B3'], min:0, max: 3000}, 'False Color (543)');

Vamos a utilizar una composición de colores rojo, verde, azul (RGB). En este caso, vamos a proporcionar las bandas que vamos a utilizar acá: banda B4, banda B3, banda B2. No las estamos utilizando para nuestro modelo, sino solo para visualizar la imagen en el mapa. Y luego un valor mínimo y máximo en función de los datos de reflectancia de la superficie para estas bandas.

5.1.6. Función para calcular el NDVI#

En nuestra clasificación aplicando aprendizaje automático utilizaremos valores de reflectancia de ciertas bandas de la imagen Sentinel en conjunto con el índice NDVI.

El NDVI es el índice de vegetación diferencial normalizado que ayuda a monitorear la salud de la vegetación (ver fig. Fig. 5.8). Por lo tanto, un NDVI más alto generalmente indica una cobertura vegetal densa o una vegetación más verde. Valores negativos son comunes en cuerpos de agua abiertos. mientras que valores cercanos a 0 pueden observarse en agua con sedimentos o cubierta parcial por vegetación flotante (ver fig. Fig. 5.8).

_images/PosIT-NDVIbis.png

Fig. 5.8 Indice Espectral NDVI#

Rangos típicos del NDVI:

  • Agua: -1 a ~0

    • Valores negativos son comunes en cuerpos de agua abiertos.

    • Valores cercanos a 0 pueden observarse en agua con sedimentos o cubierta parcial por vegetación flotante.

  • Suelo desnudo: 0 a 0.2

  • Vegetación escasa: 0.2 a 0.5

  • Vegetación densa: 0.5 a 1

_images/valoresNDVI.png

Fig. 5.9 Valores típicos del NDVI#

Lo primero que debemos hacer es escribir una función que calcule el NDVI. Vamos a crear una función denominada agregarNDVI para calcular el NDVI, que vamos a utilizar en nuestra clasificación de aprendizaje automático.

Otros índices espectrales para calcular índices

También podríamos utilizar otros índices (ver fig. Fig. 5.10) como:

  • NDWI (Índice de Diferencia Normalizada del Agua)

  • MNDWI (Índice de Diferencia Normalizada del Agua Modificado)

  • AWEI (Índice de Extracción Automática del Agua)

  • LSWI (Índice del Agua Basado en Tierra)

  • WI (Índice del Agua)

_images/IndicesAguaBis.png

Fig. 5.10 Algunos Indices espectrales en el dominio de aplicación Agua#

Nuestra función agregarNDVI, tiene un parámetro de entrada imagen. Podemos llamarlo imagen o algún otro nombre significativo.

La primera instrucción es definir la variable NDVI. que es igual a la diferencia normalizada de la banda B8, la banda infrarroja cercana, y la banda B4, la banda roja de la imagen Sentinel. Si consultamos la documentación del método normalizedDifference, este metodo se aplica a una imagen. Aca se utiliza la notación orientada a objetos objeto.metodo() el objeto es una imagen y el método es normalizedDifference.

Consultando la documentación de los métodos que se aplican a un objeto, la función normalizedDifference, una función incorporada a Google Earth Engine, calcula la diferencia normalizada entre dos bandas. Es necesario proporcionar las dos bandas a este método para obtener el índice NDVI calculado.

El método no tiene parámetros obligatorios, cuando los parámetros en la documentación están en letra itálica esto significa que los parámetros son opcionales. Si no se especifican las bandas a utilizar, es decir no se proporcionan parámetros, el método utiliza las dos primeras bandas. La diferencia normalizada se calcula con la siguiente fórmula: (primero − segundo) / (primero + segundo)

para nuestro ejemplo:

(first  second) / (first + second)  = (B8  B4) / (B8 + B4)

es decir

NDVI= (NIRRED) / (NIR+RED)

Asi de simple, es calcular un índice para una imagen satelital. Este acercamiento nos permite definir otros índices como: NDWI, MNDWI, y otros mostrados en la fig. Fig. 5.10 ya qeu tienen el mismo patrón de fórmula:

_images/IndicesAgua.png

Fig. 5.11 Indices Espectrales con el mismo patron de fórmula matemática#

Luego, finalmente, la funcion devuelve nuestra imagen, a la cual le agregaremos estas banda calculada con el NDVI.

// función para calcular NDVI
var agregarNDVI = function(imagen){
  var ndvi = imagen.normalizedDifference(['B8','B4']).rename('NDVI');
  return imagen.addBands(ndvi);
};

// Cargar imagen Sentinel
var image = ee.ImageCollection("COPERNICUS/S2_HARMONIZED")
  .filterDate('2024-01-01','2025-01-01')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(roi)
  .map(agregarNDVI)
  .median()
  .clipToCollection(roi);

var visParams = {bands: ['B4','B3','B2'], min:0, max: 3000};
  
Map.addLayer(image, visParams, "Sentinel 2024");

// Map.addLayer(image, {bands: ['B5','B4','B3'], min:0, max: 3000}, 'False Color (543)');

Esta función se define para una imagen, luego calculará técnicamente el NDVI para todas las imágenes dentro de la colección de imágenes con la operación map.

Antes de ejecutar este código comprobamos que la colección de imágenes no cuenta inicialmente con este índice NDVI, y que solo luego de ejecutar el código es posible ver este nuevo índice en cada imagen.

5.1.7. Imágen de Falso Color#

Generaremos una imagen de falso color utilizando las bandas B5 (NIR), B4 (Red), y B3 (Green) de Sentinel-2, que nos permitirá detectar agua y generar mas facilmente los puntos de entrenamiento. Esto permite detectar cuerpos de agua de manera efectiva debido a cómo el agua interactúa con la luz en estas longitudes de onda.

_images/FalsoColor.png

Fig. 5.12 Falso Color: Descripción#

Imágen de falso color

En un mapa con este esquema de falso color (ver fig. Fig. 5.13):

_images/mapaFalsoColor.png

Fig. 5.13 Falso Color: Ejemplo#

  • Ríos, lagos y océanos: Oscuros o negros.

  • Vegetación: Rojo brillante.

  • Suelo desnudo o urbano: Varía entre tonos de gris y verde tenue.

Estos son los datos de Sentinel que vamos a utilizar para nuestro aprendizaje automático, es decir, la clasificación supervisada Random forest.

Map.addLayer(image, {bands: ['B5','B4','B3'], min:0, max: 3000}, 'False Color (543)');

5.2. Workflow#

Para aplicar la técnica supervisada de random forest tendremos en cuenta el flujo de trabajo de la fig. Fig. 6.1.

_images/Workflow.png

Fig. 5.14 Workflow para aplicar ML a imágenes satelitales#

5.2.1. Entrenamiento#

Ahora crearemos datos de entrenamiento y luego ejecutaremos nuestro aprendizaje automático.

_images/p_1.png

Fig. 5.15 Etapa: Seleccionar las muestras de entrenamiento#

Para crear los datos de entrenamiento, utilizando la herramienta de dibujo del panel de Earth Engine. Crearemos una nueva capa, denominada agua, de tipo featureCollection, y definimos una propiedad denomninada categoriaAgua, con valor 1. También crearemos otro Featurecollection denominado no-agua, con una propiedad denominada categoriaAgua, con valor 0. De esta forma tenemos dos FeatureCollection Agua y NoAgua, con cero features.

Vamos a comenzar a recopilar los datos, primero para el FeatureCollection agua, vamos a resaltar aquella capa para la cual deseamos capturar datos de entrenamiento.

En este caso, se resaltamos la capa agua. Lo que haremos será recopilar la mayor cantidad posible de pixeles que representen agua y asegurarnos de que estén en nuestro region de estudio. Vamos a capturar tantos puntos como sea posible y luego lo vamos a cambiar un poco de zonas para obtener muestras espacialmente dispersas y representativas. Continuamos capturando más puntos. Esto debería ser suficiente para nuestro feature collection de agua.

Continuamos con nuestro FeatureCollection de no-agua. Podemos caputrar puntos que no sean agua, esto puede ser pixeles de una zona urbana, un cultivo o un bosque. Esta es un área agrícola de cultivo marcaeremos algunos putnos. Podemos ir a alguna ciudad importante solo para capturar pixels de la zona urbana. Podemos capturar puntos de cobertura vegetal tambien. Capturar puntos de una zona industrial. Y estamos a punto de terminar nuestra captura de muestras de no agua. Estos puntos de control en el terreno, deberían ser suficientes para nuestro análisis.

Si realmente estás haciendo un análisis científico real o tu proyecto, necesitamos capturar varios puntos de datos representativos y asegurarnos de que la calidad de los puntos de datos sea buena.

Nuestro próximo paso es combinar ambos featureCollection en un solo featureCollection, esto lo hace la función MERGE. Fusiona ambas capas: agua y no-agua en una sola colección denominada training. La fusión o merge, combinó estos dos colecciones en una, la cual contiene tiene aproximadamente más de 300 puntos de datos de entrenamiento, puntos de clase agua y de clase no-agua.

Muy bien. Ahora estamos listos para nuestra clasificación de aprendizaje automático.

La etiqueta es categoriaAgua y representa nuestra etiqueta para la clasificación de aprendizaje automático o la clasificación supervisada. Recorda que la definimos cuando creamos los dos featureCollection de datos de entrenamiento agua y no-agua. Esa será la etiqueta para nuestra clasificación.

Vamos a utilizar para el entrenamiento las bandas B2, B3, B4, B8 y NDVI de la imagen del satélite Sentinel. Extraemos las bandas Sentinel para estos datos de entrenamiento que hemos capturado.

_images/Tabla_Comahue.png

Fig. 5.16 Variables Independientes y Dependientes#

Para obtener estos datos de la imagen utilizamos la función sampleRegions. Vamos a utilizar la resolución de 10 metros, la resolución más alta posible de los datos de Sentinel-2.

SampleRegions

SampleRegions permite definir la tabla que entrena el modelo de clasificación supervisada. Los parámetro de esta función son un diccionario de JavaScript que incluye:

  • En collection especificamos la coleccion de imagenes Sentinel con las bandas seleccionadas.

  • En properties indicamos la etiqeuta que deseamos predecir, en este caso categoriaAgua, que representa la propiedad o etiqueta para nuestro entrenamiento.

  • En scale indicamos la escala es la resolución espacial, que es de 10 metros, porque estamos usando las bandas Sentinel con una resolución de 10 metros.

Podemos imprimir la tabla de entrenamiento que vamos a utilizar para la clasificación de aprendizaje automático. Contamos con el valor de las bandas Sentinel seleccionadas, la banda NDVI y la etiqueta categoriaAgua.

Necesitamos separar algunos de los datos para entrenar el modelo y también algunos de los datos para evaluarlo. Por lo tanto, dividiremos esos datos: crearemos una columna aleatoria y luego solo el 70 % de los datos se utilizarán para entrenar nuestro modelo. Y luego, el conjunto de prueba aquí será la evaluación, que es el 30 % de datos restantes.

5.2.2. Entrenamiento#

_images/p_2.png

Fig. 5.17 Etapa Entrenamiento del Clasificador#

En este paso entrenemos el modelo para luego aplicarlo a la imagen con el fin de generar un mapa de masa de agua. El paso de entrenar el modelo consiste en aplicar la clasificación supervisada de Random Forest con los datos de entremiento.

Tendrás features, que son datos de entrenamiento, que es el conjunto de entrenamiento. Luego una etiqueta, necesita una etiqueta para la clasificación, y finalmente, las bandas, que usamos para hacer la predicción, que son las bandas Sentinel.

Entonces, cuando ejecutes esto, entrenarás el modelo de aprendizaje automático. Cuando lo haga, el modelo de aprendizaje automático se entrenará en función de nuestros datos de entrenamiento en las bandas Sentinel.

// Crear los datos de entrenamiento
var training = agua.merge(noAgua);
print('FeatureCollection Training:', training);
print('size of Training:', training.size());

var label = 'categoriaAgua';
var bands = ['B2', 'B3', 'B4', 'B8','NDVI'];
var input = image.select(bands);

// Superponer los puntos sobre la imagen para obtener datos completos de entrenamiento

var trainImage = input.sampleRegions({
  collection: training,
  properties: [label],
  scale: 10
});


// separar de los datos de entrenamiento
// 80% para entrenar el modelo y 20% para test

var trainingData = trainImage.randomColumn();
var trainSet = trainingData.filter(ee.Filter.lessThan('random', 0.8));
var testSet = trainingData.filter(ee.Filter.greaterThanOrEquals('random', 0.8));

// print('size of trainingData', trainingData.size());
// print('size of trainSet', trainSet.size());
// print('size of testSet', testSet.size());

// Modelo de Clasificación
var classifier = ee.Classifier.smileRandomForest(30)
   .train({
     features: trainSet,
     classProperty: label,
     inputProperties: bands
   });
   
// Classify the Image
var classifiedImage = input.classify(classifier);
_images/Tabla_1.png

Fig. 5.18 Tabla de variables independientes y dependientes#

5.2.3. Clasificar la imagen#

_images/p_3.png

Fig. 5.19 Etapa Clasificar la imagen#

El siguiente paso es aplicar este modelo, que es el clasificador, a la imagen, la imagen Sentinel. Simplemente se va a aplicar el modelo para generar una clasificación.

Ahora, podemos ver la imagen, que es una clasificación. El siguiente paso es ver nuestra imagen clasificada en el mapa.

// Modelo de Clasificación
var classifier = ee.Classifier.smileRandomForest(30)
   .train({
     features: trainSet,
     classProperty: label,
     inputProperties: bands
   });
   
// Classify the Image
var classifiedImage = input.classify(classifier);

5.2.4. Máscara#

En el comando: var water = classifiedImage.updateMask(classifiedImage);

La función updateMask utiliza los valores de classified como una máscara. Los píxeles con valor 1 (agua) permanecen visibles, mientras que los píxeles con valor 0 (no agua) se enmascaran (ocultan).

Esto se traduce en:

  • Si un píxel tiene valor 1 → Se mantiene visible en la nueva variable water.

  • Si un píxel tiene valor 0 → Se vuelve invisible en water.

var water = classifiedImage.updateMask(classifiedImage);

Crear un parámetro de visualización: cuyo parámetros son el mínimo y el máximo. Por lo tanto, se trata simplemente de un binario de cero y uno. El azul es para la clase de agua y el blanco para la clase de no-agua y luego agregaremos la capa al mapa.

Agregaremos la capa al mapa, pasaremos la imagen clasificada aquí, los parámetros de visualización y la llamaremos agua. Entonces, simplemente ejecutemos eso. Y cuando lo ejecutemos, veremos nuestra clase de agua.

Esta es nuestra clase de agua, que se superpone a la imagen de Sentinel. Es nuestra clase de agua basada en el aprendizaje automático en la imagen de Sentinel. Esta es nuestra clasificación. Se trata de una clasificación predicha por un modelo basado en el aprendizaje automático, específicamente en un algoritmo de Random Forest o bosque aleatorio en la plataforma Earth Engine que utiliza datos satelitales Sentinel-2 de 10 metros.

var visParams = {min: 0, max: 1, palette: ['white','blue']};
Map.addLayer(water, visParams, 'Water');

5.2.5. Evaluación de precisión (Acurracy Assessment)#

Como hemos mencionado en capítulos anteriores, hay algunos pasos después de este posprocesamiento y tenemos que calcular evaluaciones de precisión de estos datos.

Utilizaremos la función de matriz de confusión. Vamos a utilizar el conjunto de prueba y luego el clasificador. Este último es nuestro modelo, el modelo de entrenamiento. Utilizamos los datos del conjunto de prueba que están configurados para la evaluación.

El primero es el valor real, que es el valor observado en función de este conjunto de pruebas, Y el siguiente es el valor previsto, que es la clasificación. Una vez que se proporcionan estos parámetros, se va a crear una evaluación de precisión, es decir, valores.

_images/p_4.png

Fig. 5.20 Etapa Evaluación de Precisión#

// Accuracy Assessment
// Obtener la matriz de confusión
// Accuracy Assessment
// Obtener la matriz de confusión

var confusionMatrix = ee.ConfusionMatrix(testSet.classify(classifier)
   .errorMatrix({
     actual: 'categoriaAgua',   // valor real
     predicted: 'classification' // valor previsto
   }));
   
print('Confusion Matrix:', confusionMatrix);
// Obtener otras métricas 
print('Overall Accuracy:', confusionMatrix.accuracy());
print('Producers Accuracy:', confusionMatrix.producersAccuracy());
print('Consumers Accuracy:', confusionMatrix.consumersAccuracy());

// Exportación de la clasificación

Export.image.toDrive({
  image: water,
  description: 'Proceso_de_Exportacion',
  scale: 10,
  region: roi, //aca podemos cambiar la region de exportación
  maxPixels: 1e13, // Aumenta el límite al máximo
  fileFormat: 'GeoTIFF' //GeoTIFF es el predeterminado
});

Algunos de estos valores incluyen: una matriz de confusión, una tabla y también la precisión general, es decir, la precisión del productor y la precisión del consumidor.

Básicamente, nos limitaremos a observar la precisión general y ejecutaremos la prueba basándonos en estos puntos de datos. Veamos cuál es nuestra precisión. Parece que es 100 %, bastante buena, pero creo que si capturamos más puntos de datos.

La razón por la que esto es 100 % cierto es que:

  • A, el agua es bastante fácil de predecir,

  • B, hemos utilizado la resolución de 10 metros, que también es otro factor.

5.2.6. Exportación#

La última parte a considerar, es que si deseas usar esta clasificación fuera de Google Earth Engine, digamos un software GIS estándar, como QGIS o ArcGIS vas a usar la función Export.image.toDrive para exportar, ya sabes, tu mapa de agua.

Cuando lo ejecutes, lo verás en tu Google Drive. Una vez que hayas completado esto, verás los datos o la imagen en tu Google Drive. Luego, lo descargarás desde Google Drive a tu equipo local y lo abrirás en un software de GIS estándard. La exportación duro varias horas, considerando que el mapa comprende un extensión de 297.091 kilómetros cuadrados.

// Exportación de la clasificación
Export.image.toDrive({
  image: water,
  description: 'Proceso_de_Exportacion',
  scale: 10,
  region: roi, //aca podemos cambiar la region de exportación
  maxPixels: 1e13, // Aumenta el límite al máximo
  fileFormat: 'GeoTIFF' //GeoTIFF es el predeterminado
});

Así es como se aplica la clasificación de aprendizaje automático, los datos satelitales en este caso Sentinel de Google Earth Engine Cloud y se realiza con estos pasos generales:

  • Captura tus propios datos de entrenamiento,

  • Desarrolla un modelo de aprendizaje automático,

  • Aplicas el modelo para clasificar la imagen de la region de estudio

  • Estudias el rendimiento de tu modelo y

  • También exporta tu clasificación final a Google Drive.

5.3. Árbol de decisión luego de aplicar CART#

Se puede aplicar el método CART en lugar de Random Forest, como se ejemplificó en el capítulo anterior. Para obtener el árbol de decisión en formato dot el siguiente código python muestra el árbol de decisión obtenido:

!apt-get install graphviz
!pip install graphviz

# Instalar dependencias adicionales para la conversión de PDF a imagen
!apt-get -qq install -y poppler-utils
!pip install pdf2image


# Importar las librerías necesarias
import graphviz
from google.colab import files
from pdf2image import convert_from_path
import matplotlib.pyplot as plt

# Definir las reglas del árbol de decisión en formato DOT
dot_data = """
digraph DecisionTree {
 node [shape=box, style="filled, rounded", color="black", fontname=helvetica];
 edge [fontname=helvetica];
 0 [label=<B8 &le; 969.0000<br/>score = 0.4649>, fillcolor="#00000000"];
 1 [label=<class = 1>, fillcolor="#00000000", shape=ellipse];
 0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"];
 2 [label=<class = 0>, fillcolor="#00000000", shape=ellipse];
 0 -> 2 [labeldistance=2.5, labelangle=-45, headlabel="False"];
}
"""

# Crear el gráfico a partir del DOT
graph = graphviz.Source(dot_data)

# Renderizar el árbol de decisión en formato PDF
graph.render('decision_tree', format='pdf', cleanup=False)

# Convertir el PDF a imagen
images = convert_from_path('decision_tree.pdf')

# Mostrar la imagen del árbol de decisión
plt.imshow(images[0])
plt.axis('off')  # Desactivar los ejes para que solo se vea la imagen
plt.show()

Si ejecutamos este último código python en Colab, obtendremos el gráfico del árbol de decisión de la fig. Fig. 5.21. El árbol tiene un único nodo de decisión, que es el nodo raíz del árbol, en el cual la banda espectral B8 con la condición B8 <= 0.4998 es suficiente para determinar si un píxel es agua o no-agua. Si la condición B8 <= 0.4998 es verdadera el píxel es clasificado como agua, en caso contrario es clasificado como no-agua.

_images/arbolAguaNoagua.png

Fig. 5.21 Árbol de decisión para detectar agua obtenido con CART#

5.4. Video del capítulo#

Podes mirar el video asociado a este capítulo en el canal de youtube de IDERA: https://www.youtube.com/watch?v=-DMd6LXIEkE&t=610s