Skip to content

Commit 3108dea

Browse files
authored
Set new LK defaults (#151)
* Set default value for epsilon from 5 to 10 * Set default value for buffer_mask from 0 to 5 * Change function name to lowercase to comply with pep8 * Adapt test for interfaces * Clean up docstrings
1 parent 2a14ac8 commit 3108dea

File tree

6 files changed

+53
-50
lines changed

6 files changed

+53
-50
lines changed

examples/LK_buffer_mask.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,22 @@
9797
# Sparse Lucas-Kanade
9898
# -------------------
9999
#
100-
# By setting the optional argument 'dense=False' in 'xy, uv = LK_optflow(.....)',
100+
# By setting the optional argument ``dense=False`` in ``xy, uv = dense_lucaskanade(...)``,
101101
# the LK algorithm returns the motion vectors detected by the Lucas-Kanade scheme
102102
# without interpolating them on the grid.
103103
# This allows us to better identify the presence of wrongly detected
104104
# stationary motion in areas where precipitation is leaving the domain (look
105105
# for the red dots within the blue circle in the figure below).
106106

107-
# get Lucas-Kanade optical flow method
108-
LK_optflow = motion.get_method("LK")
107+
# Get Lucas-Kanade optical flow method
108+
dense_lucaskanade = motion.get_method("LK")
109109

110110
# Mask invalid values
111111
R = np.ma.masked_invalid(R)
112112

113-
# Use default settings (i.e., no buffering of the radar mask)
114-
fd_kwargs1 = {"buffer_mask":0}
115-
xy, uv = LK_optflow(R, dense=False, fd_kwargs=fd_kwargs1)
113+
# Use no buffering of the radar mask
114+
fd_kwargs1 = {"buffer_mask" : 0}
115+
xy, uv = dense_lucaskanade(R, dense=False, fd_kwargs=fd_kwargs1)
116116
plt.imshow(ref_dbr, cmap=plt.get_cmap("Greys"))
117117
plt.imshow(mask, cmap=colors.ListedColormap(["black"]), alpha=0.5)
118118
plt.quiver(
@@ -127,22 +127,23 @@
127127
)
128128
circle = plt.Circle((620, 245), 100, color="b", clip_on=False, fill=False)
129129
plt.gca().add_artist(circle)
130-
plt.title("buffer_mask = 0 (default)")
130+
plt.title("buffer_mask = 0")
131131
plt.show()
132132

133133
################################################################################
134-
# By default, the LK algorithm considers missing values as no precipitation, i.e.,
134+
# The LK algorithm cannot distinguish missing values from no precipitation, that is,
135135
# no-data are the same as no-echoes. As a result, the fixed boundaries produced
136136
# by precipitation in contact with no-data areas are interpreted as stationary motion.
137137
# One way to mitigate this effect of the boundaries is to introduce a slight buffer
138138
# of the no-data mask so that the algorithm will ignore all the portions of the
139139
# radar domain that are nearby no-data areas.
140-
# This is achieved by passing the keyword argument 'buffer_mask = 10' within the
141-
# feature detection optional arguments 'fd_kwargs'.
140+
# This buffer can be set by the keyword argument ``buffer_mask`` within the
141+
# feature detection optional arguments ``fd_kwargs``.
142+
# Note that by default ``dense_lucaskanade`` uses a 5-pixel buffer.
142143

143144
# with buffer
144-
buffer = 10
145-
fd_kwargs2 = {"buffer_mask":buffer}
145+
buffer = 5
146+
fd_kwargs2 = {"buffer_mask" : buffer}
146147
xy, uv = LK_optflow(R, dense=False, fd_kwargs=fd_kwargs2)
147148
plt.imshow(ref_dbr, cmap=plt.get_cmap("Greys"))
148149
plt.imshow(mask, cmap=colors.ListedColormap(["black"]), alpha=0.5)
@@ -166,13 +167,16 @@
166167
# ------------------
167168
#
168169
# The above displacement vectors produced by the Lucas-Kanade method are now
169-
# interpolated to produce a full field of motion (i.e., 'dense=True').
170+
# interpolated to produce a full field of motion (i.e., ``dense=True``).
170171
# By comparing the velocity of the motion fields, we can easily notice
171172
# the negative bias that is introduced by the the erroneous interpretation of
172173
# velocities near the maximum range of the radars.
174+
# Please note that we are setting a small shape parameter ``epsilon`` for the
175+
# interpolation routine. This will produce a smoother motion field.
173176

174-
UV1 = LK_optflow(R, dense=True, fd_kwargs=fd_kwargs1)
175-
UV2 = LK_optflow(R, dense=True, fd_kwargs=fd_kwargs2)
177+
interp_kwargs = {"epsilon" : 5} # use a small shape parameter for interpolation
178+
UV1 = LK_optflow(R, dense=True, fd_kwargs=fd_kwargs1, interp_kwargs=interp_kwargs)
179+
UV2 = LK_optflow(R, dense=True, fd_kwargs=fd_kwargs2, interp_kwargs=interp_kwargs)
176180

177181
V1 = np.sqrt(UV1[0] ** 2 + UV1[1] ** 2)
178182
V2 = np.sqrt(UV2[0] ** 2 + UV2[1] ** 2)
@@ -183,16 +187,16 @@
183187
plt.show()
184188

185189
################################################################################
186-
# Notice that the default motion field can be significantly slower (more than 10%
187-
# slower) because of the presence of wrong stationary motion vectors at the edges.
190+
# Notice how the presence of erroneous velocity vectors produces a significantly
191+
# slower motion field near the right edge of the domain.
188192
#
189193
# Forecast skill
190194
# --------------
191195
#
192196
# We are now going to evaluate the benefit of buffering the radar mask by computing
193197
# the forecast skill in terms of the Spearman correlation coefficient.
194198
# The extrapolation forecasts are computed using the dense UV motion fields
195-
# estimted above.
199+
# estimated above.
196200

197201
# Get the advection routine and extrapolate the last radar frame by 12 time steps
198202
# (i.e., 1 hour lead time)

pysteps/tests/test_interfaces.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def test_utils_interface():
229229
('clip', dimension.clip_domain),
230230
('square', dimension.square_domain),
231231
('upscale', dimension.aggregate_fields_space),
232-
('shitomasi', images.ShiTomasi_detection),
232+
('shitomasi', images.shitomasi_detection),
233233
('morph_opening', images.morph_opening),
234234
('rbfinterp2d', interpolate.rbfinterp2d),
235235
('rapsd', spectral.rapsd),

pysteps/utils/cleansing.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ def decluster(coord, input_array, scale, min_samples=1, verbose=False):
3131
input_array : array_like
3232
Array of shape (n) or (n, m), where *n* is the number of samples and
3333
*m* the number of variables.
34-
All values in **input_array** are required to have finite values.
34+
All values in ``input_array`` are required to have finite values.
3535
3636
scale : float or array_like
37-
The **scale** parameter in the same units of **coord**.
37+
The ``scale`` parameter in the same units of ``coord``.
3838
It can be a scalar or an array_like of shape (d).
39-
Data points within the declustering **scale** are aggregated.
39+
Data points within the declustering ``scale`` are aggregated.
4040
4141
min_samples : int, optional
4242
The minimum number of samples for computing the median within a given
@@ -49,8 +49,8 @@ def decluster(coord, input_array, scale, min_samples=1, verbose=False):
4949
-------
5050
5151
out : tuple of ndarrays
52-
A two-element tuple (**out_coord**, **output_array**) containing the
53-
declustered coordinates (l, d) and **input_array** (l, m), where *l* is
52+
A two-element tuple (``out_coord``, ``output_array``) containing the
53+
declustered coordinates (l, d) and input array (l, m), where *l* is
5454
the new number of samples with *l* <= *n*.
5555
"""
5656

@@ -147,22 +147,21 @@ def detect_outliers(input_array, thr, coord=None, k=None, verbose=False):
147147
Array of shape (n) or (n, m), where *n* is the number of samples and
148148
*m* the number of variables. If *m* > 1, the Mahalanobis distance
149149
is used.
150-
All values in **input_array** are required to have finite values.
150+
All values in ``input_array`` are required to have finite values.
151151
152152
thr : float
153-
The number of standard deviations from the mean
154-
that defines an outlier.
153+
The number of standard deviations from the mean used to define an outlier.
155154
156155
coord : array_like, optional
157156
Array of shape (n, d) containing the coordinates of the input data into
158157
a space of *d* dimensions.
159-
Passing **coord** requires that **k** is not None.
158+
Passing ``coord`` requires that ``k`` is not None.
160159
161160
k : int or None, optional
162161
The number of nearest neighbours used to localize the outlier
163162
detection.
164163
If set to None (the default), it employs all the data points (global
165-
detection). Setting **k** requires that **coord** is not None.
164+
detection). Setting ``k`` requires that ``coord`` is not None.
166165
167166
verbose : bool, optional
168167
Print out information.
@@ -172,7 +171,7 @@ def detect_outliers(input_array, thr, coord=None, k=None, verbose=False):
172171
173172
out : array_like
174173
A 1-D boolean array of shape (n) with True values indicating the outliers
175-
detected in **input_array**.
174+
detected in ``input_array``.
176175
"""
177176

178177
input_array = np.copy(input_array)

pysteps/utils/images.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
.. autosummary::
1313
:toctree: ../generated/
1414
15-
ShiTomasi_detection
15+
shitomasi_detection
1616
morph_opening
1717
"""
1818

@@ -29,12 +29,12 @@
2929
CV2_IMPORTED = False
3030

3131

32-
def ShiTomasi_detection(input_image,
32+
def shitomasi_detection(input_image,
3333
max_corners=1000,
3434
quality_level=0.01,
3535
min_distance=10,
3636
block_size=5,
37-
buffer_mask=0,
37+
buffer_mask=5,
3838
use_harris=False,
3939
k=0.04,
4040
verbose=False,
@@ -75,27 +75,27 @@ def ShiTomasi_detection(input_image,
7575
valid pixels.
7676
7777
max_corners : int, optional
78-
The **maxCorners** parameter in the `Shi-Tomasi`_ corner detection
78+
The ``maxCorners`` parameter in the `Shi-Tomasi`_ corner detection
7979
method.
8080
It represents the maximum number of points to be tracked (corners).
8181
If set to zero, all detected corners are used.
8282
8383
quality_level : float, optional
84-
The **qualityLevel** parameter in the `Shi-Tomasi`_ corner detection
84+
The ``qualityLevel`` parameter in the `Shi-Tomasi`_ corner detection
8585
method.
8686
It represents the minimal accepted quality for the image corners.
8787
8888
min_distance : int, optional
89-
The **minDistance** parameter in the `Shi-Tomasi`_ corner detection
89+
The ``minDistance`` parameter in the `Shi-Tomasi`_ corner detection
9090
method.
9191
It represents minimum possible Euclidean distance in pixels between
9292
corners.
9393
9494
block_size : int, optional
95-
The **blockSize** parameter in the `Shi-Tomasi`_ corner detection
95+
The ``blockSize`` parameter in the `Shi-Tomasi`_ corner detection
9696
method.
9797
It represents the window size in pixels used for computing a derivative
98-
covariation matrix over each pixel neighborhood.
98+
covariation matrix over each pixel neighbourhood.
9999
100100
use_harris : bool, optional
101101
Whether to use a `Harris detector`_ or cornerMinEigenVal_.

pysteps/utils/interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def donothing(R, metadata=None, *args, **kwargs):
179179
methods_objects["upscale"] = dimension.aggregate_fields_space
180180

181181
# image processing methods
182-
methods_objects["shitomasi"] = images.ShiTomasi_detection
182+
methods_objects["shitomasi"] = images.shitomasi_detection
183183
methods_objects["morph_opening"] = images.morph_opening
184184

185185
# interpolation methods

pysteps/utils/interpolate.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def rbfinterp2d(
2222
xgrid,
2323
ygrid,
2424
rbfunction="gaussian",
25-
epsilon=5,
25+
epsilon=10,
2626
k=50,
2727
nchunks=5,
2828
):
@@ -43,27 +43,27 @@ def rbfinterp2d(
4343
Array of shape (n) or (n, m) containing the values of the data points,
4444
where *n* is the number of data points and *m* the number of co-located
4545
variables.
46-
All values in **input_array** are required to have finite values.
46+
All values in ``input_array`` are required to have finite values.
4747
4848
xgrid, ygrid : array_like
4949
1D arrays representing the coordinates of the 2-D output grid.
5050
51-
rbfunction : {"gaussian", "multiquadric", "inverse quadratic",
52-
"inverse multiquadric", "bump"}, optional
51+
rbfunction : {"gaussian", "multiquadric", "inverse quadratic", "inverse multiquadric", "bump"}, optional
5352
The name of one of the available radial basis function based on a
54-
normalized Euclidian norm.
53+
normalized Euclidian norm as defined in the **Notes** section below.
5554
56-
See also the Notes section below.
55+
More details provided in the wikipedia reference page linked below.
5756
5857
epsilon : float, optional
5958
The shape parameter used to scale the input to the radial kernel.
6059
61-
A smaller value for **epsilon** produces a smoother interpolation. More
62-
details provided in the wikipedia reference page.
60+
A smaller value for ``epsilon`` produces a smoother interpolation. More
61+
details provided in the wikipedia reference page linked below.
6362
6463
k : int or None, optional
65-
The number of nearest neighbours used to speed-up the interpolation.
66-
If set to None, it interpolates based on all the data points.
64+
The number of nearest neighbours used for each target location.
65+
This can also be useful to to speed-up the interpolation.
66+
If set to None, it interpolates using all the data points at once.
6767
6868
nchunks : int, optional
6969
The number of chunks in which the grid points are split to limit the
@@ -73,7 +73,7 @@ def rbfinterp2d(
7373
-------
7474
7575
output_array : ndarray_
76-
The interpolated field(s) having shape (m, ygrid.size, xgrid.size).
76+
The interpolated field(s) having shape (*m*, ``ygrid.size``, ``xgrid.size``).
7777
7878
Notes
7979
-----

0 commit comments

Comments
 (0)