camfi.util module¶
- class camfi.util.SubDirDict(mapping: Optional[Mapping[pathlib.Path, camfi.util.V]] = None)¶
Bases:
Mapping[pathlib.Path,camfi.util.V]A mapping from subdirectory Paths to V which returns self[‘foo/bar’] if ‘foo/bar/baz’ is missing from the available keys.
Examples
>>> d = SubDirDict() >>> d["foo"] = "foo" >>> d["foo"] 'foo' >>> d["foo/bar"] 'foo' >>> d["foo/bar/baz"] 'foo' >>> d["bar"] Traceback (most recent call last): ... KeyError: "'bar' not in SubDirDict({Path('foo'): 'foo'})"
SubDirDict can be initialised from a dictionary
>>> SubDirDict({"foo": "bar", "foobar": "baz"}) SubDirDict({Path('foo'): 'bar', Path('foobar'): 'baz'})
- __init__(mapping: Optional[Mapping[pathlib.Path, camfi.util.V]] = None)¶
Initialises SubDirDict.
- Parameters
mapping (Optional[Mapping[Path, V]]) – E.g. an instance of type dictt[Path, V]
- __repr__()¶
String representation of SubDirDict. Uses “Path” instead of platform dependant “PosixPath” or “WindowsPath”.
- __weakref__¶
list of weak references to the object (if defined)
- items() a set-like object providing a view on D’s items¶
- keys() a set-like object providing a view on D’s keys¶
- values() an object providing a view on D’s values¶
- class camfi.util.Timezone(v)¶
Bases:
datetime.tzinfoProvides pydantic validation for timezones.
- __init__(v)¶
- __repr__()¶
Return repr(self).
- __str__()¶
Return str(self).
- __weakref__¶
list of weak references to the object (if defined)
- dst(dt: Optional[datetime.datetime]) Optional[datetime.timedelta]¶
datetime -> DST offset as timedelta positive east of UTC.
- fromutc(dt: datetime.datetime) datetime.datetime¶
datetime in UTC -> datetime in local time.
- tzname(dt: Optional[datetime.datetime]) Optional[str]¶
datetime -> string name of time zone.
- utcoffset(dt: Optional[datetime.datetime]) Optional[datetime.timedelta]¶
datetime -> timedelta showing offset from UTC, negative values indicating West of UTC
- camfi.util.dilate_idx(rr: numpy.ndarray, cc: numpy.ndarray, d: int, img_shape: Optional[tuple] = None) tuple¶
Takes index arrays rr and cc and performs a morphological dilation of size d on them.
- Parameters
rr (np.ndarray) – Row indices.
cc (np.ndarray) – Column indices (must have same shape as rr).
d (int) – Dilation factor, must be at least 1 (or a ValueError is raised).
img_shape (Optional[tuple[int, int]]) – Shape of image (rows, columns). Indices which lie outside this will be ommitted.
- Returns
rr_dilated (np.ndarray) – Row indices after morphological dilation.
cc_dilated (np.ndarray) – Column indices after morphological dilation.
Examples
>>> a = np.array([[0, 0, 0, 0, 0], ... [0, 0, 1, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0]]) >>> rr, cc = np.nonzero(a) >>> rr_dilated, cc_dilated = dilate_idx(rr, cc, 1) >>> a[rr_dilated, cc_dilated] = 1 >>> a array([[0, 0, 1, 0, 0], [0, 1, 1, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
If shape is given, omits indices larger than the dimensions given
>>> a = np.array([[0, 0, 0, 0, 0], ... [0, 0, 0, 0, 1], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0]]) >>> rr, cc = np.nonzero(a) >>> rr_dilated, cc_dilated = dilate_idx(rr, cc, 1, a.shape) >>> a[rr_dilated, cc_dilated] = 1 >>> a array([[0, 0, 0, 0, 1], [0, 0, 0, 1, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
If we didn’t give the shape argument in the above example, we get an IndexError
>>> a = np.array([[0, 0, 0, 0, 0], ... [0, 0, 0, 0, 1], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0]]) >>> rr, cc = np.nonzero(a) >>> rr_dilated, cc_dilated = dilate_idx(rr, cc, 1) >>> a[rr_dilated, cc_dilated] = 1 Traceback (most recent call last): ... IndexError: index 5 is out of bounds for axis 1 with size 5
But we don’t need the shape parameter to filter out negative indices
>>> a = np.array([[1, 0, 0, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0]]) >>> rr, cc = np.nonzero(a) >>> rr_dilated, cc_dilated = dilate_idx(rr, cc, 1) >>> a[rr_dilated, cc_dilated] = 1 >>> a array([[1, 1, 0, 0, 0], [1, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
Dilation is based on euclidean distance
>>> a = np.array([[0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 1, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0], ... [0, 0, 0, 0, 0, 0, 0]]) >>> rr, cc = np.nonzero(a) >>> rr_dilated, cc_dilated = dilate_idx(rr, cc, 3, a.shape) >>> a[rr_dilated, cc_dilated] = 1 >>> a array([[0, 0, 0, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 0, 0, 0]])
If input is sorted, then the ouptut will be too (with precedence rr, cc)
>>> rr, cc = np.array([50]), np.array([50]) >>> dilate_idx(rr, cc, 1, (100, 100)) (array([49, 50, 50, 50, 51]), array([50, 49, 50, 51, 50]))
If a non-positive dilation factor is given, a ValueError is raised
>>> dilate_idx(1, 2, 0) Traceback (most recent call last): ... ValueError: d=0. Should be positive.
- camfi.util.endpoint_truncate(fit_mask_vals: numpy.ndarray, n: pydantic.types.NonNegativeInt) tuple¶
An implementation of an endpoint_method.
- Parameters
fit_mask_vals (np.ndarray) – Array to find the endpoints of.
n (NonNegativeInt) – Number of values to truncate off start and end of fit_mask_vals.
- Returns
start_index (NonNegativeInt) – Index of endpoint of polyline annotation.
end_index (NonNegativeInt) – Index of endpoint of polyline annotation.
- camfi.util.smallest_enclosing_circle(points: Union[Iterable[tuple], numpy.ndarray]) tuple¶
Performs Welzl’s algorithm to find the smallest enclosing circle of a set of points in a cartesian plane.
- Parameters
points (Union[Iterable[tuple[float, float]], np.ndarray]) – Iterable of 2-tuples or (N, 2)-array, with each tuple defining the coordinates of a point.
- Returns
x (float) – x-coordinate of centre of circle.
y (float) – y-coordinate of centre of circle.
r (float) – Radius of circle.
Examples
If no points are given, values are still returned:
>>> smallest_enclosing_circle([]) (0.0, 0.0, 0.0)
If one point is given, r will be 0.0:
>>> smallest_enclosing_circle([(1.0, 2.0)]) (1.0, 2.0, 0.0)
Two points trivial case:
>>> smallest_enclosing_circle([(0.0, 0.0), (2.0, 0.0)]) (1.0, 0.0, 1.0)
Three points trivial case:
>>> np.allclose( ... smallest_enclosing_circle([(0.0, 0.0), (2.0, 0.0), (1.0, sqrt(3))]), ... (1.0, sqrt(3) / 3, 2 * sqrt(3) / 3) ... ) True
Extra points within the circle don’t affect the circle:
>>> np.allclose( ... smallest_enclosing_circle([ ... (0.0, 0.0), ... (2.0, 0.0), ... (1.0, sqrt(3)), ... (0.5, 0.5)]), ... (1.0, sqrt(3) / 3, 2 * sqrt(3) / 3) ... ) True
If points are inscribed on a circle, the correct circle is also given:
>>> np.allclose( ... smallest_enclosing_circle([(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0)]), ... (1.0, 1.0, sqrt(2)) ... ) True
- camfi.util.weighted_intersection_over_minimum(mask0: torch.Tensor, mask1: torch.Tensor) float¶
Calculates the weighted intersection over minimum (IoM) between two segmentation masks.
- Parameters
mask0 (torch.Tensor) – Instance segmentation mask to compare to mask1.
mask1 (torch.Tensor) – Instance segmentation mask to compare to mask0. Should have the same shape as mask0.
- Returns
iom – Weighted intersection over union of mask0 and mask1.
- Return type
float
Examples
>>> mask0 = torch.tensor([0.0, 0.5, 0.5, 0.0]) >>> mask1 = torch.tensor([1.0, 1.0, 0.0, 0.0]) >>> weighted_intersection_over_minimum(mask0, mask1) 0.5