def mod_ndwi_learned(domain, b): if domain.unflooded_domain == None: print "No unflooded training domain provided." return None unflooded_b = modis_utilities.compute_modis_indices(domain.unflooded_domain) water_mask = modis_utilities.get_permanent_water_mask() threshold = modis_utilities.compute_binary_threshold(get_mod_ndwi(unflooded_b), water_mask, domain.bounds) return mod_ndwi(domain, b, threshold)
def mod_ndwi_learned(domain, b): if domain.unflooded_domain == None: print 'No unflooded training domain provided.' return None unflooded_b = modis_utilities.compute_modis_indices(domain.unflooded_domain) water_mask = modis_utilities.get_permanent_water_mask() threshold = modis_utilities.compute_binary_threshold(get_mod_ndwi(unflooded_b), water_mask, domain.bounds) return mod_ndwi(domain, b, threshold)
def dart_learned(domain, b): """The dartmouth method but with threshold calculation included (training image required)""" if domain.unflooded_domain == None: print "No unflooded training domain provided." return None unflooded_b = modis_utilities.compute_modis_indices(domain.unflooded_domain) water_mask = modis_utilities.get_permanent_water_mask() threshold = modis_utilities.compute_binary_threshold(get_dartmouth(unflooded_b), water_mask, domain.bounds) return dartmouth(domain, b, threshold)
def dart_learned(domain, b): '''The dartmouth method but with threshold calculation included (training image required)''' if domain.unflooded_domain == None: print 'No unflooded training domain provided.' return None unflooded_b = modis_utilities.compute_modis_indices(domain.unflooded_domain) water_mask = modis_utilities.get_permanent_water_mask() threshold = modis_utilities.compute_binary_threshold(get_dartmouth(unflooded_b), water_mask, domain.bounds) return dartmouth(domain, b, threshold)
def diff_learned(domain, b): '''modis_diff but with the threshold calculation included (training image required)''' if domain.unflooded_domain == None: print 'No unflooded training domain provided.' return None unflooded_b = modis_utilities.compute_modis_indices(domain.unflooded_domain) water_mask = modis_utilities.get_permanent_water_mask() threshold = modis_utilities.compute_binary_threshold(get_diff(unflooded_b), water_mask, domain.bounds) return modis_diff(domain, b, threshold)
def dnns(domain, b, use_modis_diff=False): '''Dynamic Nearest Neighbor Search adapted from the paper: "Li, Sun, Yu, et. al. "A new short-wave infrared (SWIR) method for quantitative water fraction derivation and evaluation with EOS/MODIS and Landsat/TM data." IEEE Transactions on Geoscience and Remote Sensing, 2013." The core idea of this algorithm is to compute local estimates of a "pure water" and "pure land" pixel and compute each pixel's water percentage as a mixed composition of those two pure spectral types. ''' # This algorithm has some differences from the original paper implementation. # The most signficant of these is that it does not make use of land/water/partial # preclassifications like the original paper does. The search range is also # much smaller in order to make the algorithm run faster in Earth Engine. # - Running this with a tiny kernel (effectively treating the entire region # as part of the kernel) might get the best results! # Parameters KERNEL_SIZE = 40 # The original paper used a 100x100 pixel box = 25,000 meters! AVERAGE_SCALE_METERS = 250 # This scale is used to compute averages over the entire region # Set up two square kernels of the same size # - These kernels define the search range for nearby pure water and land pixels kernel = ee.Kernel.square(KERNEL_SIZE, 'pixels', False) kernel_normalized = ee.Kernel.square(KERNEL_SIZE, 'pixels', True) # Compute b1/b6 and b2/b6 composite_image = b['b1'].addBands(b['b2']).addBands(b['b6']) # Use CART classifier to divide pixels up into water, land, and mixed. # - Mixed pixels are just low probability water/land pixels. if use_modis_diff: unflooded_b = modis_utilities.compute_modis_indices(domain.unflooded_domain) water_mask = get_permanent_water_mask() thresholds = modis_utilities.compute_binary_threshold(simple_modis_algorithms.get_diff(unflooded_b), water_mask, domain.bounds, True) pureWater = simple_modis_algorithms.modis_diff(domain, b, thresholds[0]) pureLand = simple_modis_algorithms.modis_diff(domain, b, thresholds[1]).Not() mixed = pureWater.Or(pureLand).Not() else: classes = ee_classifiers.earth_engine_classifier(domain, b, 'Pegasos', {'classifier_mode' : 'probability'}) pureWater = classes.gte(0.95) pureLand = classes.lte(0.05) #addToMap(classes, {'min': -1, 'max': 1}, 'CLASSES') #raise Exception('DEBUG') mixed = pureWater.Not().And(pureLand.Not()) averageWater = safe_get_info(pureWater.mask(pureWater).multiply(composite_image).reduceRegion(ee.Reducer.mean(), domain.bounds, AVERAGE_SCALE_METERS)) averageWaterImage = ee.Image([averageWater['sur_refl_b01'], averageWater['sur_refl_b02'], averageWater['sur_refl_b06']]) # For each pixel, compute the number of nearby pure water pixels pureWaterCount = pureWater.convolve(kernel) # Get mean of nearby pure water (b1,b2,b6) values for each pixel with enough pure water nearby MIN_PUREWATER_NEARBY = 1 pureWaterRef = pureWater.multiply(composite_image).convolve(kernel).multiply(pureWaterCount.gte(MIN_PUREWATER_NEARBY)).divide(pureWaterCount) # For pixels that did not have enough pure water nearby, just use the global average water value pureWaterRef = pureWaterRef.add(averageWaterImage.multiply(pureWaterRef.Not())) # Compute a backup, global pure land value to use when pixels have none nearby. averagePureLand = safe_get_info(pureLand.mask(pureLand).multiply(composite_image).reduceRegion(ee.Reducer.mean(), domain.bounds, AVERAGE_SCALE_METERS)) #averagePureLand = composite_image.mask(pureLand).reduceRegion(ee.Reducer.mean(), domain.bounds, AVERAGE_SCALE_METERS) averagePureLandImage = ee.Image([averagePureLand['sur_refl_b01'], averagePureLand['sur_refl_b02'], averagePureLand['sur_refl_b06']]) # Implement equations 10 and 11 from the paper --> It takes many lines of code to compute the local land pixels! oneOverSix = b['b1'].divide(b['b6']) twoOverSix = b['b2'].divide(b['b6']) eqTenLeft = oneOverSix.subtract( pureWaterRef.select('sur_refl_b01').divide(b['b6']) ) eqElevenLeft = twoOverSix.subtract( pureWaterRef.select('sur_refl_b02').divide(b['b6']) ) # For each pixel, grab all the ratios from nearby pixels nearbyPixelsOneOverSix = oneOverSix.neighborhoodToBands(kernel) # Each of these images has one band per nearby pixel nearbyPixelsTwoOverSix = twoOverSix.neighborhoodToBands(kernel) nearbyPixelsOne = b['b1'].neighborhoodToBands(kernel) nearbyPixelsTwo = b['b2'].neighborhoodToBands(kernel) nearbyPixelsSix = b['b6'].neighborhoodToBands(kernel) # Find which nearby pixels meet the EQ 10 and 11 criteria eqTenMatches = ( nearbyPixelsOneOverSix.gt(eqTenLeft ) ).And( nearbyPixelsOneOverSix.lt(oneOverSix) ) eqElevenMatches = ( nearbyPixelsTwoOverSix.gt(eqElevenLeft) ).And( nearbyPixelsTwoOverSix.lt(twoOverSix) ) nearbyLandPixels = eqTenMatches.And(eqElevenMatches) # Find the average of the nearby matching pixels numNearbyLandPixels = nearbyLandPixels.reduce(ee.Reducer.sum()) meanNearbyBandOne = nearbyPixelsOne.multiply(nearbyLandPixels).reduce(ee.Reducer.sum()).divide(numNearbyLandPixels) meanNearbyBandTwo = nearbyPixelsTwo.multiply(nearbyLandPixels).reduce(ee.Reducer.sum()).divide(numNearbyLandPixels) meanNearbyBandSix = nearbyPixelsSix.multiply(nearbyLandPixels).reduce(ee.Reducer.sum()).divide(numNearbyLandPixels) # Pack the results into a three channel image for the whole region # - Use the global pure land calculation to fill in if there are no nearby equation matching pixels MIN_PURE_NEARBY = 1 meanPureLand = meanNearbyBandOne.addBands(meanNearbyBandTwo).addBands(meanNearbyBandSix) meanPureLand = meanPureLand.multiply(numNearbyLandPixels.gte(MIN_PURE_NEARBY)).add( averagePureLandImage.multiply(numNearbyLandPixels.lt(MIN_PURE_NEARBY)) ) # Compute the water fraction: (land[b6] - b6) / (land[b6] - water[b6]) # - Ultimately, relying solely on band 6 for the final classification may not be a good idea! meanPureLandSix = meanPureLand.select('sum_2') water_fraction = (meanPureLandSix.subtract(b['b6'])).divide(meanPureLandSix.subtract(pureWaterRef.select('sur_refl_b06'))).clamp(0, 1) # Set pure water to 1, pure land to 0 water_fraction = water_fraction.add(pureWater).subtract(pureLand).clamp(0, 1) #addToMap(fraction, {'min': 0, 'max': 1}, 'fraction', False) #addToMap(pureWater, {'min': 0, 'max': 1}, 'pure water', False) #addToMap(pureLand, {'min': 0, 'max': 1}, 'pure land', False) #addToMap(mixed, {'min': 0, 'max': 1}, 'mixed', False) #addToMap(pureWaterCount, {'min': 0, 'max': 300}, 'pure water count', False) #addToMap(water_fraction, {'min': 0, 'max': 5}, 'water_fractionDNNS', False) #addToMap(pureWaterRef, {'min': 0, 'max': 3000, 'bands': ['sur_refl_b01', 'sur_refl_b02', 'sur_refl_b06']}, 'pureWaterRef', False) return water_fraction.select(['sum_2'], ['b1']) # Rename sum_2 to b1