Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ venv/
ENV/
env.bak/
venv.bak/
.vscode/

dclab/_version.py
*.bib.bak
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
0.67.1
- docs: type hinted the features sub-module
- enh: add mass density feature, deprecate some qpi features (#284)
0.67.0
- BREAKING CHANGE: make it difficult to export the "contour" feature
Expand Down
16 changes: 16 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,22 @@ We use flake8 to enforce coding style::
flake8 tests


Type Hinting
~~~~~~~~~~~~
For type hinting and docstring styling, try to keep the
Code Reference readable in the documentation website.
If in doubt just ask, or look at the examples in the codebase.

* Simple type combintions such as ``float`` or ``str | pathlib.Path``
should be included as type hints, but do not need to be included
in the docstring parameter description.
* More involved type hints can have extra information in the
docstring. For example for numpy arrays, ``npt.NDArray[np.bool]`` doesn't
render in a readable way in the Code Reference, and doesn't include shape.
Therefore, you can also keep the docstring parameter description with
the shape and dtype information e.g., ``binary ndarray of shape (M, N)``.


Incrementing version
~~~~~~~~~~~~~~~~~~~~
Dclab gets its version from the latest git tag.
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
19 changes: 12 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,8 @@ def __len__(self):
return len(self.masks)


def get_contour(mask):
def get_contour(mask: npt.NDArray[np.bool | np.int]
) -> 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 +131,8 @@ def get_contour(mask):
return contours[0]


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

Parameters
Expand All @@ -153,7 +158,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
60 changes: 33 additions & 27 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 @@ -61,16 +66,16 @@ def extrapolate_emodulus(lut, datax, deform, emod, deform_norm,
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_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,65 +104,66 @@ 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
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
area_um
Apparent (2D image) area [µm²] of the event(s)
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)
lut_data: str, path, or tuple of (np.ndarray of shape (N, 3), dict)
The LUT data to use. If it is a built-in identifier,
then the respective LUT will be used. Otherwise, a path to a
file on disk or a tuple (LUT array, metadata) is possible.
The LUT metadata is used to check whether the given features
(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 input arrays. If set to false, input arrays are
copy
Copy input arrays. If set to False, input arrays are
overridden.

Returns
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
Loading
Loading