Skip to content

Commit

Permalink
Fixes SpatialResizeTask that switched width/height when using resolut…
Browse files Browse the repository at this point in the history
…ion (#544)

* Fixes SpatialResizeTask bug that switched  width/height when using resolution

* Updates docstring of SpatialResizeTask with MR suggestions.

* Updates docstring alignment for auto-generated docs.

* Updated docstrings with multi-line bulleted list formatting.
  • Loading branch information
veseln committed Jan 20, 2023
1 parent 99b6cf3 commit d288629
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 26 deletions.
32 changes: 20 additions & 12 deletions features/eolearn/features/feature_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import datetime as dt
import logging
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union, cast
from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast

import numpy as np
from geopandas import GeoDataFrame
Expand Down Expand Up @@ -252,38 +252,46 @@ class SpatialResizeTask(EOTask):
def __init__(
self,
*,
resize_parameters: Tuple[ResizeParam, Tuple[float, float]],
resize_type: ResizeParam,
height_param: float,
width_param: float,
features: FeaturesSpecification = ...,
resize_method: ResizeMethod = ResizeMethod.LINEAR,
resize_library: ResizeLib = ResizeLib.PIL,
):
"""
:param features: Which features to resize. Supports new names for features.
:param resize_parameters: Instructions on how to perform the resizing process. For example use:
`['new_size', [500, 1000]]` to resize the data to size (500, 1000),
`['resolution', [10, 20]]` for changing resolution from 10 to 20,
`['scale_factors', [3, 3]]` to make the data three times larger.
:param resize_type: Determines type of resizing process and how `width_param` and `height_param` are used.
Options:
* `new_size`: Resizes data to size (width_param, height_param)
* | `resolution`: Resizes the data to have width_param, height_param resolution over width/height axis.
| Uses EOPatch bbox to compute.
* | `scale_factor` Resizes the data by scaling the width and height by a factor set by
| width_param and height_param respectively.
:param height_param: Parameter to be applied to the height in combination with the resize_type
:param width_param: Parameter to be applied to the width in combination with the resize_type
:param resize_method: Interpolation method used for resizing.
:param resize_library: Which Python library to use for resizing. Default is PIL, as it supports all dtypes and
features anti-aliasing. For cases where execution speed is crucial one can use CV2.
"""
self.features = features
self.parameter_kind = ResizeParam(resize_parameters[0])
self.parameter_values = resize_parameters[1]
self.resize_type = ResizeParam(resize_type)
self.height_param = height_param
self.width_param = width_param

self.resize_function = partial(
spatially_resize_image, resize_method=resize_method, resize_library=resize_library
)

def execute(self, eopatch: EOPatch) -> EOPatch:
resize_fun_kwargs: Dict[str, Any]
if self.parameter_kind == ResizeParam.RESOLUTION:
if self.resize_type == ResizeParam.RESOLUTION:
if not eopatch.bbox:
raise ValueError("Resolution-specified resizing can only be done on EOPatches with a defined BBox.")
new_size = bbox_to_dimensions(eopatch.bbox, self.parameter_values)
resize_fun_kwargs = {ResizeParam.NEW_SIZE.value: new_size}
new_width, new_height = bbox_to_dimensions(eopatch.bbox, (self.width_param, self.height_param))
resize_fun_kwargs = {ResizeParam.NEW_SIZE.value: (new_height, new_width)}
else:
resize_fun_kwargs = {self.parameter_kind.value: self.parameter_values}
resize_fun_kwargs = {self.resize_type.value: (self.height_param, self.width_param)}

for ftype, fname, new_name in self.parse_renamed_features(self.features, eopatch=eopatch):
if ftype.is_spatial() and ftype.is_raster():
Expand Down
36 changes: 22 additions & 14 deletions features/eolearn/tests/test_feature_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,29 +231,37 @@ def test_linear_function_task():


@pytest.mark.parametrize(
["resize_parameters", "features_call", "features_check", "outputs"],
["resize_type", "height_param", "width_param", "features_call", "features_check", "outputs"],
[
((ResizeParam.NEW_SIZE, (50, 70)), ("data", "CLP"), ("data", "CLP"), (68, 50, 70, 1)),
((ResizeParam.NEW_SIZE, (50, 70)), ("data", "CLP"), ("mask", "CLM"), (68, 101, 100, 1)),
((ResizeParam.NEW_SIZE, (50, 70)), ..., ("data", "CLP"), (68, 50, 70, 1)),
((ResizeParam.NEW_SIZE, (50, 70)), ..., ("mask", "CLM"), (68, 50, 70, 1)),
((ResizeParam.NEW_SIZE, (50, 70)), ("data", "CLP", "CLP_small"), ("data", "CLP_small"), (68, 50, 70, 1)),
((ResizeParam.NEW_SIZE, (50, 70)), ("data", "CLP", "CLP_small"), ("data", "CLP"), (68, 101, 100, 1)),
((ResizeParam.SCALE_FACTORS, (2, 2)), ("data", "CLP"), ("data", "CLP"), (68, 202, 200, 1)),
((ResizeParam.SCALE_FACTORS, (0.1, 0.1)), ("data", "CLP"), ("data", "CLP"), (68, 10, 10, 1)),
((ResizeParam.RESOLUTION, (5, 5)), ("data", "CLP"), ("data", "CLP"), (68, 200, 202, 1)),
((ResizeParam.RESOLUTION, (20, 20)), ("data", "CLP"), ("data", "CLP"), (68, 50, 50, 1)),
(ResizeParam.NEW_SIZE, 50, 70, ("data", "CLP"), ("data", "CLP"), (68, 50, 70, 1)),
(ResizeParam.NEW_SIZE, 50, 70, ("data", "CLP"), ("mask", "CLM"), (68, 101, 100, 1)),
(ResizeParam.NEW_SIZE, 50, 70, ..., ("data", "CLP"), (68, 50, 70, 1)),
(ResizeParam.NEW_SIZE, 50, 70, ..., ("mask", "CLM"), (68, 50, 70, 1)),
(ResizeParam.NEW_SIZE, 50, 70, ("data", "CLP", "CLP_small"), ("data", "CLP_small"), (68, 50, 70, 1)),
(ResizeParam.NEW_SIZE, 50, 70, ("data", "CLP", "CLP_small"), ("data", "CLP"), (68, 101, 100, 1)),
(ResizeParam.SCALE_FACTORS, 2, 2, ("data", "CLP"), ("data", "CLP"), (68, 202, 200, 1)),
(ResizeParam.SCALE_FACTORS, 0.5, 2, ("data", "CLP"), ("data", "CLP"), (68, 50, 200, 1)),
(ResizeParam.SCALE_FACTORS, 0.1, 0.1, ("data", "CLP"), ("data", "CLP"), (68, 10, 10, 1)),
(ResizeParam.RESOLUTION, 5, 5, ("data", "CLP"), ("data", "CLP"), (68, 202, 200, 1)),
(ResizeParam.RESOLUTION, 20, 20, ("data", "CLP"), ("data", "CLP"), (68, 50, 50, 1)),
(ResizeParam.RESOLUTION, 5, 20, ("data", "CLP"), ("data", "CLP"), (68, 202, 50, 1)),
],
)
@pytest.mark.filterwarnings("ignore::RuntimeWarning")
def test_spatial_resize_task(example_eopatch, resize_parameters, features_call, features_check, outputs):
def test_spatial_resize_task(
example_eopatch, resize_type, height_param, width_param, features_call, features_check, outputs
):
# Warnings occur due to lossy casting in the downsampling procedure

resize = SpatialResizeTask(resize_parameters=resize_parameters, features=features_call)
resize = SpatialResizeTask(
resize_type=resize_type, height_param=height_param, width_param=width_param, features=features_call
)
assert resize(example_eopatch)[features_check].shape == outputs


def test_spatial_resize_task_exception(example_eopatch):
with pytest.raises(ValueError):
resize_wrong_param = SpatialResizeTask(features=("mask", "CLM"), resize_parameters=("blabla", (20, 20)))
resize_wrong_param = SpatialResizeTask(
features=("mask", "CLM"), resize_type="blabla", height_param=20, width_param=20
)
resize_wrong_param(example_eopatch)

0 comments on commit d288629

Please sign in to comment.