|
19 | 19 |
|
20 | 20 | from connectomics.common import bounding_box |
21 | 21 | from connectomics.segmentation import labels |
| 22 | +from ffn.inference import segmentation as segmentation_lib |
22 | 23 | import numpy as np |
23 | 24 | import pandas as pd |
24 | 25 | from scipy import ndimage |
25 | 26 |
|
26 | 27 |
|
27 | | -def find_decision_points(seg: np.ndarray, |
28 | | - voxel_size: Sequence[float], |
29 | | - max_distance: Optional[float] = None, |
30 | | - subvol_box: Optional[bounding_box.BoundingBox] = None |
31 | | - ) -> dict[tuple[int, int], tuple[float, np.ndarray]]: |
| 28 | +def find_decision_points( |
| 29 | + seg: np.ndarray, |
| 30 | + voxel_size: Sequence[float], |
| 31 | + max_distance: Optional[float] = None, |
| 32 | + subvol_box: Optional[bounding_box.BoundingBox] = None, |
| 33 | + optimize_sparse: bool = False, |
| 34 | + sparse_noise_threshold: int = 0, |
| 35 | +) -> dict[tuple[int, int], tuple[float, np.ndarray]]: |
32 | 36 | """Identifies decision points in a segmentation subvolume. |
33 | 37 |
|
34 | 38 | Args: |
35 | 39 | seg: 3d uint64 ndarray of segmentation data |
36 | 40 | voxel_size: 3-tuple (xyz) defining the physical voxel size |
37 | 41 | max_distance: maximum distance between the segment and the decision point |
38 | 42 | (same units as voxel_size); if None, distances will not be limited |
39 | | - subvol_box: selector for a subvolume within `seg` within which |
40 | | - to search for decision points; the whole subvolume is always used |
41 | | - to compute the distance transform |
| 43 | + subvol_box: selector for a subvolume within `seg` within which to search for |
| 44 | + decision points; the whole subvolume is always used to compute the |
| 45 | + distance transform |
| 46 | + optimize_sparse: if True, first counts the number of segments in `seg` and |
| 47 | + returns early if there are fewer than 2. |
| 48 | + sparse_noise_threshold: if > 0 and `optimize_sparse` is True, ignores |
| 49 | + components with voxel counts < this threshold when counting segments. |
42 | 50 |
|
43 | 51 | Returns: |
44 | 52 | dict from segment ID pairs to tuples of: |
45 | 53 | approximate physical distance from the segment to the decision point |
46 | 54 | (x, y, z) decision point |
47 | 55 | """ |
| 56 | + if optimize_sparse: |
| 57 | + _, counts = segmentation_lib.clean_up_and_count( |
| 58 | + seg, |
| 59 | + split_cc=False, |
| 60 | + min_size=sparse_noise_threshold, |
| 61 | + compute_id_map=False, |
| 62 | + ) |
| 63 | + |
| 64 | + if counts is not None and len([k for k in counts.keys() if k > 0]) <= 1: |
| 65 | + # If there are 0 or 1 unique segments (excluding background), |
| 66 | + # they cannot possibly touch another segment. |
| 67 | + return {} |
| 68 | + |
48 | 69 | # EDT is the Euclidean Distance Transform, specifying how far voxels added |
49 | 70 | # in 'expanded_seg' are from the seeds in 'seg'. |
50 | 71 | expanded_seg, edt = labels.watershed_expand(seg, voxel_size, max_distance) |
|
0 commit comments