@@ -99,25 +99,28 @@ def compute_hand_mask(
9999 return mask_hand
100100
101101
102- def get_random_indexes_from_masks (nb_indexes , mask_1 , mask_2 ):
102+ def get_random_indexes_from_masks (
103+ nb_samples : int , mask_1 : np .ndarray , mask_2 : np .ndarray
104+ ):
103105 """
104106 Get random valid indexes from masks.
105- Mask 1 is a validity mask
107+ :param int nb_samples : number of indices to count
108+ :param np.ndarray Mask 1 is a validity mask - shape (height, width)
109+ :param np.ndarray Mask 2 is the reference data mask - shape (height, width)
106110 """
107111 np .random .seed (712 ) # reproductible results
108112 rows_idxs = []
109113 cols_idxs = []
110114
111- if nb_indexes != 0 :
115+ if nb_samples != 0 :
112116 nb_idxs = 0
113117
114118 height = mask_1 .shape [0 ]
115119 width = mask_1 .shape [1 ]
116120
117- while nb_idxs < nb_indexes :
121+ while nb_idxs < nb_samples :
118122 row = np .random .randint (0 , height )
119123 col = np .random .randint (0 , width )
120-
121124 if mask_1 [row , col ] and mask_2 [row , col ]:
122125 rows_idxs .append (row )
123126 cols_idxs .append (col )
@@ -126,19 +129,21 @@ def get_random_indexes_from_masks(nb_indexes, mask_1, mask_2):
126129 return rows_idxs , cols_idxs
127130
128131
129- def get_grid_indexes_from_mask (nb_samples , valid_mask , mask_ground_truth ):
132+ def get_grid_indexes_from_mask (
133+ nb_samples : int , valid_mask : np .ndarray , mask_ground_truth : np .ndarray
134+ ):
130135 """
131136 Retrieve row and columns indices selected on the valid pixel of the image
132137
133138 :param int nb_samples: number of samples selected
134- :param boolean numpy array valid_mask :
135- :param boolean numpy array mask_ground_truth :
139+ :param boolean numpy array valid_mask : shape (height, width)
140+ :param boolean numpy array mask_ground_truth : shape (height, width)
136141 :return: tuple of list , row indices and columns indices
137142 """
138143 valid_samples = np .logical_and (mask_ground_truth , valid_mask ).astype (
139144 np .uint8
140145 )
141- _ , rows , cols = np .where (valid_samples )
146+ rows , cols = np .where (valid_samples )
142147
143148 if 1 <= nb_samples <= len (rows ):
144149 # np.arange(0, len(rows) -1, ...) : to be sure to exclude index len(rows)
@@ -156,30 +161,30 @@ def get_grid_indexes_from_mask(nb_samples, valid_mask, mask_ground_truth):
156161 return s_rows , s_cols
157162
158163
159- def get_smart_indexes_from_mask (nb_indexes , pct_area , minimum , mask ):
164+ def get_smart_indexes_from_mask (nb_samples , pct_area , minimum , mask ):
160165 """
161166 Retrieve row and columns indices selected on the valid pixel of the image
162167
163- :param int nb_indexes : number of samples selected
168+ :param int nb_samples : number of samples selected
164169 :param int pct_area: importance of area for selecting number of samples in each water surface
165170 :param int minimum: minimum number of samples in each water surface
166- :param boolean numpy array mask:
171+ :param boolean numpy array mask: validity mask (shape (height, width))
167172 :return: tuple of list , row indices and columns indices
168173
169174 """
170175 rows_idxs = []
171176 cols_idxs = []
172177
173- if nb_indexes != 0 :
178+ if nb_samples != 0 :
174179 img_labels , nb_labels = label (mask , return_num = True )
175180 props = regionprops (img_labels )
176181 mask_area = float (np .sum (mask ))
177182
178183 # number of samples for each label/prop
179- n1_indexes = int ((1.0 - pct_area / 100.0 ) * nb_indexes / nb_labels )
184+ n1_indexes = int ((1.0 - pct_area / 100.0 ) * nb_samples / nb_labels )
180185
181186 # number of samples to distribute to each label/prop
182- n2_indexes = pct_area / 100.0 * nb_indexes / mask_area
187+ n2_indexes = pct_area / 100.0 * nb_samples / mask_area
183188
184189 for prop in props :
185190 n3_indexes = n1_indexes + int (n2_indexes * prop .area )
@@ -216,22 +221,32 @@ def build_samples(
216221 :param dict params: dictionary of arguments
217222 :returns: Retrieve number of pixels for each class
218223 """
224+ # Pixels are valid water pixels if is known in Pekel, above NDWI threshold
225+ # and not masked in the input image (NO_DATA, clouds, etc.)
226+ #
227+ # Input validity mask has shape (1, heigth, width) to be consistent
228+ # with other raster data (channel, heigth, width)
229+ # But we propagate a simple boolean mask (heigth, width)
230+ validity_mask = (input_buffer [0 ] == 0 )[0 ]
219231 valid_water_pixels = np .logical_and (
220232 input_buffer [2 ], input_buffer [5 ] > params ["ndwi_threshold" ]
221233 )
234+ valid_water_pixels = np .logical_and (valid_water_pixels , validity_mask )
222235
223- nb_water_subset = np .count_nonzero (
224- np .logical_and (valid_water_pixels , input_buffer [0 ])
225- )
236+ nb_water_subset = np .count_nonzero (valid_water_pixels )
226237
227238 # valid pixels for 'other' (every thing but water) : valid mask + hand > 0
228239 # This criteria should be reconsidered
229240 # (ie : think of a relative threshold to select samples not too far from water)
230241 nb_valid_pix_other = np .count_nonzero (
231- np .logical_and (input_buffer [ 0 ] , input_buffer [1 ])
242+ np .logical_and (validity_mask , input_buffer [1 ])
232243 )
233244 nb_other_subset = nb_valid_pix_other
234245
246+ logger .debug (
247+ f"DBG> { nb_water_subset = } { nb_other_subset = } { params ['nb_valid_water_pixels' ]= } "
248+ )
249+
235250 # Ratio of pixel class compare to the full image ratio
236251 water_ratio = nb_water_subset / params ["nb_valid_water_pixels" ]
237252 other_ratio = nb_other_subset / params ["nb_valid_other_pixels" ]
@@ -247,33 +262,33 @@ def build_samples(
247262 # Pekel samples
248263 if params ["samples_method" ] == "random" :
249264 rows_pekel , cols_pekel = get_random_indexes_from_masks (
250- nb_water_subsamples , input_buffer [ 0 ][ 0 ] , input_buffer [2 ][0 ]
265+ nb_water_subsamples , validity_mask , input_buffer [2 ][0 ]
251266 )
252267 # Hand samples, always random (currently)
253268 rows_hand , cols_hand = get_random_indexes_from_masks (
254- nb_other_subsamples , input_buffer [ 0 ][ 0 ] , input_buffer [1 ][0 ]
269+ nb_other_subsamples , validity_mask , input_buffer [1 ][0 ]
255270 )
256271
257272 elif params ["samples_method" ] == "smart" :
258273 rows_pekel , cols_pekel = get_smart_indexes_from_mask (
259274 nb_water_subsamples ,
260275 params ["smart_area_pct" ],
261276 params ["smart_minimum" ],
262- np .logical_and (input_buffer [2 ][0 ], input_buffer [ 0 ][ 0 ] ),
277+ np .logical_and (input_buffer [2 ][0 ], validity_mask ),
263278 )
264279 # Hand samples, always random (currently)
265280 rows_hand , cols_hand = get_random_indexes_from_masks (
266- nb_other_subsamples , input_buffer [ 0 ][ 0 ] , input_buffer [1 ][0 ]
281+ nb_other_subsamples , validity_mask , input_buffer [1 ][0 ]
267282 )
268283
269284 elif params ["samples_method" ] == "grid" :
270285 rows_pekel , cols_pekel = get_grid_indexes_from_mask (
271- nb_water_subsamples , input_buffer [ 0 ] , valid_water_pixels [0 ]
286+ nb_water_subsamples , validity_mask , valid_water_pixels [0 ]
272287 )
273288
274289 # Hand samples, always random (currently)
275290 rows_hand , cols_hand = get_random_indexes_from_masks (
276- nb_other_subsamples , input_buffer [ 0 ][ 0 ] , input_buffer [1 ][0 ]
291+ nb_other_subsamples , validity_mask , input_buffer [1 ][0 ]
277292 )
278293
279294 else :
@@ -306,15 +321,13 @@ def rf_prediction(
306321 :returns: predicted mask
307322 """
308323 im_stack = np .concatenate ((input_buffer [1 :]), axis = 0 )
309- valid_mask = input_buffer [0 ]. astype ( bool )
310- buffer_to_predict = np .transpose (im_stack [:, valid_mask [ 0 ]. astype ( bool ) ])
324+ valid_mask = np . logical_not ( input_buffer [0 ][ 0 ] )
325+ buffer_to_predict = np .transpose (im_stack [:, valid_mask ])
311326
312327 classifier = params ["classifier" ]
313- prediction = np .zeros (valid_mask [0 ].shape , dtype = np .uint8 )
328+ prediction = np .zeros (im_stack [0 ].shape , dtype = np .uint8 )
314329 if buffer_to_predict .shape [0 ] > 0 : # not only NO DATA
315- prediction [valid_mask [0 ].astype (bool )] = classifier .predict (
316- buffer_to_predict
317- )
330+ prediction [valid_mask ] = classifier .predict (buffer_to_predict )
318331
319332 utils .display_mem_usage (
320333 params ["debug" ],
@@ -411,11 +424,11 @@ def post_process(
411424 ).astype (np .uint8 )
412425
413426 # Add nodata in im_classif
414- im_classif [np .logical_not (input_buffer [3 ])] = NODATA_INT8
427+ im_classif [np .where (input_buffer [3 ] != 0 )] = NODATA_INT8
415428 im_classif [im_classif == 1 ] = params ["value_classif" ]
416429
417430 im_predict = input_buffer [0 ]
418- im_predict [np .logical_not (input_buffer [3 ])] = NODATA_INT8
431+ im_predict [np .where (input_buffer [3 ] != 0 )] = NODATA_INT8
419432 im_predict [im_predict == 1 ] = params ["value_classif" ]
420433
421434 return [im_predict , im_classif ]
@@ -619,10 +632,14 @@ def nominal_case_predict(
619632 ]
620633 # Sample selection
621634 valid_stack = eoscale_manager .get_array (key_valid_stack )
622- nb_valid_pixels = np .count_nonzero (valid_stack )
635+ nb_valid_pixels = len (
636+ np .where (valid_stack == 0 )[0 ]
637+ ) # np."count_zero"(valid_stack)
638+
623639 args .nb_valid_water_pixels = np .count_nonzero (
624- np .logical_and (local_mask_pekel , valid_stack )
640+ np .logical_and (local_mask_pekel , valid_stack == 0 )
625641 )
642+
626643 args .nb_valid_other_pixels = nb_valid_pixels - args .nb_valid_water_pixels
627644 input_for_samples = [
628645 key_valid_stack ,
0 commit comments