Skip to content

Commit 5631232

Browse files
author
Anze Zupanc
committed
Added control over what value should be assigned to cloud probability map and mask in the case of non valid data pixels.
1 parent 59ead2b commit 5631232

File tree

2 files changed

+56
-24
lines changed

2 files changed

+56
-24
lines changed

s2cloudless/S2PixelCloudDetector.py

+42-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import numpy as np
22
import os.path
3+
import copy
34
from .PixelClassifier import PixelClassifier
45
from skimage.morphology import disk, dilation
56
from scipy.ndimage.filters import convolve
67

8+
from sentinelhub import CustomUrlParam
9+
710
from sklearn.externals import joblib
811

912

@@ -130,7 +133,9 @@ class CloudMaskRequest:
130133
131134
The user can then efficiently derive binary clouds masks based on a threshold.
132135
133-
:param ogc_request: An instance of WmsRequest or WcsRequest (defined in sentinelhub-py package).
136+
:param ogc_request: An instance of WmsRequest or WcsRequest (defined in sentinelhub-py package). The cloud mask
137+
request creates a copy of this request and sets two custom url parameters: turns of the logo and
138+
adds a request for transparency layer, which is used to determine the (non) valid data pixels.
134139
:type ogc_request: data_request.WmsRequest or data_request.WcsRequest
135140
:param threshold: Defines cloud and non-cloud in the binary cloud mask.
136141
:type threshold: float
@@ -156,10 +161,21 @@ def __init__(self, ogc_request, *, threshold=0.4, average_over=4,
156161

157162
self.cloud_detector = S2PixelCloudDetector(threshold=threshold, average_over=average_over, all_bands=all_bands,
158163
dilation_size=dilation_size, model_filename=model_filename)
159-
self.ogc_bands_request = ogc_request
160164

165+
self.ogc_bands_request = copy.deepcopy(ogc_request)
166+
# Add the transparency layer to the request
167+
# Transparency layer is used to determine valid data points
168+
if self.ogc_bands_request.custom_url_params is None:
169+
self.ogc_bands_request.custom_url_params = {CustomUrlParam.SHOWLOGO: False,
170+
CustomUrlParam.TRANSPARENT: True}
171+
else:
172+
self.ogc_bands_request.custom_url_params.update({CustomUrlParam.SHOWLOGO: False,
173+
CustomUrlParam.TRANSPARENT: True})
174+
175+
self.ogc_bands_request.create_request()
161176
self.bands = None
162177
self.probability_masks = None
178+
self.valid_data = None
163179

164180
def __len__(self):
165181
return len(self.bands)
@@ -178,14 +194,19 @@ def get_dates(self):
178194
"""
179195
return self.ogc_bands_request.get_dates()
180196

181-
def get_probability_masks(self):
197+
def get_probability_masks(self, non_valid_value=0):
182198
"""
183-
Get probability maps of areas for each available date.
199+
Get probability maps of areas for each available date. The pixels without valid data are assigned
200+
non_valid_value.
201+
202+
:param non_valid_value: Value to be assigned to non valid data pixels
203+
184204
:return: np.ndarray
185205
"""
186206
if self.probability_masks is None:
187207
self.get_data()
188-
self.probability_masks = self.cloud_detector.get_cloud_probability_maps(np.array(self.bands))
208+
self.probability_masks = self.cloud_detector.get_cloud_probability_maps(self.bands)
209+
self.probability_masks[self.valid_data == False] = non_valid_value
189210
return self.probability_masks
190211

191212
def get_data(self):
@@ -194,11 +215,22 @@ def get_data(self):
194215
:return: np.ndarray
195216
"""
196217
if self.bands is None:
197-
self.bands = self.ogc_bands_request.get_data()
218+
# last 'band' is the transperency layer
219+
data = np.asarray(self.ogc_bands_request.get_data())
220+
self.bands = data[..., :-1]
221+
self.valid_data = (data[..., -1] > 0.5).astype(np.bool)
198222
return self.bands
223+
224+
def get_valid_data(self):
225+
"""
226+
Returns valid data mask.
227+
228+
:return: np.ndarray
229+
"""
199230

200-
def get_cloud_masks(self, threshold=None):
201-
""" The binary cloud mask is computed on the fly. Be cautious.
231+
def get_cloud_masks(self, threshold=None, non_valid_value=False):
232+
""" The binary cloud mask is computed on the fly. Be cautious. The pixels without valid data are assigned
233+
non_valid_value.
202234
203235
:param threshold: A float from [0,1] specifying threshold
204236
:return: Binary cloud masks
@@ -218,4 +250,6 @@ def get_cloud_masks(self, threshold=None):
218250
[dilation(cloud_mask, self.cloud_detector.dilation_filter) for cloud_mask in cloud_masks],
219251
dtype=np.int8)
220252

253+
cloud_masks[self.valid_data == False] = non_valid_value
254+
221255
return cloud_masks

setup.py

+14-16
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
from setuptools import setup, find_packages
22

3-
__version__ = '1.0.1'
3+
__version__ = '1.1.0'
44

5-
setup(
6-
name='s2cloudless',
7-
version=__version__,
8-
description='Sentinel Hub\'s cloud detector for Sentinel-2 imagery',
9-
url='https://github.com/sentinel-hub/sentinel2-cloud-detector',
10-
author='Anze Zupanc',
11-
author_email='[email protected]',
12-
license='CC-BY-SA-4.0',
13-
packages=find_packages('.'),
14-
package_dir={'': '.'},
15-
package_data={'s2cloudless': ['models/pixel_s2_cloud_detector_lightGBM_v0.1.joblib.dat',
16-
'TestInputs/input_arrays.npz']},
17-
install_requires=['numpy', 'scipy', 'scikit-learn', 'scikit-image', 'matplotlib', 'lightgbm'],
18-
zip_safe=False
19-
)
5+
setup(name='s2cloudless',
6+
version=__version__,
7+
description='Sentinel Hub\'s cloud detector for Sentinel-2 imagery',
8+
url='https://github.com/sentinel-hub/sentinel2-cloud-detector',
9+
author='Anze Zupanc',
10+
author_email='[email protected]',
11+
packages=find_packages('.'),
12+
package_dir={'':'.'},
13+
package_data={'s2cloudless': ['models/pixel_s2_cloud_detector_lightGBM_v0.1.joblib.dat',
14+
'TestInputs/input_arrays.npz']},
15+
install_requires=['numpy', 'scipy', 'scikit-learn', 'scikit-image', 'matplotlib', 'lightgbm',
16+
'sentinelhub'],
17+
zip_safe=False)

0 commit comments

Comments
 (0)