MogensR commited on
Commit
2ada5b3
·
verified ·
1 Parent(s): fd5031f

Update processing/two_stage/two_stage_processor.py

Browse files
processing/two_stage/two_stage_processor.py CHANGED
@@ -12,7 +12,7 @@
12
  - Fix: Add logging for background preparation issue
13
  - COMPOSITING FIX: Normalize frame and background scales to prevent dark backgrounds
14
  - MAJOR FIX: Use enhanced SAM2Handler instead of basic segment_person_hq
15
- - MAXIMUM QUALITY: Added aggressive mask cleaning for gap elimination
16
  """
17
  from __future__ import annotations
18
  import cv2, numpy as np, os, gc, pickle, logging, tempfile, traceback, threading
@@ -181,7 +181,7 @@ def _choose_best_key_color(frame_bgr: np.ndarray, mask_uint8: np.ndarray) -> dic
181
  }
182
 
183
  # ---------------------------------------------------------------------------
184
- # Two-Stage Processor - MAXIMUM QUALITY VERSION
185
  # ---------------------------------------------------------------------------
186
  class TwoStageProcessor:
187
  def __init__(self, sam2_predictor=None, matanyone_model=None):
@@ -215,8 +215,8 @@ def _unwrap_sam2(self, predictor):
215
  return predictor
216
 
217
  def _get_mask(self, frame: np.ndarray) -> np.ndarray:
218
- """MAXIMUM QUALITY mask with enhanced cleaning."""
219
- logger.info("=== TwoStageProcessor _get_mask called (MAX QUALITY) ===")
220
 
221
  if self.sam2_handler is None:
222
  logger.warning("No SAM2Handler available - using fallback threshold")
@@ -226,17 +226,17 @@ def _get_mask(self, frame: np.ndarray) -> np.ndarray:
226
 
227
  try:
228
  if hasattr(self.sam2_handler, 'create_mask'):
229
- logger.info("Using ENHANCED SAM2Handler.create_mask() with MAXIMUM QUALITY")
230
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
231
  mask = self.sam2_handler.create_mask(frame_rgb)
232
 
233
  if mask is not None:
234
- # MAXIMUM QUALITY POST-PROCESSING
235
- mask = self._maximum_quality_mask_cleaning(mask)
236
- logger.info(f"Enhanced SAM2 mask with max quality cleaning - coverage: {np.mean(mask/255.0):.3f}")
237
  return mask
238
  else:
239
- logger.warning("Enhanced SAM2Handler returned None mask")
240
  else:
241
  logger.warning("SAM2Handler doesn't have create_mask method")
242
 
@@ -246,7 +246,8 @@ def _get_mask(self, frame: np.ndarray) -> np.ndarray:
246
  try:
247
  from utils.cv_processing import segment_person_hq
248
  mask = segment_person_hq(frame, self.sam2)
249
- mask = self._maximum_quality_mask_cleaning(mask)
 
250
  return mask
251
  except ImportError:
252
  logger.warning("Could not import segment_person_hq")
@@ -254,7 +255,7 @@ def _get_mask(self, frame: np.ndarray) -> np.ndarray:
254
  logger.warning(f"Basic SAM2 segmentation failed: {e}")
255
 
256
  except Exception as e:
257
- logger.error(f"Enhanced SAM2 segmentation failed: {e}")
258
 
259
  # Final fallback
260
  logger.warning("All SAM2 methods failed - using simple threshold")
@@ -263,41 +264,47 @@ def _get_mask(self, frame: np.ndarray) -> np.ndarray:
263
  return mask
264
 
265
  def _maximum_quality_mask_cleaning(self, mask: np.ndarray) -> np.ndarray:
266
- """Maximum quality mask cleaning and refinement."""
267
- try:
268
- # Ensure uint8 format
269
- if mask.max() <= 1.0:
270
- mask_uint8 = (mask * 255).astype(np.uint8)
271
- else:
272
- mask_uint8 = mask.astype(np.uint8)
273
-
274
- # Step 1: Fill small holes aggressively
275
- kernel_fill = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9))
276
- mask_filled = cv2.morphologyEx(mask_uint8, cv2.MORPH_CLOSE, kernel_fill)
277
-
278
- # Step 2: Connect nearby regions
279
- kernel_connect = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
280
- mask_connected = cv2.morphologyEx(mask_filled, cv2.MORPH_CLOSE, kernel_connect)
281
-
282
- # Step 3: Smooth boundaries heavily
283
- mask_smooth1 = cv2.GaussianBlur(mask_connected, (7, 7), 2.0)
284
-
285
- # Step 4: Re-threshold to crisp edges
286
- _, mask_thresh = cv2.threshold(mask_smooth1, 127, 255, cv2.THRESH_BINARY)
287
-
288
- # Step 5: Final edge smoothing
289
- mask_final = cv2.GaussianBlur(mask_thresh, (5, 5), 1.0)
290
-
291
- # Step 6: Dilate slightly to ensure full coverage
292
- kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
293
- mask_dilated = cv2.dilate(mask_final, kernel_dilate, iterations=1)
294
-
295
- logger.info("Maximum quality mask cleaning applied successfully")
296
- return mask_dilated
297
-
298
- except Exception as e:
299
- logger.warning(f"Maximum quality mask cleaning failed: {e}")
300
- return mask
 
 
 
 
 
 
301
 
302
  @staticmethod
303
  def _to_binary_mask(mask: np.ndarray) -> Optional[np.ndarray]:
@@ -466,8 +473,8 @@ def _prog(p, d):
466
  if not ok:
467
  break
468
 
469
- # --- ENHANCED SAM2 segmentation with full-body detection ---
470
- logger.info(f"Processing frame {frame_idx} with enhanced SAM2")
471
  mask = self._get_mask(frame)
472
 
473
  # --- MatAnyone bootstrap exactly once (first frame) ---
 
12
  - Fix: Add logging for background preparation issue
13
  - COMPOSITING FIX: Normalize frame and background scales to prevent dark backgrounds
14
  - MAJOR FIX: Use enhanced SAM2Handler instead of basic segment_person_hq
15
+ - TESTING FIX: Disabled double mask cleaning to eliminate artifacts
16
  """
17
  from __future__ import annotations
18
  import cv2, numpy as np, os, gc, pickle, logging, tempfile, traceback, threading
 
181
  }
182
 
183
  # ---------------------------------------------------------------------------
184
+ # Two-Stage Processor - TESTING: NO DOUBLE CLEANING
185
  # ---------------------------------------------------------------------------
186
  class TwoStageProcessor:
187
  def __init__(self, sam2_predictor=None, matanyone_model=None):
 
215
  return predictor
216
 
217
  def _get_mask(self, frame: np.ndarray) -> np.ndarray:
218
+ """TESTING: Disable double cleaning to isolate mask quality issues."""
219
+ logger.info("=== TwoStageProcessor _get_mask called (TESTING - NO DOUBLE CLEANING) ===")
220
 
221
  if self.sam2_handler is None:
222
  logger.warning("No SAM2Handler available - using fallback threshold")
 
226
 
227
  try:
228
  if hasattr(self.sam2_handler, 'create_mask'):
229
+ logger.info("Using SAM2Handler.create_mask() WITHOUT additional cleaning")
230
  frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
231
  mask = self.sam2_handler.create_mask(frame_rgb)
232
 
233
  if mask is not None:
234
+ # DISABLED: No additional cleaning - let SAM2Handler's cleaning stand
235
+ # mask = self._maximum_quality_mask_cleaning(mask) # DISABLED FOR TESTING
236
+ logger.info(f"SAM2 mask WITHOUT additional cleaning - coverage: {np.mean(mask/255.0):.3f}")
237
  return mask
238
  else:
239
+ logger.warning("SAM2Handler returned None mask")
240
  else:
241
  logger.warning("SAM2Handler doesn't have create_mask method")
242
 
 
246
  try:
247
  from utils.cv_processing import segment_person_hq
248
  mask = segment_person_hq(frame, self.sam2)
249
+ # DISABLED: No additional cleaning in fallback either
250
+ # mask = self._maximum_quality_mask_cleaning(mask) # DISABLED FOR TESTING
251
  return mask
252
  except ImportError:
253
  logger.warning("Could not import segment_person_hq")
 
255
  logger.warning(f"Basic SAM2 segmentation failed: {e}")
256
 
257
  except Exception as e:
258
+ logger.error(f"SAM2 segmentation failed: {e}")
259
 
260
  # Final fallback
261
  logger.warning("All SAM2 methods failed - using simple threshold")
 
264
  return mask
265
 
266
  def _maximum_quality_mask_cleaning(self, mask: np.ndarray) -> np.ndarray:
267
+ """Maximum quality mask cleaning and refinement - CURRENTLY DISABLED."""
268
+ # THIS METHOD IS DISABLED TO PREVENT DOUBLE CLEANING
269
+ # The SAM2Handler already applies its own cleaning
270
+ logger.info("Maximum quality mask cleaning DISABLED to prevent double processing")
271
+ return mask
272
+
273
+ # ORIGINAL CODE COMMENTED OUT:
274
+ # try:
275
+ # # Ensure uint8 format
276
+ # if mask.max() <= 1.0:
277
+ # mask_uint8 = (mask * 255).astype(np.uint8)
278
+ # else:
279
+ # mask_uint8 = mask.astype(np.uint8)
280
+ #
281
+ # # Step 1: Fill small holes aggressively
282
+ # kernel_fill = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9))
283
+ # mask_filled = cv2.morphologyEx(mask_uint8, cv2.MORPH_CLOSE, kernel_fill)
284
+ #
285
+ # # Step 2: Connect nearby regions
286
+ # kernel_connect = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
287
+ # mask_connected = cv2.morphologyEx(mask_filled, cv2.MORPH_CLOSE, kernel_connect)
288
+ #
289
+ # # Step 3: Smooth boundaries heavily
290
+ # mask_smooth1 = cv2.GaussianBlur(mask_connected, (7, 7), 2.0)
291
+ #
292
+ # # Step 4: Re-threshold to crisp edges
293
+ # _, mask_thresh = cv2.threshold(mask_smooth1, 127, 255, cv2.THRESH_BINARY)
294
+ #
295
+ # # Step 5: Final edge smoothing
296
+ # mask_final = cv2.GaussianBlur(mask_thresh, (5, 5), 1.0)
297
+ #
298
+ # # Step 6: Dilate slightly to ensure full coverage
299
+ # kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
300
+ # mask_dilated = cv2.dilate(mask_final, kernel_dilate, iterations=1)
301
+ #
302
+ # logger.info("Maximum quality mask cleaning applied successfully")
303
+ # return mask_dilated
304
+ #
305
+ # except Exception as e:
306
+ # logger.warning(f"Maximum quality mask cleaning failed: {e}")
307
+ # return mask
308
 
309
  @staticmethod
310
  def _to_binary_mask(mask: np.ndarray) -> Optional[np.ndarray]:
 
473
  if not ok:
474
  break
475
 
476
+ # --- SAM2 segmentation WITHOUT double cleaning ---
477
+ logger.info(f"Processing frame {frame_idx} with SAM2 (no double cleaning)")
478
  mask = self._get_mask(frame)
479
 
480
  # --- MatAnyone bootstrap exactly once (first frame) ---