Skip to content
Snippets Groups Projects

Extended filenames as dict

Merged Rémi Cresson requested to merge 102-pythonic_ext_fname into develop
All threads resolved!
Compare and Show latest version
2 files
+ 172
174
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 127
173
@@ -87,7 +87,7 @@ class OTBObject(ABC):
return App("ComputeImagesStatistics", self, quiet=True).data
def get_values_at_coords(
self, row: int, col: int, bands: int = None
self, row: int, col: int, bands: int = None
) -> list[int | float] | int | float:
"""Get pixel value(s) at a given YX coordinates.
@@ -101,8 +101,7 @@ class OTBObject(ABC):
"""
channels = []
app = App("PixelValue", self, coordx=col, coordy=row, frozen=True,
quiet=True)
app = App("PixelValue", self, coordx=col, coordy=row, frozen=True, quiet=True)
if bands is not None:
if isinstance(bands, int):
if bands < 0:
@@ -116,8 +115,7 @@ class OTBObject(ABC):
)
if channels:
app.app.Execute()
app.set_parameters(
{"cl": [f"Channel{n + 1}" for n in channels]})
app.set_parameters({"cl": [f"Channel{n + 1}" for n in channels]})
app.execute()
data = literal_eval(app.app.GetParameterString("value"))
return data[0] if len(channels) == 1 else data
@@ -126,10 +124,8 @@ class OTBObject(ABC):
"""Get list of channels to read values at, from a slice."""
nb_channels = self.shape[2]
start, stop, step = bands.start, bands.stop, bands.step
start = nb_channels + start if isinstance(start,
int) and start < 0 else start
stop = nb_channels + stop if isinstance(stop,
int) and stop < 0 else stop
start = nb_channels + start if isinstance(start, int) and start < 0 else start
stop = nb_channels + stop if isinstance(stop, int) and stop < 0 else stop
step = 1 if step is None else step
if start is not None and stop is not None:
return list(range(start, stop, step))
@@ -144,7 +140,7 @@ class OTBObject(ABC):
)
def export(
self, key: str = None, preserve_dtype: bool = True
self, key: str = None, preserve_dtype: bool = True
) -> dict[str, dict[str, np.ndarray]]:
"""Export a specific output image as numpy array and store it in object exports_dic.
@@ -162,15 +158,13 @@ class OTBObject(ABC):
if key not in self.exports_dic:
self.exports_dic[key] = self.app.ExportImage(key)
if preserve_dtype:
self.exports_dic[key]["array"] = self.exports_dic[key][
"array"].astype(
self.exports_dic[key]["array"] = self.exports_dic[key]["array"].astype(
self.dtype
)
return self.exports_dic[key]
def to_numpy(
self, key: str = None, preserve_dtype: bool = True,
copy: bool = False
self, key: str = None, preserve_dtype: bool = True, copy: bool = False
) -> np.ndarray:
"""Export a pyotb object to numpy array.
@@ -199,8 +193,7 @@ class OTBObject(ABC):
profile = {}
array = self.to_numpy(preserve_dtype=True, copy=False)
proj = self.app.GetImageProjection(self.output_image_key)
profile.update(
{"crs": proj, "dtype": array.dtype, "transform": self.transform})
profile.update({"crs": proj, "dtype": array.dtype, "transform": self.transform})
height, width, count = array.shape
profile.update({"count": count, "height": height, "width": width})
return np.moveaxis(array, 2, 0), profile
@@ -301,8 +294,7 @@ class OTBObject(ABC):
"""Logical or."""
return self.__create_operator(LogicalOperation, "||", self, other)
def __and__(self,
other: OTBObject | str | int | float) -> LogicalOperation:
def __and__(self, other: OTBObject | str | int | float) -> LogicalOperation:
"""Logical and."""
return self.__create_operator(LogicalOperation, "&&", self, other)
@@ -398,7 +390,7 @@ class OTBObject(ABC):
return self.get_values_at_coords(row, col, channels)
# Slicing
if not isinstance(key, tuple) or (
isinstance(key, tuple) and (len(key) < 2 or len(key) > 3)
isinstance(key, tuple) and (len(key) < 2 or len(key) > 3)
):
raise ValueError(
f'"{key}" cannot be interpreted as valid slicing. Slicing should be 2D or 3D.'
@@ -454,13 +446,13 @@ class App(OTBObject):
]
def __init__(
self,
appname: str,
*args,
frozen: bool = False,
quiet: bool = False,
name: str = "",
**kwargs,
self,
appname: str,
*args,
frozen: bool = False,
quiet: bool = False,
name: str = "",
**kwargs,
):
"""Common constructor for OTB applications. Handles in-memory connection between apps.
@@ -510,9 +502,8 @@ class App(OTBObject):
self.set_parameters(*args, **kwargs)
# Create Output image objects
for key in filter(
lambda k: self._out_param_types[
k] == otb.ParameterType_OutputImage,
self._out_param_types,
lambda k: self._out_param_types[k] == otb.ParameterType_OutputImage,
self._out_param_types,
):
self.outputs[key] = Output(self, key, self._settings.get(key))
if not self.frozen:
@@ -533,8 +524,7 @@ class App(OTBObject):
@property
def parameters(self):
"""Return used OTB application parameters."""
return {**self._auto_parameters, **self.app.GetParameters(),
**self._settings}
return {**self._auto_parameters, **self.app.GetParameters(), **self._settings}
@property
def exports_dic(self) -> dict[str, dict]:
@@ -544,8 +534,7 @@ class App(OTBObject):
def __is_one_of_types(self, key: str, param_types: list[int]) -> bool:
"""Helper to factor is_input and is_output."""
if key not in self._all_param_types:
raise KeyError(
f"key {key} not found in the application parameters types")
raise KeyError(f"key {key} not found in the application parameters types")
return self._all_param_types[key] in param_types
def is_input(self, key: str) -> bool:
@@ -558,8 +547,7 @@ class App(OTBObject):
True if the parameter is an input, else False
"""
return self.__is_one_of_types(key=key,
param_types=self.INPUT_PARAM_TYPES)
return self.__is_one_of_types(key=key, param_types=self.INPUT_PARAM_TYPES)
def is_output(self, key: str) -> bool:
"""Returns True if the key is an output.
@@ -571,8 +559,7 @@ class App(OTBObject):
True if the parameter is an output, else False
"""
return self.__is_one_of_types(key=key,
param_types=self.OUTPUT_PARAM_TYPES)
return self.__is_one_of_types(key=key, param_types=self.OUTPUT_PARAM_TYPES)
def is_key_list(self, key: str) -> bool:
"""Check if a parameter key is an input parameter list."""
@@ -696,8 +683,7 @@ class App(OTBObject):
dtype = get_pixel_type(param)
except (TypeError, RuntimeError):
logger.warning(
'%s: unable to identify pixel type of key "%s"', self.name,
param
'%s: unable to identify pixel type of key "%s"', self.name, param
)
return
if target_key:
@@ -713,8 +699,7 @@ class App(OTBObject):
def execute(self):
"""Execute and write to disk if any output parameter has been set during init."""
logger.debug("%s: run execute() with parameters=%s", self.name,
self.parameters)
logger.debug("%s: run execute() with parameters=%s", self.name, self.parameters)
self._time_start = perf_counter()
try:
self.app.Execute()
@@ -742,36 +727,28 @@ class App(OTBObject):
self._time_end = perf_counter()
def write(
self,
path: str | Path | dict[str, str] = None,
pixel_type: dict[str, str] | str = None,
preserve_dtype: bool = False,
ext_fname: str | dict[str, str] | dict[str, int] | dict[
str, float] = None,
**kwargs,
self,
path: str | Path | dict[str, str] = None,
pixel_type: dict[str, str] | str = None,
preserve_dtype: bool = False,
ext_fname: str = "",
**kwargs,
) -> bool:
"""Set output pixel type and write the output raster files.
Args:
path: Can be :
- filepath, useful when there is only one output, e.g.
'output.tif'
- dictionary containing key-arguments enumeration. Useful when
a key contains non-standard characters such as a point, e.g.
{'io.out':'output.tif'}
- None if output file was passed during App init
ext_fname: Optional, an extended filename as understood by OTB
(e.g. "&gdal:co:TILED=YES") or a dict of key/value. Will be
used for all outputs.
pixel_type: Can be :
- dictionary {out_param_key: pixeltype} when specifying for
several outputs
- str (e.g. 'uint16') or otbApplication.ImagePixelType_...
When there are several outputs, all outputs are written with
this unique type. Valid pixel types are uint8, uint16, uint32,
int16, int32, float, double, cint16, cint32, cfloat, cdouble.
preserve_dtype: propagate main input pixel type to outputs, in
case pixel_type is None
path: Can be : - filepath, useful when there is only one output, e.g. 'output.tif'
- dictionary containing key-arguments enumeration. Useful when a key contains
non-standard characters such as a point, e.g. {'io.out':'output.tif'}
- None if output file was passed during App init
ext_fname: Optional, an extended filename as understood by OTB (e.g. "&gdal:co:TILED=YES")
Will be used for all outputs (Default value = "")
pixel_type: Can be : - dictionary {out_param_key: pixeltype} when specifying for several outputs
- str (e.g. 'uint16') or otbApplication.ImagePixelType_... When there are several
outputs, all outputs are written with this unique type.
Valid pixel types are uint8, uint16, uint32, int16, int32, float, double,
cint16, cint32, cfloat, cdouble. (Default value = None)
preserve_dtype: propagate main input pixel type to outputs, in case pixel_type is None
**kwargs: keyword arguments e.g. out='output.tif'
Returns:
@@ -790,10 +767,8 @@ class App(OTBObject):
elif isinstance(path, (str, Path)) and self.output_key:
kwargs.update({self.output_key: str(path)})
elif path is not None:
raise TypeError(
f"{self.name}: unsupported filepath type ({type(path)})")
if not (kwargs or any(
k in self._settings for k in self._out_param_types)):
raise TypeError(f"{self.name}: unsupported filepath type ({type(path)})")
if not (kwargs or any(k in self._settings for k in self._out_param_types)):
raise KeyError(
f"{self.name}: at least one filepath is required, if not provided during App init"
)
@@ -801,33 +776,34 @@ class App(OTBObject):
# Append filename extension to filenames
if ext_fname:
assert isinstance(ext_fname, (dict, str))
if not isinstance(ext_fname, (dict, str)):
raise ValueError("Extended filename must be a str or a dict")
def _str2dict(ext_str):
"""Function that converts str to dict."""
return dict(
kv for pair in ext_str.split("&")
if len(kv := pair.split("=")) == 2
)
splits = [pair.split("=") for pair in ext_str.split("&")]
return dict(split for split in splits if len(split) == 2)
if isinstance(ext_fname, str):
# transform str to dict
ext_fname = _str2dict(ext_fname)
gen_ext_fname = _str2dict(ext_fname) \
if isinstance(ext_fname, str) else ext_fname
logger.debug("%s: extended filename for outputs: %s", self.name)
for key, ext in ext_fname.items():
logger.debug("%s: extended filename for all outputs:", self.name)
for key, ext in gen_ext_fname.items():
logger.debug("%s: %s", key, ext)
for key, filepath in kwargs.items():
if self._out_param_types[key] == otb.ParameterType_OutputImage:
new_ext_fname = gen_ext_fname.copy()
# grab already set extended filename key/values
already_set_ext = _str2dict(filepath.split("?&")[0]) \
if "?&" in filepath else {}
if "?&" in filepath:
filepath, already_set_ext = filepath.split("?&", 1)
# extensions in filepath prevail over `new_ext_fname`
new_ext_fname.update(_str2dict(already_set_ext))
# transform dict to str
ext_fname_str = "&".join([
f"{k}={v}" for k, v in ext_fname.items()
if k not in already_set_ext
f"{key}={value}"
for key, value in new_ext_fname.items()
])
parameters[key] = f"{filepath}?&{ext_fname_str}"
@@ -838,17 +814,14 @@ class App(OTBObject):
dtype = parse_pixel_type(pixel_type)
type_name = self.app.ConvertPixelTypeToNumpy(dtype)
logger.debug(
'%s: output(s) will be written with type "%s"', self.name,
type_name
'%s: output(s) will be written with type "%s"', self.name, type_name
)
for key in parameters:
if self._out_param_types[
key] == otb.ParameterType_OutputImage:
if self._out_param_types[key] == otb.ParameterType_OutputImage:
data_types[key] = dtype
elif isinstance(pixel_type, dict):
data_types = {
key: parse_pixel_type(dtype) for key, dtype in
pixel_type.items()
key: parse_pixel_type(dtype) for key, dtype in pixel_type.items()
}
elif preserve_dtype:
self.propagate_dtype() # all outputs will have the same type as the main input raster
@@ -881,8 +854,7 @@ class App(OTBObject):
return bool(files) and not missing
# Private functions
def __parse_args(self, args: list[str | OTBObject | dict | list]) -> dict[
str, Any]:
def __parse_args(self, args: list[str | OTBObject | dict | list]) -> dict[str, Any]:
"""Gather all input arguments in kwargs dict.
Args:
@@ -897,16 +869,15 @@ class App(OTBObject):
if isinstance(arg, dict):
kwargs.update(arg)
elif (
isinstance(arg, (str, OTBObject))
or isinstance(arg, list)
and self.is_key_list(self.input_key)
isinstance(arg, (str, OTBObject))
or isinstance(arg, list)
and self.is_key_list(self.input_key)
):
kwargs.update({self.input_key: arg})
return kwargs
def __set_param(
self, key: str,
obj: list | tuple | OTBObject | otb.Application | list[Any]
self, key: str, obj: list | tuple | OTBObject | otb.Application | list[Any]
):
"""Set one parameter, decide which otb.Application method to use depending on target object."""
if obj is None or (isinstance(obj, (list, tuple)) and not obj):
@@ -916,11 +887,11 @@ class App(OTBObject):
if isinstance(obj, OTBObject):
self.app.ConnectImage(key, obj.app, obj.output_image_key)
elif isinstance(
obj, otb.Application
obj, otb.Application
): # this is for backward comp with plain OTB
self.app.ConnectImage(key, obj, get_out_images_param_keys(obj)[0])
elif (
key == "ram"
key == "ram"
): # SetParameterValue in OTB<7.4 doesn't work for ram parameter cf gitlab OTB issue 2200
self.app.SetParameterInt("ram", int(obj))
elif not isinstance(obj, list): # any other parameters (str, int...)
@@ -932,10 +903,9 @@ class App(OTBObject):
if isinstance(inp, OTBObject):
self.app.ConnectImage(key, inp.app, inp.output_image_key)
elif isinstance(
inp, otb.Application
inp, otb.Application
): # this is for backward comp with plain OTB
self.app.ConnectImage(key, obj,
get_out_images_param_keys(inp)[0])
self.app.ConnectImage(key, obj, get_out_images_param_keys(inp)[0])
else: # here `input` should be an image filepath
# Append `input` to the list, do not overwrite any previously set element of the image list
self.app.AddParameterStringList(key, inp)
@@ -950,9 +920,8 @@ class App(OTBObject):
continue
value = self.app.GetParameterValue(key)
# TODO: here we *should* use self.app.IsParameterEnabled, but it seems broken
if isinstance(value,
otb.ApplicationProxy) and self.app.HasAutomaticValue(
key
if isinstance(value, otb.ApplicationProxy) and self.app.HasAutomaticValue(
key
):
try:
value = str(
@@ -963,8 +932,7 @@ class App(OTBObject):
except RuntimeError:
continue # grouped parameters
# Save static output data (ReadImageInfo, ComputeImageStatistics, etc.)
elif self.app.GetParameterRole(key) == 1 and bool(
value) or value == 0:
elif self.app.GetParameterRole(key) == 1 and bool(value) or value == 0:
if isinstance(value, str):
try:
value = literal_eval(value)
@@ -973,8 +941,7 @@ class App(OTBObject):
self.data[key] = value
# Special functions
def __getitem__(self, key: str) -> Any | list[
int | float] | int | float | Slicer:
def __getitem__(self, key: str) -> Any | list[int | float] | int | float | Slicer:
"""This function is called when we use App()[...].
We allow to return attr if key is a parameter, or call OTBObject __getitem__ for pixel values or Slicer
@@ -988,8 +955,7 @@ class App(OTBObject):
return self.outputs[key]
if key in self.parameters:
return self.parameters[key]
raise KeyError(
f"{self.name}: unknown or undefined parameter '{key}'")
raise KeyError(f"{self.name}: unknown or undefined parameter '{key}'")
raise TypeError(
f"{self.name}: cannot access object item or slice using {type(key)} object"
)
@@ -999,11 +965,11 @@ class Slicer(App):
"""Slicer objects i.e. when we call something like raster[:, :, 2] from Python."""
def __init__(
self,
obj: OTBObject,
rows: slice,
cols: slice,
channels: slice | list[int] | int,
self,
obj: OTBObject,
rows: slice,
cols: slice,
channels: slice | list[int] | int,
):
"""Create a slicer object, that can be used directly for writing or inside a BandMath.
@@ -1070,10 +1036,9 @@ class Slicer(App):
) # subtract 1 to respect python convention
spatial_slicing = True
# These are some attributes when the user simply wants to extract *one* band to be used in an Operation
if not spatial_slicing and isinstance(channels, list) and len(
channels) == 1:
if not spatial_slicing and isinstance(channels, list) and len(channels) == 1:
self.one_band_sliced = (
channels[0] + 1
channels[0] + 1
) # OTB convention: channels start at 1
self.input = obj
@@ -1104,8 +1069,7 @@ class Operation(App):
"""
def __init__(self, operator: str, *inputs, nb_bands: int = None,
name: str = None):
def __init__(self, operator: str, *inputs, nb_bands: int = None, name: str = None):
"""Given some inputs and an operator, this function enables to transform this into an OTB application.
Operations generally involve 2 inputs (+, -...). It can have only 1 input for `abs` operator.
@@ -1152,10 +1116,10 @@ class Operation(App):
)
def build_fake_expressions(
self,
operator: str,
inputs: list[OTBObject | str | int | float],
nb_bands: int = None,
self,
operator: str,
inputs: list[OTBObject | str | int | float],
nb_bands: int = None,
):
"""Create a list of 'fake' expressions, one for each band.
@@ -1176,8 +1140,8 @@ class Operation(App):
# For any other operations, the output number of bands is the same as inputs
else:
if any(
isinstance(inp, Slicer) and hasattr(inp, "one_band_sliced")
for inp in inputs
isinstance(inp, Slicer) and hasattr(inp, "one_band_sliced")
for inp in inputs
):
nb_bands = 1
else:
@@ -1188,10 +1152,9 @@ class Operation(App):
]
# check that all inputs have the same nb of bands
if len(nb_bands_list) > 1 and not all(
x == nb_bands_list[0] for x in nb_bands_list
x == nb_bands_list[0] for x in nb_bands_list
):
raise ValueError(
"All images do not have the same number of bands")
raise ValueError("All images do not have the same number of bands")
nb_bands = nb_bands_list[0]
# Create a list of fake expressions, each item of the list corresponding to one band
@@ -1226,7 +1189,7 @@ class Operation(App):
# the false expression stores the expression 2 * str(input1) + str(input2)
fake_exp = f"({expressions[0]} {operator} {expressions[1]})"
elif (
len(inputs) == 3 and operator == "?"
len(inputs) == 3 and operator == "?"
): # this is only for ternary expression
fake_exp = f"({expressions[0]} ? {expressions[1]} : {expressions[2]})"
self.fake_exp_bands.append(fake_exp)
@@ -1248,15 +1211,14 @@ class Operation(App):
one_band_exp = one_band_fake_exp
for inp in self.inputs:
# Replace the name of in-memory object (e.g. '<pyotb.App object>b1' by 'im1b1')
one_band_exp = one_band_exp.replace(repr(inp),
self.im_dic[repr(inp)])
one_band_exp = one_band_exp.replace(repr(inp), self.im_dic[repr(inp)])
exp_bands.append(one_band_exp)
# Form the final expression (e.g. 'im1b1 + 1; im1b2 + 1')
return exp_bands, ";".join(exp_bands)
@staticmethod
def make_fake_exp(
x: OTBObject | str, band: int, keep_logical: bool = False
x: OTBObject | str, band: int, keep_logical: bool = False
) -> tuple[str, list[OTBObject], int]:
"""This an internal function, only to be used by `build_fake_expressions`.
@@ -1279,8 +1241,7 @@ class Operation(App):
# Special case for one-band slicer
if isinstance(x, Slicer) and hasattr(x, "one_band_sliced"):
if keep_logical and isinstance(x.input, LogicalOperation):
fake_exp = x.input.logical_fake_exp_bands[
x.one_band_sliced - 1]
fake_exp = x.input.logical_fake_exp_bands[x.one_band_sliced - 1]
inputs, nb_channels = x.input.inputs, x.input.nb_channels
elif isinstance(x.input, Operation):
# Keep only one band of the expression
@@ -1332,17 +1293,16 @@ class LogicalOperation(Operation):
"""
self.logical_fake_exp_bands = []
super().__init__(operator, *inputs, nb_bands=nb_bands,
name="LogicalOperation")
super().__init__(operator, *inputs, nb_bands=nb_bands, name="LogicalOperation")
self.logical_exp_bands, self.logical_exp = self.get_real_exp(
self.logical_fake_exp_bands
)
def build_fake_expressions(
self,
operator: str,
inputs: list[OTBObject | str | int | float],
nb_bands: int = None,
self,
operator: str,
inputs: list[OTBObject | str | int | float],
nb_bands: int = None,
):
"""Create a list of 'fake' expressions, one for each band.
@@ -1357,8 +1317,8 @@ class LogicalOperation(Operation):
"""
# For any other operations, the output number of bands is the same as inputs
if any(
isinstance(inp, Slicer) and hasattr(inp, "one_band_sliced")
for inp in inputs
isinstance(inp, Slicer) and hasattr(inp, "one_band_sliced")
for inp in inputs
):
nb_bands = 1
else:
@@ -1369,10 +1329,9 @@ class LogicalOperation(Operation):
]
# check that all inputs have the same nb of bands
if len(nb_bands_list) > 1 and not all(
x == nb_bands_list[0] for x in nb_bands_list
x == nb_bands_list[0] for x in nb_bands_list
):
raise ValueError(
"All images do not have the same number of bands")
raise ValueError("All images do not have the same number of bands")
nb_bands = nb_bands_list[0]
# Create a list of fake exp, each item of the list corresponding to one band
for i, band in enumerate(range(1, nb_bands + 1)):
@@ -1425,11 +1384,11 @@ class Output(OTBObject):
_filepath: str | Path = None
def __init__(
self,
pyotb_app: App,
param_key: str = None,
filepath: str = None,
mkdir: bool = True,
self,
pyotb_app: App,
param_key: str = None,
filepath: str = None,
mkdir: bool = True,
):
"""Constructor for an Output object.
@@ -1476,8 +1435,7 @@ class Output(OTBObject):
@filepath.setter
def filepath(self, path: str):
if isinstance(path, str):
if path and not path.startswith(
("/vsi", "http://", "https://", "ftp://")):
if path and not path.startswith(("/vsi", "http://", "https://", "ftp://")):
path = Path(path.split("?")[0])
self._filepath = path
@@ -1499,8 +1457,7 @@ class Output(OTBObject):
return self.parent_pyotb_app.write(
{self.output_image_key: self.filepath}, **kwargs
)
return self.parent_pyotb_app.write({self.output_image_key: filepath},
**kwargs)
return self.parent_pyotb_app.write({self.output_image_key: filepath}, **kwargs)
def __str__(self) -> str:
"""Return string representation of Output filepath."""
@@ -1557,13 +1514,12 @@ def get_nbchannels(inp: str | Path | OTBObject) -> int:
info = App("ReadImageInfo", inp, quiet=True)
return info["numberbands"]
except (
RuntimeError
RuntimeError
) as info_err: # this happens when we pass a str that is not a filepath
raise TypeError(
f"Could not get the number of channels file '{inp}' ({info_err})"
) from info_err
raise TypeError(
f"Can't read number of channels of type '{type(inp)}' object {inp}")
raise TypeError(f"Can't read number of channels of type '{type(inp)}' object {inp}")
def get_pixel_type(inp: str | Path | OTBObject) -> str:
@@ -1584,15 +1540,14 @@ def get_pixel_type(inp: str | Path | OTBObject) -> str:
info = App("ReadImageInfo", inp, quiet=True)
datatype = info["datatype"] # which is such as short, float...
except (
RuntimeError
RuntimeError
) as info_err: # this happens when we pass a str that is not a filepath
raise TypeError(
f"Could not get the pixel type of `{inp}` ({info_err})"
) from info_err
if datatype:
return parse_pixel_type(datatype)
raise TypeError(
f"Could not get the pixel type of {type(inp)} object {inp}")
raise TypeError(f"Could not get the pixel type of {type(inp)} object {inp}")
def parse_pixel_type(pixel_type: str | int) -> int:
@@ -1622,8 +1577,7 @@ def parse_pixel_type(pixel_type: str | int) -> int:
if pixel_type in datatype_to_pixeltype.values():
return getattr(otb, f"ImagePixelType_{pixel_type}")
if pixel_type in datatype_to_pixeltype:
return getattr(otb,
f"ImagePixelType_{datatype_to_pixeltype[pixel_type]}")
return getattr(otb, f"ImagePixelType_{datatype_to_pixeltype[pixel_type]}")
raise KeyError(
f"Unknown data type `{pixel_type}`. Available ones: {datatype_to_pixeltype}"
)
@@ -1642,9 +1596,9 @@ def get_out_images_param_keys(app: OTBObject) -> list[str]:
def summarize(
obj: App | Output | Any,
strip_input_paths: bool = False,
strip_output_paths: bool = False,
obj: App | Output | Any,
strip_input_paths: bool = False,
strip_output_paths: bool = False,
) -> dict[str, str | dict[str, Any]]:
"""Recursively summarize parameters of an App or Output object and its parents.
@@ -1678,10 +1632,10 @@ def summarize(
parameters = {}
for key, param in obj.parameters.items():
if (
strip_input_paths
and obj.is_input(key)
or strip_output_paths
and obj.is_output(key)
strip_input_paths
and obj.is_input(key)
or strip_output_paths
and obj.is_output(key)
):
parameters[key] = (
[strip_path(p) for p in param]
Loading