Update processing/two_stage/quality_manager.py
Browse files
processing/two_stage/quality_manager.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
"""
|
| 2 |
-
Enhanced Quality Profile Manager for Two-Stage Processing
|
| 3 |
-
|
| 4 |
"""
|
| 5 |
import os
|
| 6 |
import logging
|
|
@@ -11,27 +11,27 @@
|
|
| 11 |
|
| 12 |
@dataclass
|
| 13 |
class QualityProfile:
|
| 14 |
-
"""Quality profile with all tunable parameters"""
|
| 15 |
# MatAnyone refinement
|
| 16 |
refine_enabled: bool = True
|
| 17 |
refine_cadence: int = 3 # Process every N frames (1=every frame)
|
| 18 |
refine_start_frame: int = 0 # Start refinement from this frame
|
| 19 |
|
| 20 |
-
# Mask processing
|
| 21 |
mask_dilate: int = 5 # Morphological dilation radius
|
| 22 |
mask_blur: int = 3 # Gaussian blur for feathering
|
| 23 |
mask_threshold: float = 0.5 # Binary threshold
|
| 24 |
|
| 25 |
-
# Chroma keying
|
| 26 |
chroma_tolerance: float = 30.0 # Color distance tolerance
|
| 27 |
chroma_softness: float = 10.0 # Edge softness
|
| 28 |
spill_suppression: float = 0.3 # Green/blue spill removal strength
|
| 29 |
|
| 30 |
-
# Hybrid mixing
|
| 31 |
hybrid_mix: float = 0.7 # 0=pure chroma, 1=pure segmentation
|
| 32 |
temporal_smooth: float = 0.2 # Temporal consistency weight
|
| 33 |
|
| 34 |
-
# Edge refinement
|
| 35 |
edge_feather: int = 5 # Edge feathering radius
|
| 36 |
edge_contrast: float = 1.2 # Edge contrast enhancement
|
| 37 |
|
|
@@ -42,19 +42,19 @@ class QualityProfile:
|
|
| 42 |
max_resolution: int = 1080 # Max processing resolution
|
| 43 |
batch_size: int = 1 # Frames to process in parallel
|
| 44 |
|
| 45 |
-
# Define quality presets with
|
| 46 |
QUALITY_PRESETS = {
|
| 47 |
"speed": QualityProfile(
|
| 48 |
refine_enabled=True,
|
| 49 |
refine_cadence=10, # Refine every 10th frame only
|
| 50 |
refine_start_frame=0,
|
| 51 |
-
mask_dilate=
|
| 52 |
mask_blur=1,
|
| 53 |
mask_threshold=0.5,
|
| 54 |
-
chroma_tolerance=
|
| 55 |
chroma_softness=15.0,
|
| 56 |
spill_suppression=0.1, # Minimal spill work
|
| 57 |
-
hybrid_mix=0.
|
| 58 |
temporal_smooth=0.1,
|
| 59 |
edge_feather=2,
|
| 60 |
edge_contrast=1.0,
|
|
@@ -69,9 +69,9 @@ class QualityProfile:
|
|
| 69 |
mask_dilate=5,
|
| 70 |
mask_blur=3,
|
| 71 |
mask_threshold=0.5,
|
| 72 |
-
chroma_tolerance=
|
| 73 |
-
chroma_softness=
|
| 74 |
-
spill_suppression=0.
|
| 75 |
hybrid_mix=0.7, # Favor segmentation
|
| 76 |
temporal_smooth=0.2,
|
| 77 |
edge_feather=5,
|
|
@@ -82,41 +82,41 @@ class QualityProfile:
|
|
| 82 |
),
|
| 83 |
"max": QualityProfile(
|
| 84 |
refine_enabled=True,
|
| 85 |
-
refine_cadence=1, # Refine EVERY frame
|
| 86 |
refine_start_frame=0,
|
| 87 |
-
mask_dilate=
|
| 88 |
-
mask_blur=
|
| 89 |
-
mask_threshold=0.
|
| 90 |
-
chroma_tolerance=
|
| 91 |
-
chroma_softness=
|
| 92 |
-
spill_suppression=0.6, # Strong spill removal
|
| 93 |
-
hybrid_mix=0.
|
| 94 |
-
temporal_smooth=0.
|
| 95 |
-
edge_feather=
|
| 96 |
-
edge_contrast=1.
|
| 97 |
-
bg_blur_sigma=2.
|
| 98 |
-
max_resolution=1920, # Full HD+
|
| 99 |
-
batch_size=1
|
| 100 |
)
|
| 101 |
}
|
| 102 |
|
| 103 |
class QualityManager:
|
| 104 |
-
"""Manages quality profiles and settings"""
|
| 105 |
|
| 106 |
def __init__(self):
|
| 107 |
self.current_profile: Optional[QualityProfile] = None
|
| 108 |
-
self.profile_name: str = "
|
| 109 |
|
| 110 |
def load_profile(self, profile_name: Optional[str] = None) -> QualityProfile:
|
| 111 |
"""Load quality profile from environment or parameter"""
|
| 112 |
-
# Check environment variable first
|
| 113 |
if profile_name is None:
|
| 114 |
-
profile_name = os.environ.get("BFX_QUALITY", "
|
| 115 |
|
| 116 |
# Validate and load profile
|
| 117 |
if profile_name not in QUALITY_PRESETS:
|
| 118 |
-
logger.warning(f"Unknown quality profile '{profile_name}', using '
|
| 119 |
-
profile_name = "
|
| 120 |
|
| 121 |
self.profile_name = profile_name
|
| 122 |
self.current_profile = QUALITY_PRESETS[profile_name]
|
|
@@ -134,6 +134,8 @@ def _profile_summary(self) -> str:
|
|
| 134 |
p = self.current_profile
|
| 135 |
return (
|
| 136 |
f"refine_every={p.refine_cadence} frames, "
|
|
|
|
|
|
|
| 137 |
f"spill={p.spill_suppression:.1f}, "
|
| 138 |
f"hybrid_mix={p.hybrid_mix:.1f}, "
|
| 139 |
f"edge_feather={p.edge_feather}, "
|
|
@@ -152,7 +154,7 @@ def should_refine_frame(self, frame_idx: int) -> bool:
|
|
| 152 |
if frame_idx == 0:
|
| 153 |
return True
|
| 154 |
|
| 155 |
-
# Check cadence
|
| 156 |
return (frame_idx % self.current_profile.refine_cadence) == 0
|
| 157 |
|
| 158 |
def get_params(self) -> Dict[str, Any]:
|
|
@@ -173,8 +175,36 @@ def log_quality_impact(self, frame_idx: int, processing_time: float):
|
|
| 173 |
f"Frame {frame_idx}: Skipped refinement (quality={self.profile_name}, "
|
| 174 |
f"time={processing_time:.3f}s)"
|
| 175 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
-
# Global instance
|
| 178 |
quality_manager = QualityManager()
|
| 179 |
|
| 180 |
def get_quality_profile(profile_name: Optional[str] = None) -> QualityProfile:
|
|
@@ -183,4 +213,15 @@ def get_quality_profile(profile_name: Optional[str] = None) -> QualityProfile:
|
|
| 183 |
|
| 184 |
def should_refine_frame(frame_idx: int) -> bool:
|
| 185 |
"""Check if frame should be refined (convenience function)"""
|
| 186 |
-
return quality_manager.should_refine_frame(frame_idx)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
Enhanced Quality Profile Manager for Two-Stage Processing - MAXIMUM QUALITY VERSION
|
| 3 |
+
Updated to align with maximum quality SAM2 settings and aggressive mask cleaning
|
| 4 |
"""
|
| 5 |
import os
|
| 6 |
import logging
|
|
|
|
| 11 |
|
| 12 |
@dataclass
|
| 13 |
class QualityProfile:
|
| 14 |
+
"""Quality profile with all tunable parameters - optimized for maximum quality"""
|
| 15 |
# MatAnyone refinement
|
| 16 |
refine_enabled: bool = True
|
| 17 |
refine_cadence: int = 3 # Process every N frames (1=every frame)
|
| 18 |
refine_start_frame: int = 0 # Start refinement from this frame
|
| 19 |
|
| 20 |
+
# Mask processing - enhanced for maximum quality
|
| 21 |
mask_dilate: int = 5 # Morphological dilation radius
|
| 22 |
mask_blur: int = 3 # Gaussian blur for feathering
|
| 23 |
mask_threshold: float = 0.5 # Binary threshold
|
| 24 |
|
| 25 |
+
# Chroma keying - tightened for better precision
|
| 26 |
chroma_tolerance: float = 30.0 # Color distance tolerance
|
| 27 |
chroma_softness: float = 10.0 # Edge softness
|
| 28 |
spill_suppression: float = 0.3 # Green/blue spill removal strength
|
| 29 |
|
| 30 |
+
# Hybrid mixing - favor segmentation for better quality
|
| 31 |
hybrid_mix: float = 0.7 # 0=pure chroma, 1=pure segmentation
|
| 32 |
temporal_smooth: float = 0.2 # Temporal consistency weight
|
| 33 |
|
| 34 |
+
# Edge refinement - enhanced for smoother results
|
| 35 |
edge_feather: int = 5 # Edge feathering radius
|
| 36 |
edge_contrast: float = 1.2 # Edge contrast enhancement
|
| 37 |
|
|
|
|
| 42 |
max_resolution: int = 1080 # Max processing resolution
|
| 43 |
batch_size: int = 1 # Frames to process in parallel
|
| 44 |
|
| 45 |
+
# Define quality presets with MAXIMUM QUALITY optimizations
|
| 46 |
QUALITY_PRESETS = {
|
| 47 |
"speed": QualityProfile(
|
| 48 |
refine_enabled=True,
|
| 49 |
refine_cadence=10, # Refine every 10th frame only
|
| 50 |
refine_start_frame=0,
|
| 51 |
+
mask_dilate=2,
|
| 52 |
mask_blur=1,
|
| 53 |
mask_threshold=0.5,
|
| 54 |
+
chroma_tolerance=50.0, # Less precise for speed
|
| 55 |
chroma_softness=15.0,
|
| 56 |
spill_suppression=0.1, # Minimal spill work
|
| 57 |
+
hybrid_mix=0.4, # Favor chroma for speed
|
| 58 |
temporal_smooth=0.1,
|
| 59 |
edge_feather=2,
|
| 60 |
edge_contrast=1.0,
|
|
|
|
| 69 |
mask_dilate=5,
|
| 70 |
mask_blur=3,
|
| 71 |
mask_threshold=0.5,
|
| 72 |
+
chroma_tolerance=35.0,
|
| 73 |
+
chroma_softness=8.0,
|
| 74 |
+
spill_suppression=0.35,
|
| 75 |
hybrid_mix=0.7, # Favor segmentation
|
| 76 |
temporal_smooth=0.2,
|
| 77 |
edge_feather=5,
|
|
|
|
| 82 |
),
|
| 83 |
"max": QualityProfile(
|
| 84 |
refine_enabled=True,
|
| 85 |
+
refine_cadence=1, # Refine EVERY frame for maximum quality
|
| 86 |
refine_start_frame=0,
|
| 87 |
+
mask_dilate=8, # MAXIMUM: Aggressive hole filling to eliminate gaps
|
| 88 |
+
mask_blur=6, # MAXIMUM: Heavy smoothing for seamless edges
|
| 89 |
+
mask_threshold=0.4, # MAXIMUM: Lower threshold to keep more detail
|
| 90 |
+
chroma_tolerance=25.0, # MAXIMUM: Tighter tolerance for precision
|
| 91 |
+
chroma_softness=3.0, # MAXIMUM: Minimal softness for crisp edges
|
| 92 |
+
spill_suppression=0.6, # MAXIMUM: Strong spill removal
|
| 93 |
+
hybrid_mix=0.9, # MAXIMUM: Heavy favor to segmentation over chroma
|
| 94 |
+
temporal_smooth=0.4, # MAXIMUM: Strong temporal consistency
|
| 95 |
+
edge_feather=8, # MAXIMUM: Heavy edge feathering
|
| 96 |
+
edge_contrast=1.8, # MAXIMUM: Strong edge enhancement
|
| 97 |
+
bg_blur_sigma=2.5, # MAXIMUM: More background blur for seamless blend
|
| 98 |
+
max_resolution=1920, # MAXIMUM: Full HD+ processing
|
| 99 |
+
batch_size=1 # Single frame processing for stability
|
| 100 |
)
|
| 101 |
}
|
| 102 |
|
| 103 |
class QualityManager:
|
| 104 |
+
"""Manages quality profiles and settings with maximum quality optimizations"""
|
| 105 |
|
| 106 |
def __init__(self):
|
| 107 |
self.current_profile: Optional[QualityProfile] = None
|
| 108 |
+
self.profile_name: str = "max" # Default to maximum quality
|
| 109 |
|
| 110 |
def load_profile(self, profile_name: Optional[str] = None) -> QualityProfile:
|
| 111 |
"""Load quality profile from environment or parameter"""
|
| 112 |
+
# Check environment variable first, default to max quality
|
| 113 |
if profile_name is None:
|
| 114 |
+
profile_name = os.environ.get("BFX_QUALITY", "max").lower()
|
| 115 |
|
| 116 |
# Validate and load profile
|
| 117 |
if profile_name not in QUALITY_PRESETS:
|
| 118 |
+
logger.warning(f"Unknown quality profile '{profile_name}', using 'max' for best results")
|
| 119 |
+
profile_name = "max"
|
| 120 |
|
| 121 |
self.profile_name = profile_name
|
| 122 |
self.current_profile = QUALITY_PRESETS[profile_name]
|
|
|
|
| 134 |
p = self.current_profile
|
| 135 |
return (
|
| 136 |
f"refine_every={p.refine_cadence} frames, "
|
| 137 |
+
f"mask_dilate={p.mask_dilate}, "
|
| 138 |
+
f"mask_blur={p.mask_blur}, "
|
| 139 |
f"spill={p.spill_suppression:.1f}, "
|
| 140 |
f"hybrid_mix={p.hybrid_mix:.1f}, "
|
| 141 |
f"edge_feather={p.edge_feather}, "
|
|
|
|
| 154 |
if frame_idx == 0:
|
| 155 |
return True
|
| 156 |
|
| 157 |
+
# Check cadence - for max quality, refine every frame
|
| 158 |
return (frame_idx % self.current_profile.refine_cadence) == 0
|
| 159 |
|
| 160 |
def get_params(self) -> Dict[str, Any]:
|
|
|
|
| 175 |
f"Frame {frame_idx}: Skipped refinement (quality={self.profile_name}, "
|
| 176 |
f"time={processing_time:.3f}s)"
|
| 177 |
)
|
| 178 |
+
|
| 179 |
+
def get_max_quality_params(self) -> Dict[str, Any]:
|
| 180 |
+
"""Get parameters specifically optimized for maximum quality gap elimination"""
|
| 181 |
+
max_profile = QUALITY_PRESETS["max"]
|
| 182 |
+
params = asdict(max_profile)
|
| 183 |
+
|
| 184 |
+
# Add specific maximum quality enhancements
|
| 185 |
+
params.update({
|
| 186 |
+
# Aggressive morphological operations
|
| 187 |
+
"morph_close_kernel": 9, # Large closing kernel for gap filling
|
| 188 |
+
"morph_connect_kernel": 7, # Connect nearby regions
|
| 189 |
+
"gaussian_heavy_sigma": 2.0, # Heavy boundary smoothing
|
| 190 |
+
"gaussian_final_sigma": 1.0, # Final edge smoothing
|
| 191 |
+
"dilate_final_kernel": 3, # Final dilation for coverage
|
| 192 |
+
|
| 193 |
+
# Enhanced SAM2 integration
|
| 194 |
+
"sam2_points_per_side": 64, # Double density sampling
|
| 195 |
+
"sam2_confidence_threshold": 0.05, # Accept almost everything
|
| 196 |
+
"sam2_bbox_padding": 0.3, # 30% padding for full coverage
|
| 197 |
+
"sam2_refine_iterations": 5, # Maximum refinement iterations
|
| 198 |
+
|
| 199 |
+
# Quality flags
|
| 200 |
+
"enable_aggressive_cleaning": True,
|
| 201 |
+
"enable_heavy_smoothing": True,
|
| 202 |
+
"enable_gap_elimination": True,
|
| 203 |
+
})
|
| 204 |
+
|
| 205 |
+
return params
|
| 206 |
|
| 207 |
+
# Global instance with maximum quality default
|
| 208 |
quality_manager = QualityManager()
|
| 209 |
|
| 210 |
def get_quality_profile(profile_name: Optional[str] = None) -> QualityProfile:
|
|
|
|
| 213 |
|
| 214 |
def should_refine_frame(frame_idx: int) -> bool:
|
| 215 |
"""Check if frame should be refined (convenience function)"""
|
| 216 |
+
return quality_manager.should_refine_frame(frame_idx)
|
| 217 |
+
|
| 218 |
+
def get_max_quality_settings() -> Dict[str, Any]:
|
| 219 |
+
"""Get maximum quality settings for gap elimination"""
|
| 220 |
+
return quality_manager.get_max_quality_params()
|
| 221 |
+
|
| 222 |
+
def is_max_quality_enabled() -> bool:
|
| 223 |
+
"""Check if maximum quality mode is active"""
|
| 224 |
+
return quality_manager.profile_name == "max"
|
| 225 |
+
|
| 226 |
+
# Initialize with maximum quality by default
|
| 227 |
+
quality_manager.load_profile("max")
|