Can you advise me on how to correctly use .stratifiedSample to prep input for .smileRandomForest, using Google Earth Engine (GEE) Python API?
I'm trying to create training data with .stratifiedSample. I'm sampling from a layer with four classes, labeled 0-3. Then I want to use that output to train a .smileRandomForest classifier, and then classify the input image with the trained classifier.
The code runs, but the .classify step produces an image object that I can't visualize and which is difficult to examine (by which I mean .bandNames() and .display() don't work on it). I assume a step before that (sampling or training the classifier) is not set up correctly, but I'm not sure how.
My code is below:
#Imports
import numpy
import ee
import geemap
#ee.Authenticate()
ee.Initialize()
#Constants
i_date = '2024-02-10'
f_date = '2024-03-01'
lon = -80.205595
lat = 25.748059
geometry = ee.Geometry({
'type': 'Polygon',
'coordinates': [[ #lat, lon
[-80.267610, 25.796023], #Top left,
[-80.263518, 25.730725], #Lower left,
[-80.121574, 25.730725], #Lower right,
[-80.121574, 25.810281], #Top right
]],
})
geometry_bounds = geometry.bounds()
# Define functions
def addNDWI(image):
ndwi = (image.select('B3').subtract(image.select('B8A'))).divide(image.select('B3').add(image.select('B8A'))).rename('NDWI_water')
return image.addBands(ndwi)
def addMNDWI(image):
mndwi = (image.select('B3').subtract(image.select('B11'))).divide(image.select('B3').add(image.select('B11'))).rename('MNDWI_water')
return image.addBands(mndwi)
def addAWEI(image):
awei = (((image.select('B2').add(image.select('B3').multiply(ee.Number(2.5)))).subtract(
(image.select('B8A').add(image.select('B11'))).multiply(ee.Number(1.5))).subtract(
image.select('B12').multiply(ee.Number(.25)))).divide(
image.select('B2').add(image.select('B3')).add(image.select('B8A')).add(image.select('B11')).add(image.select('B12')))).rename('AWEI_water')
return image.addBands(awei)
def reclass_band(image, bandname, threshold, newname):
bandtoreclass = image.select(bandname)
reclassed = bandtoreclass.where(bandtoreclass.lt(threshold), 0).where(bandtoreclass.gte(threshold), 1).rename(newname)
return image.addBands(reclassed)
def tally(image):
tallied = (image.select('NDWI_reclass')).add(image.select('MNDWI_reclass')).add(image.select('AWEI_reclass')).toUint8().rename('Ensemble_Vote')
return image.addBands(tallied)
# Get imagery (Sentinel 2)
s2_filtered = ee.ImageCollection('COPERNICUS/S2_SR').filterBounds(geometry).filterDate(i_date, f_date)
s2_clipped_collection = ee.ImageCollection(s2_filtered.map(lambda image: image.clip(geometry)))
# Add spectral indices
s2_ndwi_added = s2_clipped_collection.map(addNDWI)
s2_mndwi_added = s2_ndwi_added.map(addMNDWI)
s2_just_ndwi_mndwi_awei = s2_mndwi_added.map(addAWEI)
# Reclass indices to binary
s2_ndwi_reclass_added = s2_just_ndwi_mndwi_awei.map(lambda image: reclass_band(image, 'NDWI_water', 0, 'NDWI_reclass'))
s2_mndwi_reclass_added = s2_ndwi_reclass_added.map(lambda image: reclass_band(image, 'MNDWI_water', 0, 'MNDWI_reclass'))
s2_awei_reclass_added = s2_mndwi_reclass_added.map(lambda image: reclass_band(image, 'AWEI_water', 0, 'AWEI_reclass'))
s2_B8A_reclass_added = s2_awei_reclass_added.map(lambda image: reclass_band(image, 'B8A', 850, 'B8A_NIR_reclass'))
s2_B12_reclass_added = s2_B8A_reclass_added.map(lambda image: reclass_band(image, 'B12', 800, 'B12_SWIR_reclass'))
# Calculate ensemble vote (0-3, how many indices agree a pixel is water)
s2_tallied = s2_B12_reclass_added.map(tally)
# Get one image to test the sampling method on.
s2_List_b=ee.ImageCollection(s2_tallied).toList(s2_tallied.size());
s2_single_Image_b=ee.Image(ee.List(s2_List_b).get(1));
### The following does not produce expected output:
# Collect stratified random points in the four ensemble classes (0-3)
stratified = s2_single_Image_b.stratifiedSample(
numPoints=1000,
classBand='Ensemble_Vote',
geometries=True
)
# Attempt to create classifier, train using points collected from the ensemble layer, and then classify an image using the trained classifier
RF_classifier = ee.Classifier.smileRandomForest(numberOfTrees=30)
Training_RF_classifier = RF_classifier.train(
features=stratified,
classProperty="Ensemble_Vote",
inputProperties=s2_single_Image_b,
)
classifiedImage = s2_single_Image_b.classify(Training_RF_classifier)
# none of these produce information:
band_names = classifiedImage.bandNames()
display('Band names:', band_names)
classifiedImage.propertyNames()
# View
m = geemap.Map(center=[lat, lon], zoom=13)
viz2 = {'min': 0, 'max': 3, 'palette': ['34a116', '185bba', 'fc0398','0000FF']}
m.addLayer(classifiedImage.select('Ensemble_Vote'), viz2, 'Classified')
m
#The .addLayer produces EEException: Classifier.train, argument 'inputProperties': Invalid type.
#Expected type: List<String>.
#Actual type: Image<...