forked from matthias92schubert/Forest_Monitoring_BR
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcloud_mask_algorithms
315 lines (259 loc) · 13.7 KB
/
cloud_mask_algorithms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
// Function that selects and returns the correct cloud mask for a given image.
var selectCM = function(func_image, func_CM) {
var image_date = ee.Date(func_image.get('system:time_start')).format('YYYY-MM-dd').replace('-','','g');
var cm_band_names = func_CM.bandNames();
var list = cm_band_names.map(function(instance) {
return ee.String(instance).rindex(image_date);
});
var right_cm = list.indexOf(19);
var matched_cm_string = ee.String(cm_band_names.get(right_cm));
var mask = ee.Image(func_CM.select(matched_cm_string)).unmask().remap([0, 1], [1, 0]);
var func_image_masked = func_image.updateMask(mask);
return func_image_masked;
}
// Function to simply clip every Image of an Image Collection.
var clipImage = function(clipping_geometry) {
var wrap = function(image) {
var clipped_image = image.clip(clipping_geometry);
return clipped_image;
};
return wrap;
};
// Function to simply clip an Image.
var clipSingleImage = function(image, clipping_geometry) {
return image.clip(clipping_geometry);
};
// Function to compute new CLOUDY_PIXEL_PERCENTAGE-property (CPP) for clipped images.
var newCPP = function(area_of_interest) {
var wrap = function(image) {
var qa60 = image.select(['QA60']);
qa60 = qa60.unmask();
// Get all cloudy pixels from QA60-band.
var clouds = qa60.expression(
'denseCloud || cirrus',{
'denseCloud': qa60.eq(1024),
'cirrus': qa60.eq(2048)
});
// Count all cloudy pixels (all pixels with value 1).
var countCloudyPixels = clouds.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: area_of_interest,
scale : 10
});
// Get all cloud-free pixels from QA60-band.
var noClouds = qa60.expression(
'cloudless',{
'cloudless': qa60.eq(0),
});
// Count all cloud-free pixels (all pixels with value 1).
var countNoCloudPixels = noClouds.reduceRegion({
reducer: ee.Reducer.sum(),
geometry: area_of_interest,
scale: 10
});
// Compute new ratio (CPP). #cloudy pixels / #all pixels * 100.
var nrCloudyPixels = ee.Number(countCloudyPixels.get('QA60')).int();
var nrNoCloudPixels = ee.Number(countNoCloudPixels.get('QA60')).int();
var allPixels = ee.Number(nrCloudyPixels.add(nrNoCloudPixels));
var newCPP = ee.Number(nrCloudyPixels.divide(allPixels).multiply(100));
return image.set({CLOUDY_PIXEL_PERCENTAGE: newCPP});
};
return wrap;
};
// Function to eliminate several same images in a row.
var getRidOfDoubles = function(image, list) {
var previous = ee.Image(ee.List(list).get(-1));
var date_0 = ee.Date(previous.get('system:time_start')).format('YYYY-MM-dd');
var date_1 = ee.Date(ee.Image(image).get('system:time_start')).format('YYYY-MM-dd');
var marked_image = ee.Algorithms.If(ee.Algorithms.IsEqual(date_0, date_1),
marked_image = image.set('repetition?', 1),
marked_image = image.set('repetition?', 0));
return ee.List(list).add(marked_image);
};
// Function that returns a normalized feature collection together with mean and std.
var computeNormalizationFeatureCollection = function(feature_collection, bands_to_normalize) {
var bands_to_normalize_server = ee.List(bands_to_normalize);
var mean = feature_collection.reduceColumns({
reducer: ee.Reducer.mean().forEach(bands_to_normalize_server),
selectors: bands_to_normalize_server
});
var sd = feature_collection.reduceColumns({
reducer: ee.Reducer.stdDev().forEach(bands_to_normalize_server),
selectors: bands_to_normalize_server
});
var normalizeFeature = function(feature) {
feature = ee.Feature(feature);
var values = bands_to_normalize_server.map(function(i) {
var dummi_number = ee.Number(feature.get(i)).subtract(mean.get(i)).divide(sd.get(i));
return dummi_number;
});
var dic = ee.Dictionary.fromLists(bands_to_normalize_server, values);
return feature.setMulti(dic);
};
var mapped_feature_collection = feature_collection.map(normalizeFeature);
mapped_feature_collection = mapped_feature_collection.set('mean', mean, 'sd', sd);
return mapped_feature_collection;
};
// Function that normalizes an Image based on mean and standart deviation values from the training feature.
var applyNormalizationImage = function(img, mean, sd) {
var normalized_img = ee.ImageCollection.fromImages(
img.bandNames().map(function(band){
band = ee.String(band);
var img_band = img.select(band);
return img_band.subtract(ee.Number(mean.get(band))).divide(ee.Number(sd.get(band)))
})).toBands().rename(img.bandNames());
return normalized_img;
};
// Function to select the clusters.
var selectClusters = function(img, background_model, result_clustering, n_clusters, bands_thresholds, region_of_interest) {
var n_clusters_list = ee.List.sequence(0, n_clusters.subtract(1));
var bands_norm_difference = bands_thresholds.map(function(bands) {
var entry = ee.String(bands).cat("_difference");
return entry;
});
var img_joined = img.subtract(background_model).select(bands_thresholds, bands_norm_difference).addBands(img.select(bands_thresholds));
var bands_and_difference_bands = bands_thresholds.cat(bands_norm_difference);
var wrap = n_clusters_list.map(function(i) {
i = ee.Number(i);
var img_diff_clus = img_joined.updateMask(result_clustering.eq(i)).select(bands_and_difference_bands);
var clusteri = img_diff_clus.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: region_of_interest,
bestEffort: true,
scale: 10,
tileScale: 4
});
var clusteri_diff = clusteri.toArray(bands_norm_difference);
var clusteri_refl = clusteri.toArray(bands_thresholds);
var clusteri_refl_norm = clusteri_refl.multiply(clusteri_refl).reduce(ee.Reducer.mean(), [0]).sqrt().get([0]);
var clusteri_diff_mean = clusteri_diff.reduce(ee.Reducer.mean(), [0]).get([0]);
var clusteri_diff_norm = clusteri_diff.multiply(clusteri_diff).reduce(ee.Reducer.mean(), [0]).sqrt().get([0]);
var multitemporal_score_clusteri = ee.Algorithms.If(clusteri_diff_mean.gt(0), clusteri_diff_norm, clusteri_diff_norm.multiply(-1));
multitemporal_score_clusteri = result_clustering.eq(i).toFloat().multiply(ee.Number(multitemporal_score_clusteri));
var reflectance_score_clusteri = result_clustering.eq(i).toFloat().multiply(ee.Number(clusteri_refl_norm));
var multitemporal_score = multitemporal_score_clusteri;
var reflectance_score = reflectance_score_clusteri;
var scores = ee.Dictionary({multitemporalScore: multitemporal_score, reflectanceScore: reflectance_score});
return scores;
});
return wrap;
};
// Function to merge all Images of an Image Collection into a single Image with 1 band.
var mergeBands = function(image, previous) {
return ee.Image(previous).add(image);
};
// Function that combines the resulting list from this script (all individual cloud masks) into a single ee.Image with the different
// list entries beeing the individual layers of that new image.
var cloudMasks2Image = function(cloud_mask, func_image) {
cloud_mask = ee.Image(cloud_mask);
func_image = ee.Image(func_image);
var imageID = ee.String(cloud_mask.get('image_id'));
func_image = func_image.addBands(cloud_mask.select([0], [ee.String('CM_').cat(imageID)]));
return func_image;
};
// Main code for background estimation, invokes the codes above.
var constructCloudMask = function(instance) {
// Extract the image with clouds, area of interest (convex hulls around homogenous trees), and clipping geometry (study area boundaries)
// from the dictionary that has been passed to this function.
var image = ee.Image(ee.Dictionary(instance).get('dic_image'));
image = image.unmask();
var area_of_interest = ee.FeatureCollection(ee.Dictionary(instance).get('dic_area_of_interest'));
var clipping_geometry = ee.FeatureCollection(ee.Dictionary(instance).get('dic_clipping_geometry'));
// Define important parameters.
var bands_model = image.bandNames();
var bands_threshold = ee.List(["B2", "B3", "B4"]);
var n_clusters = ee.Number(10);
var threshold_dif_cloud = ee.Number(0.045).multiply(10000);
var numPixels = ee.Number(1000);
var threshold_reflectance = ee.Number(0.15).multiply(10000);
var growing_ratio = ee.Number(2);
var imageDate = image.get('system:time_start');
var imageID = image.id();
var fullImageID = ee.String('COPERNICUS/S2/').cat(imageID);
// Steps to calculate background model (mean of the three images chronologically before the cloudy image).
// Extract start and end date for future Image Collection based on cloudy image.
var end_date = ee.Date(image.get('system:time_start'));
var start_date_number = ee.Number.parse(ee.Date(image.get('system:time_start')).format('YYYY')).subtract(1);
var start_date = ee.Date.parse('YYYY', start_date_number);
var list_dates = ee.List.sequence(1, 2).set(0, start_date).set(1, end_date);
var image_system_index = image.get('system:index');
// Construct Image Collection. Start: 1 Year before image aqucisition date. End: acquisition date.
var image_collection = ee.ImageCollection('COPERNICUS/S2')
.filterDate(list_dates.get(0), list_dates.get(1))
.filterBounds(area_of_interest)
.filter(ee.Filter.neq('system:index', image_system_index))
.sort('system:time_start');
//var image_collection = ee.ImageCollection('COPERNICUS/S2')
//.filterDate(list_dates.get(0), list_dates.get(1))
//.filterBounds(ee.Geometry.Point([9.426189582664733,48.28392370408561]));
// Clip constructed image collection to geometry.
var clipped_image_collection = image_collection.map(clipImage(clipping_geometry));
// Calculate new CPP value for all images in clipped image collection.
var clipped_image_collection_newCPP = clipped_image_collection.map(newCPP(clipping_geometry));
// Filter image collection for CPP less than 10.
var filtered_clipped_image_collection_newCPP = clipped_image_collection
.filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 10));
// Get rid of doubled images.
var filtered_clipped_image_collection_newCPP_unique = filtered_clipped_image_collection_newCPP
.iterate(getRidOfDoubles, ee.List([ee.Image(0).set('system:time_start', 0, 'repetition?', 1)]));
var filtered_clipped_image_collection_newCPP_unique_1 = ee.ImageCollection(ee.List(filtered_clipped_image_collection_newCPP_unique)
.filter(ee.Filter.neq('repetition?', 1)));
// Slice image collection to the last three images.
var filtered_clipped_image_collection_newCPP_unique_limit = filtered_clipped_image_collection_newCPP_unique_1
.limit(3, 'system:time_start', false)
.sort('system:time_start');
// Combine pictures using a median filter. Background Model is finished.
var median_picture = ee.Image(filtered_clipped_image_collection_newCPP_unique_limit
.median());
// Calculate difference image (cloud image - background model).
var difference_image = image.subtract(median_picture);
// K-means clustering.
var training = difference_image.sample(clipping_geometry, 10, null, null, numPixels, 0, true, 2);
var normalized_training = computeNormalizationFeatureCollection(training, bands_model);
var mean = ee.Dictionary(normalized_training.get('mean'));
var sd = ee.Dictionary(normalized_training.get('sd'));
normalized_training = normalized_training.set('mean', null, 'sd', null); // Eventuell wieder rausnehemn... geändert nach dem 20.5
var clusterer = ee.Clusterer.wekaKMeans(n_clusters).train(normalized_training);
var difference_image_normalized = applyNormalizationImage(difference_image, mean, sd);
var result = difference_image_normalized.cluster(clusterer);
//result = ee.Image(result).set(
// 'image_id', imageID);
// Calculate multitemporal score and reflectance score.
var multi_refl_scores = selectClusters(image, median_picture, result, n_clusters, bands_threshold, clipping_geometry);
var multitemporal_score = ee.List.sequence(0, n_clusters.subtract(1)).map(function(i) {
return ee.Dictionary(multi_refl_scores.get(i)).get('multitemporalScore');
});
var multitemporal_score_IC = ee.ImageCollection(multitemporal_score);
var multitemporal_score_IMG = ee.Image(multitemporal_score_IC.iterate(mergeBands, ee.Image([0])));
var reflectance_score = ee.List.sequence(0, n_clusters.subtract(1)).map(function(i) {
return ee.Dictionary(multi_refl_scores.get(i)).get('reflectanceScore');
});
var reflectance_score_IC = ee.ImageCollection(reflectance_score);
var reflectance_score_IMG = ee.Image(reflectance_score_IC.iterate(mergeBands, ee.Image([0])));
// #Apply thresholds.
var cloud_score_threshold = ee.Image(ee.Algorithms.If(threshold_reflectance.lte(0),
cloud_score_threshold = multitemporal_score_IMG.gt(threshold_dif_cloud),
cloud_score_threshold = multitemporal_score_IMG.gt(threshold_dif_cloud).multiply(reflectance_score_IMG.gt(threshold_reflectance))));
// #Apply openings.
var kernel = ee.Kernel.circle(growing_ratio);
cloud_score_threshold = cloud_score_threshold.focal_min({kernel: kernel}).focal_max({kernel: kernel});
// #Mask out non-cloud pixels.
cloud_score_threshold = cloud_score_threshold.mask(cloud_score_threshold.eq(1));
// #Add metadata, namely imageID.
cloud_score_threshold = cloud_score_threshold.set(
'image_id', imageID,
'full_image_id', fullImageID,
'sensing_date', imageDate);
return cloud_score_threshold; // Final Cloud Score
};
exports.selectCM = selectCM;
exports.clipImage = clipImage;
exports.newCPP = newCPP;
exports.getRidOfDoubles = getRidOfDoubles;
exports.computeNormalizationFeatureCollection = computeNormalizationFeatureCollection;
exports.applyNormalizationImage = applyNormalizationImage;
exports.selectClusters = selectClusters;
exports.mergeBands = mergeBands;
exports.cloudMasks2Image = cloudMasks2Image;
exports.constructCloudMask = constructCloudMask;
exports.clipSingleImage = clipSingleImage;