@@ -131,7 +131,9 @@ def analyze_and_filter_masks(
131131 if len (props_list ) == 0 :
132132 continue
133133
134- rp = props_list [0 ]
134+ # take the region properties from the segmentation map with the greatest area
135+ rp_areas = [x .area for x in props_list ]
136+ rp = props_list [np .argmax (rp_areas )]
135137 area = rp .area
136138 perimeter = rp .perimeter
137139 if perimeter == 0 :
@@ -141,19 +143,18 @@ def analyze_and_filter_masks(
141143 major_axis = rp .major_axis_length
142144 minor_axis = rp .minor_axis_length
143145 h , w = seg .shape [:2 ]
144- # Using a small margin (2 pixels) to be safe
146+ # Using a small margin (2 pixels) to be safe,
147+ # filter any segmentations with bounding boxes close to the size of the image
148+ # because SAM-2 can sometimes detect the image background itself.
149+ bbox_area = (rp .bbox [2 ] - rp .bbox [0 ]) * (rp .bbox [3 ] - rp .bbox [1 ])
145150 max_allowed_area = (h - 2 ) * (w - 2 )
146- if area >= area_threshold and circ >= circularity_threshold :
151+ if (area >= area_threshold and circ >= circularity_threshold
152+ and bbox_area < max_allowed_area ):
147153 binary_mask = seg .astype ('uint8' ) * 255
148154 contours , _ = cv2 .findContours (binary_mask , cv2 .RETR_LIST , cv2 .CHAIN_APPROX_NONE )
149- # reshape contours for plotting and remove any contours
150- # close to the size of the image because cv2.findContours
151- # can sometimes detect the image edge itself.
152- all_contours = [
153- c .reshape (- 1 , 2 )[:, ::- 1 ]
154- for c in contours
155- if cv2 .contourArea (c ) < max_allowed_area
156- ]
155+ # keep only the largest contour in each segmentation area
156+ # and reshape for plotting
157+ max_contour = max (contours , key = cv2 .contourArea ).squeeze (axis = 1 )
157158 radius = np .sqrt (area / np .pi )
158159 euler_number = rp .euler_number
159160 # output of cucim ``rp`` stores values as objects
@@ -164,7 +165,7 @@ def analyze_and_filter_masks(
164165 euler_number = euler_number .item ()
165166 mask_info = {
166167 'bbox' : rp .bbox ,
167- 'contour' : all_contours ,
168+ 'contour' : max_contour ,
168169 'major_axis' : major_axis ,
169170 'minor_axis' : minor_axis ,
170171 'area' : area ,
@@ -202,7 +203,7 @@ def plot_filtered_masks(
202203 for idx , row in masks_summary_df .iterrows ():
203204 contour = row ['contour' ]
204205 bbox = row ['bbox' ]
205- ax .plot (contour [0 ][ :, 1 ], contour [0 ][ :, 0 ], linewidth = 1 , color = 'blue' )
206+ ax .plot (contour [:, 0 ], contour [:, 1 ], linewidth = 1 , color = 'blue' )
206207 min_row , min_col , max_row , max_col = bbox
207208 rect = Rectangle (
208209 (min_col , min_row ),
@@ -271,7 +272,7 @@ def bubblesam_detection(
271272 )
272273
273274 # save filtered dataframe as parquet file
274- # convert ``contours `` and ``bbox`` columns to list to save as parquet
275+ # convert ``contour `` and ``bbox`` columns to list to save as parquet
275276 save_filtered_df = filtered_df .copy ()
276277 save_filtered_df ["bbox" ] = save_filtered_df ["bbox" ].apply (list )
277278 save_filtered_df ["contour" ] = save_filtered_df ["contour" ].apply (
0 commit comments