Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More type hinting for the codebase #265

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
0.62.7
- docs: type hinted the features sub-module
- fix: IntegrityChecker must not load basins
- tests: avoid architecture and Python-version dependent test
0.62.6
Expand Down
13 changes: 11 additions & 2 deletions dclab/features/bright.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@
Computation of mean and standard deviation of grayscale values inside the
RT-DC event image mask.
"""
from __future__ import annotations

import numpy as np
import numpy.typing as npt


def get_bright(mask, image, ret_data="avg,sd"):
def get_bright(mask: npt.NDArray[bool] | list[npt.NDArray[bool]],
image: npt.NDArray | list[npt.NDArray],
ret_data: str = "avg,sd"
) -> (float |
npt.NDArray |
tuple[float, float] |
tuple[npt.NDArray, npt.NDArray]):
"""Compute avg and/or std of the event brightness

The event brightness is defined by the gray-scale values of the
Expand All @@ -18,7 +27,7 @@ def get_bright(mask, image, ret_data="avg,sd"):
image: ndarray or list of ndarrays of shape (M,N)
A 2D array that holds the image in form of grayscale values
of an event.
ret_data: str
ret_data
A comma-separated list of metrices to compute
- "avg": compute the average
- "sd": compute the standard deviation
Expand Down
15 changes: 13 additions & 2 deletions dclab/features/bright_bc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@
Computation of mean and standard deviation of grayscale values inside the
RT-DC event image mask with background-correction taken into account.
"""
from __future__ import annotations

import numpy as np
import numpy.typing as npt


def get_bright_bc(mask, image, image_bg, bg_off=None, ret_data="avg,sd"):
def get_bright_bc(mask: npt.NDArray[bool] | list[npt.NDArray[bool]],
image: npt.NDArray | list[npt.NDArray],
image_bg: npt.NDArray | list[npt.NDArray],
bg_off: float | npt.NDArray = None,
ret_data: str = "avg,sd"
) -> (float |
npt.NDArray |
tuple[float, float] |
tuple[npt.NDArray, npt.NDArray]):
"""Compute avg and/or std of the background-corrected event brightness

The background-corrected event brightness is defined by the
Expand All @@ -24,7 +35,7 @@ def get_bright_bc(mask, image, image_bg, bg_off=None, ret_data="avg,sd"):
bg_off: float or 1D ndarray
Additional offset value that is added to `image_bg` before
background correction
ret_data: str
ret_data
A comma-separated list of metrices to compute
- "avg": compute the average
- "sd": compute the standard deviation
Expand Down
12 changes: 10 additions & 2 deletions dclab/features/bright_perc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@
Computation of the 10th and 90th percentile of grayscale values inside the
RT-DC event image mask with background-correction taken into account.
"""
from __future__ import annotations

import numpy as np
import numpy.typing as npt


def get_bright_perc(mask, image, image_bg, bg_off=None):
def get_bright_perc(mask: npt.NDArray[bool] | list[npt.NDArray[bool]],
image: npt.NDArray | list[npt.NDArray],
image_bg: npt.NDArray | list[npt.NDArray],
bg_off: float | npt.NDArray = None
) -> (tuple[float, float] |
tuple[npt.NDArray, npt.NDArray]):
"""Compute 10th and 90th percentile of the bg-corrected event brightness

The background-corrected event brightness is defined by the
Expand All @@ -29,7 +37,7 @@ def get_bright_perc(mask, image, image_bg, bg_off=None):
-------
bright_perc_10: float or ndarray of size N
10th percentile of brightness
bright_perc_10: float or ndarray of size N
bright_perc_90: float or ndarray of size N
90th percentile of brightness
"""
if isinstance(mask, np.ndarray) and len(mask.shape) == 2:
Expand Down
18 changes: 11 additions & 7 deletions dclab/features/contour.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Computation of event contour from event mask"""
from __future__ import annotations

from collections import deque
import numbers

import numpy as np
import numpy.typing as npt

# equivalent to
# from skimage.measure import find_contours
Expand All @@ -14,15 +17,15 @@ class NoValidContourFoundError(BaseException):


class LazyContourList(object):
def __init__(self, masks, max_events=1000):
def __init__(self, masks: npt.ArrayLike, max_events: int = 1000) -> None:
"""A list-like object that computes contours upon indexing

Parameters
----------
masks: array-like
masks
3D array of masks, may be an HDF5 dataset or any other
structure that supports indexing
max_events: int
max_events
maximum number of contours to keep in the contour list;
set to 0/False/None to cache all contours

Expand All @@ -40,7 +43,7 @@ def __init__(self, masks, max_events=1000):
self.identifier = str(masks[0][:].tobytes())
self.shape = len(masks), np.nan, 2

def __getitem__(self, idx):
def __getitem__(self, idx) -> npt.NDArray:
"""Compute contour(s) if not already in self.contours"""
if not isinstance(idx, numbers.Integral):
# slicing!
Expand Down Expand Up @@ -74,7 +77,7 @@ def __len__(self):
return len(self.masks)


def get_contour(mask):
def get_contour(mask: npt.NDArray[bool]) -> npt.NDArray | list[npt.NDArray]:
"""Compute the image contour from a mask

The contour is computed in a very inefficient way using scikit-image
Expand Down Expand Up @@ -127,7 +130,8 @@ def get_contour(mask):
return contours[0]


def get_contour_lazily(mask):
def get_contour_lazily(mask: npt.NDArray[bool]) -> \
npt.NDArray | LazyContourList:
"""Like :func:`get_contour`, but computes contours on demand

Parameters
Expand All @@ -153,7 +157,7 @@ def get_contour_lazily(mask):
return cont


def remove_duplicates(cont):
def remove_duplicates(cont: npt.NDArray) -> npt.NDArray:
"""Remove duplicates in a circular contour"""
x = np.resize(cont, (len(cont) + 1, 2))
selection = np.ones(len(x), dtype=bool)
Expand Down
58 changes: 32 additions & 26 deletions dclab/features/emodulus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import warnings

import numpy as np
import numpy.typing as npt
import scipy.interpolate as spint

from ...warn import PipelineWarning
Expand All @@ -18,7 +19,6 @@
from .scale_linear import scale_emodulus, scale_feature
from .viscosity import get_viscosity


#: Set this to True to globally enable spline extrapolation when the
#: `area_um`/`deform` data are outside the LUT. This is discouraged and
#: a :class:`KnowWhatYouAreDoingWarning` warning will be issued.
Expand All @@ -33,8 +33,13 @@ class YoungsModulusLookupTableExceededWarning(PipelineWarning):
pass


def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
deform_thresh=.05, inplace=True):
def extrapolate_emodulus(lut: npt.NDArray,
datax: npt.NDArray,
deform: npt.NDArray,
emod: npt.NDArray,
deform_norm: float,
deform_thresh: float = .05,
inplace: bool = True) -> npt.NDArray:
"""Use spline interpolation to fill in nan-values

When points (`datax`, `deform`) are outside the convex
Expand All @@ -56,21 +61,21 @@ def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
points, second axis enumerates datax, deform, and emodulus)
datax: ndarray of size N
The normalized x data (corresponding to `lut[:, 0]`)
deform: ndarray of size N
The normalized deform (corresponding to `lut[:, 1]`)
emod: ndarray of size N
The emodulus (corresponding to `lut[:, 2]`); If `emod`
does not contain nan-values, there is nothing to do here.
deform_norm: float
deform: ndarray of size N
The normalized deform (corresponding to `lut[:, 1]`)
deform_norm
The normalization value used to normalize `lut[:, 1]` and
`deform`.
deform_thresh: float
deform_thresh
Not the entire LUT is used for bivariate spline interpolation.
Only the points where `lut[:, 1] > deform_thresh/deform_norm`
are used. This is necessary, because for small deformations,
the LUT has an extreme slope that kills any meaningful
spline interpolation.
inplace: bool
inplace
If True (default), replaces nan values in `emod` in-place.
If False, `emod` is not modified.
"""
Expand Down Expand Up @@ -99,48 +104,49 @@ def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
return emod


def get_emodulus(deform: float | np.array,
area_um: float | np.array | None = None,
volume: float | np.array | None = None,
def get_emodulus(deform: float | npt.NDArray,
area_um: float | npt.NDArray | None = None,
volume: float | npt.NDArray | None = None,
medium: float | str = "0.49% MC-PBS",
channel_width: float = 20.0,
flow_rate: float = 0.16,
px_um: float = 0.34,
temperature: float | np.ndarray | None = 23.0,
lut_data: str | pathlib.Path | np.ndarray = "LE-2D-FEM-19",
temperature: float | npt.NDArray | None = 23.0,
lut_data: (str | pathlib.Path |
tuple[npt.NDArray, dict]) = "LE-2D-FEM-19",
visc_model: Literal['herold-2017',
'herold-2017-fallback',
'buyukurganci-2022',
'kestin-1978',
None] = "herold-2017-fallback",
extrapolate: bool = INACCURATE_SPLINE_EXTRAPOLATION,
copy: bool = True):
copy: bool = True) -> npt.NDArray:
"""Compute apparent Young's modulus using a look-up table

Parameters
----------
area_um: float or ndarray
area_um
Apparent (2D image) area [µm²] of the event(s)
deform: float or ndarray
deform
Deformation (1-circularity) of the event(s)
volume: float or ndarray
volume
Apparent volume of the event(s). It is not possible to define
`volume` and `area_um` at the same time (makes no sense).

.. versionadded:: 0.25.0
medium: str or float
medium
The medium to compute the viscosity for. If a string
is given, the viscosity is computed. If a float is given,
this value is used as the viscosity in mPa*s (Note that
`temperature` and `visc_model` must be set to None in this case).
channel_width: float
channel_width
The channel width [µm]
flow_rate: float
flow_rate
Flow rate [µL/s]
px_um: float
px_um
The detector pixel size [µm] used for pixelation correction.
Set to zero to disable.
temperature: float, ndarray, or None
temperature
Temperature [°C] of the event(s)
lut_data: path, str, or tuple of (np.ndarray of shape (N, 3), dict)
The LUT data to use. If it is a built-in identifier,
Expand All @@ -150,13 +156,13 @@ def get_emodulus(deform: float | np.array,
(e.g. `area_um` and `deform`) are valid interpolation choices.

.. versionadded:: 0.25.0
visc_model: str
visc_model
The viscosity model to use,
see :func:`dclab.features.emodulus.viscosity.get_viscosity`
extrapolate: bool
extrapolate
Perform extrapolation using :func:`extrapolate_emodulus`. This
is discouraged!
copy: bool
copy
Copy input arrays. If set to false, input arrays are
overridden.

Expand Down Expand Up @@ -326,7 +332,7 @@ def get_emodulus(deform: float | np.array,
return emod


def normalize(data, dmax):
def normalize(data: npt.NDArray, dmax: float) -> npt.NDArray:
"""Perform normalization in-place for interpolation

Note that :func:`scipy.interpolate.griddata` has a `rescale`
Expand Down
12 changes: 7 additions & 5 deletions dclab/features/emodulus/load.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import warnings

import numpy as np
import numpy.typing as npt

from ... import definitions as dfn

Expand All @@ -24,7 +25,7 @@


@functools.lru_cache()
def get_internal_lut_names_dict():
def get_internal_lut_names_dict() -> dict:
"""Return list of internal lut names"""
lutfiles = {}
for pp in importlib_resources.files('dclab.features.emodulus').iterdir():
Expand All @@ -34,7 +35,7 @@ def get_internal_lut_names_dict():
return lutfiles


def get_lut_path(path_or_id):
def get_lut_path(path_or_id: str | pathlib.Path) -> pathlib.Path:
"""Find the path to a LUT

path_or_id: str or pathlib.Path
Expand Down Expand Up @@ -63,7 +64,8 @@ def get_lut_path(path_or_id):
return lut_path


def load_lut(lut_data: str | pathlib.Path | np.ndarray = "LE-2D-FEM-19"):
def load_lut(lut_data: str | pathlib.Path | tuple[npt.NDArray, dict] =
"LE-2D-FEM-19") -> tuple[npt.NDArray, dict]:
"""Load LUT data from disk

Parameters
Expand Down Expand Up @@ -98,7 +100,7 @@ def load_lut(lut_data: str | pathlib.Path | np.ndarray = "LE-2D-FEM-19"):
return lut, meta


def load_mtext(path):
def load_mtext(path: str | pathlib.Path) -> tuple[npt.NDArray, dict]:
"""Load column-based data from text files with metadata

This file format is used for isoelasticity lines and look-up
Expand Down Expand Up @@ -217,7 +219,7 @@ def load_mtext(path):
return data, meta


def register_lut(path, identifier=None):
def register_lut(path: str | pathlib.Path, identifier: str = None) -> None:
"""Register an external LUT file in dclab

This will add it to :const:`EXTERNAL_LUTS`, which is required
Expand Down
Loading
Loading