@@ -43,6 +43,7 @@ def decorator(func):
4343
4444
4545__all__ = [
46+ "decode_binary_thresholding" ,
4647 "decode_binary_cc" ,
4748 "decode_binary_watershed" ,
4849 "decode_binary_contour_cc" ,
@@ -52,6 +53,80 @@ def decorator(func):
5253]
5354
5455
56+ def decode_binary_thresholding (
57+ predictions : np .ndarray ,
58+ threshold_range : Tuple [float , float ] = (0.8 , 1.0 ),
59+ ) -> np .ndarray :
60+ r"""Convert binary foreground probability maps to binary mask via simple thresholding.
61+
62+ This is a lightweight decoding function that applies thresholding to convert
63+ probability predictions to binary segmentation masks. Unlike instance segmentation
64+ methods, this produces a semantic segmentation (no individual instance IDs).
65+
66+ The function uses the minimum threshold from threshold_range to binarize predictions.
67+ This is useful for simple binary segmentation tasks where instance separation is not needed.
68+
69+ Args:
70+ predictions (numpy.ndarray): foreground probability of shape :math:`(C, Z, Y, X)` or :math:`(C, Y, X)`.
71+ The first channel (predictions[0]) is used as the foreground probability.
72+ Values should be in range [0, 1] (normalized) or [0, 255] (uint8).
73+ threshold_range (tuple): Tuple of (min_threshold, max_threshold) for binarization.
74+ Only the minimum threshold is used. Values >= min_threshold become foreground (1).
75+ Default: (0.8, 1.0)
76+
77+ Returns:
78+ numpy.ndarray: Binary segmentation mask with shape matching input spatial dimensions.
79+ Values: 0 (background) or 1 (foreground).
80+ For 3D input: shape :math:`(Z, Y, X)`
81+ For 2D input: shape :math:`(Y, X)`
82+
83+ Examples:
84+ >>> # 3D predictions (normalized [0, 1])
85+ >>> predictions = np.random.rand(2, 64, 128, 128) # (C, Z, Y, X)
86+ >>> binary_mask = decode_binary_thresholding(predictions, threshold_range=(0.8, 1.0))
87+ >>> print(binary_mask.shape) # (64, 128, 128)
88+ >>> print(np.unique(binary_mask)) # [0, 1]
89+
90+ >>> # 3D predictions (uint8 [0, 255])
91+ >>> predictions = np.random.randint(0, 256, (2, 64, 128, 128), dtype=np.uint8)
92+ >>> binary_mask = decode_binary_thresholding(predictions, threshold_range=(0.8, 1.0))
93+
94+ >>> # 2D predictions
95+ >>> predictions = np.random.rand(2, 512, 512) # (C, Y, X)
96+ >>> binary_mask = decode_binary_thresholding(predictions, threshold_range=(0.5, 1.0))
97+ >>> print(binary_mask.shape) # (512, 512)
98+
99+ Note:
100+ - **Auto-detection of value range**: Automatically handles both normalized [0, 1]
101+ and uint8 [0, 255] predictions
102+ - **2D/3D support**: Works with both 2D (C, Y, X) and 3D (C, Z, Y, X) inputs
103+ - **Channel 0 usage**: Uses first channel (predictions[0]) as foreground probability
104+ - **Simple thresholding**: No morphological operations or connected components
105+ - **Post-processing**: Use binary postprocessing config for refinement (opening/closing/CC filtering)
106+
107+ See Also:
108+ - :func:`decode_binary_cc`: Binary threshold + connected components (instance segmentation)
109+ - :func:`decode_binary_watershed`: Binary threshold + watershed (instance segmentation)
110+ - :class:`connectomics.config.BinaryPostprocessingConfig`: For morphological refinement
111+ """
112+ # Extract foreground probability (first channel)
113+ semantic = predictions [0 ]
114+
115+ # Auto-detect if predictions are in [0, 1] or [0, 255] range
116+ max_value = np .max (semantic )
117+ if max_value > 1.0 :
118+ # Assume uint8 range [0, 255]
119+ threshold = threshold_range [0 ] * 255
120+ else :
121+ # Assume normalized range [0, 1]
122+ threshold = threshold_range [0 ]
123+
124+ # Apply thresholding
125+ binary_mask = (semantic > threshold ).astype (np .uint8 )
126+
127+ return binary_mask
128+
129+
55130def decode_binary_cc (
56131 predictions : np .ndarray ,
57132 foreground_threshold : float = 0.8 ,
0 commit comments