Skip to content

Commit 84010d0

Browse files
committed
Use new convention for validity mask in prepare + shadow mask
1 parent 33e100c commit 84010d0

1 file changed

Lines changed: 52 additions & 35 deletions

File tree

slurp/masks/watermask.py

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)