1010
1111
1212@lru_cache (2 )
13- def _get_neighbor_kernel (device : torch .device ):
13+ def _get_neighbor_kernel (device : "torch.device" ) -> "torch.Tensor" :
14+ """Return a 4-connected neighbor kernel (cross-shaped) as a cached tensor.
15+
16+ Parameters
17+ ----------
18+ device : torch.device
19+ The device on which the kernel tensor will be placed.
20+
21+ Returns
22+ -------
23+ torch.Tensor
24+ A 4D tensor of shape (1, 1, 3, 3) with the 4-connected neighbor pattern.
25+ """
1426 return torch .tensor ([[[[0 , 1 , 0 ], [1 , 0 , 1 ], [0 , 1 , 0 ]]]],
1527 dtype = torch .float32 ,
1628 device = device )
1729
1830
1931@lru_cache (2 )
20- def _get_diagonal_neighbor_kernel (device : torch .device ):
32+ def _get_diagonal_neighbor_kernel (device : "torch.device" ) -> "torch.Tensor" :
33+ """Return an 8-connected neighbor kernel (full 3x3 except center) as a cached tensor.
34+
35+ Parameters
36+ ----------
37+ device : torch.device
38+ The device on which the kernel tensor will be placed.
39+
40+ Returns
41+ -------
42+ torch.Tensor
43+ A 4D tensor of shape (1, 1, 3, 3) with the 8-connected neighbor pattern.
44+ """
2145 return torch .tensor ([[[[1 , 1 , 1 ], [1 , 0 , 1 ], [1 , 1 , 1 ]]]],
2246 dtype = torch .float32 ,
2347 device = device )
2448
2549
26- def _run_convolution (mask : torch .Tensor , kernel : torch .Tensor ) -> torch .Tensor :
50+ def _run_convolution (mask : "torch.Tensor" , kernel : "torch.Tensor" ) -> "torch.Tensor" :
51+ """Convolve a 2D boolean mask with the given kernel and return a boolean output.
52+
53+ Parameters
54+ ----------
55+ mask : torch.Tensor
56+ 2D boolean tensor to convolve.
57+ kernel : torch.Tensor
58+ 4D convolution kernel of shape (1, 1, kH, kW).
59+
60+ Returns
61+ -------
62+ torch.Tensor
63+ 2D boolean tensor of the same shape as ``mask``.
64+ """
2765 mask_float = mask .to (torch .float32 )
2866 mask4d = mask_float .unsqueeze (dim = 0 ).unsqueeze (dim = 0 )
2967 conv_out = torch .nn .functional .conv2d (mask4d , kernel , padding = 1 )[0 , 0 ]
@@ -32,7 +70,26 @@ def _run_convolution(mask: torch.Tensor, kernel: torch.Tensor) -> torch.Tensor:
3270 return conv_out_bool
3371
3472
35- def make_mask_ids_consecutive (mask : np .array ) -> np .array :
73+ def make_mask_ids_consecutive (mask : np .ndarray ) -> np .ndarray :
74+ """Remap ROI mask values to a consecutive integer range starting at 0.
75+
76+ Parameters
77+ ----------
78+ mask : np.ndarray
79+ Integer array where 0 is background and positive integers are ROI IDs.
80+ Must contain 0 as the minimum value (cellpose format).
81+
82+ Returns
83+ -------
84+ np.ndarray
85+ Mask with the same shape as ``mask`` but with IDs remapped to
86+ ``{0, 1, 2, ..., n_rois}``.
87+
88+ Raises
89+ ------
90+ AssertionError
91+ If the minimum value of ``mask`` is not 0.
92+ """
3693 values = np .unique (mask )
3794 assert values .min () == 0 , "Mask has to be in cellpose format"
3895 if (values .max () + 1 ) == values .shape [0 ]:
@@ -46,27 +103,44 @@ def make_mask_ids_consecutive(mask: np.array) -> np.array:
46103
47104
48105def create_mask (
49- cell_probs : torch .Tensor ,
50- offsets : torch .Tensor ,
51- center_mask : torch .Tensor ,
106+ cell_probs : " torch.Tensor" ,
107+ offsets : " torch.Tensor" ,
108+ center_mask : " torch.Tensor" ,
52109 cell_prob_threshold : float = 0.5 ,
53110 kernel_size : int = 5 ,
54111 center_prob_threshold : float = 0.1 ,
55112 max_number_of_cells : int = 99999 ,
56- ) -> torch .Tensor :
57- """
58- Create a roi mask from the outputs of the instance unet model.
113+ ) -> " torch.Tensor" :
114+ """Create a ROI mask from the outputs of the instance UNet model.
115+
59116 See https://github.com/bowenc0221/panoptic-deeplab/blob/master/segmentation/model/post_processing/instance_post_processing.py
60- for another implementation
61-
62- Args:
63- cell_probs: probability that a pixel contains a cell, shape: [dim_x, dim_y]
64- offsets: offset to the cell center in x and y direction for each pixel, shape [2, dim_x, dim_y]
65- center_mask: 'probability' that a pixel is the center of the cell, shape: [dim_x, dim_y]
66- cell_prob_threshold: threshold that we consider a pixel to be a cell
67- kernel_size: mentioned parameter in section 3.2 of https://arxiv.org/pdf/1911.10194.pdf, changed to 5
68- center_prob_threshold: probability threshold that to determine whether a pixel is a center
69- max_number_of_cells: maximum number of cells per mask
117+ for another implementation.
118+
119+ Parameters
120+ ----------
121+ cell_probs : torch.Tensor
122+ Probability that a pixel contains a cell, shape: (dim_x, dim_y).
123+ offsets : torch.Tensor
124+ Offset to the cell center in x and y direction for each pixel,
125+ shape: (2, dim_x, dim_y).
126+ center_mask : torch.Tensor
127+ Probability that a pixel is the center of a cell, shape: (dim_x, dim_y).
128+ cell_prob_threshold : float, optional
129+ Threshold above which a pixel is considered a cell. Default is 0.5.
130+ kernel_size : int, optional
131+ Max-pooling kernel size used to find center peaks (Section 3.2 of
132+ https://arxiv.org/pdf/1911.10194.pdf). Default is 5.
133+ center_prob_threshold : float, optional
134+ Probability threshold for a pixel to be considered a center.
135+ Default is 0.1.
136+ max_number_of_cells : int, optional
137+ Maximum number of cells per mask. Default is 99999.
138+
139+ Returns
140+ -------
141+ torch.Tensor
142+ Integer tensor of the same spatial shape as ``cell_probs`` where each
143+ pixel is labelled with its instance ID (0 = background).
70144 """
71145 # determine pixels that are cells
72146 pixel_is_cell = cell_probs > cell_prob_threshold
@@ -123,11 +197,19 @@ def create_mask(
123197 return roi_mask_consecutive
124198
125199
126- def clean_roi_mask (roi_mask : torch .Tensor ) -> torch :
127- """"
128- (1) filter pixel of a cell that don't have a horizontal and vertical neighboring pixel
129- (2) throw out cells that have less than 3 pixels
130- Possible Todo: throw out pixels of a cell that are horizontal or vertical neighbors of another cell
200+ def clean_roi_mask (roi_mask : "torch.Tensor" ) -> None :
201+ """Remove isolated pixels and small ROIs from a ROI mask in-place.
202+
203+ Steps applied:
204+ (1) Remove pixels of a cell that have no horizontal or vertical neighbour
205+ belonging to the same cell.
206+ (2) Remove cells that have fewer than 3 pixels.
207+
208+ Parameters
209+ ----------
210+ roi_mask : torch.Tensor
211+ Integer 2D tensor where 0 is background and positive integers are ROI
212+ IDs. Modified in-place.
131213 """
132214 # Filter pixels that don't have any horizontal or vertical neighboring pixel
133215 kernel = _get_neighbor_kernel (roi_mask .device )
0 commit comments