MogensR commited on
Commit
ae33908
·
verified ·
1 Parent(s): 69ba459

Update debug_positioning.py

Browse files
Files changed (1) hide show
  1. debug_positioning.py +375 -209
debug_positioning.py CHANGED
@@ -1,237 +1,403 @@
1
  #!/usr/bin/env python3
2
  """
3
- Quick test script to validate positioning fixes before full video processing.
4
- Run this to verify coordinate calculations are working correctly.
 
 
 
 
 
 
5
  """
6
 
7
- import numpy as np
8
  import cv2
 
9
  import os
 
 
10
  from pathlib import Path
 
 
11
 
12
- def test_coordinate_calculations():
13
- """Test coordinate calculations with various placement scenarios"""
14
-
15
- print("=== COORDINATE CALCULATION TESTS ===\n")
16
-
17
- # Simulate video dimensions
18
- video_dims = [(720, 1280), (1080, 1920), (480, 640)] # (H, W)
19
 
20
- test_placements = [
21
- {"name": "Center", "x": 0.5, "y": 0.5, "scale": 1.0},
22
- {"name": "Bottom Center", "x": 0.5, "y": 0.75, "scale": 1.0},
23
- {"name": "Top Right", "x": 0.8, "y": 0.2, "scale": 0.7},
24
- {"name": "Bottom Left Small", "x": 0.2, "y": 0.9, "scale": 0.5},
25
- {"name": "Large Center", "x": 0.5, "y": 0.5, "scale": 1.5},
26
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- for h, w in video_dims:
29
- print(f"Video Resolution: {w}x{h}")
30
- print("-" * 40)
 
31
 
32
- for test in test_placements:
33
- px, py, ps = test["x"], test["y"], test["scale"]
34
-
35
- # Calculate coordinates (same logic as app.py)
36
- sw = max(1, round(w * ps))
37
- sh = max(1, round(h * ps))
38
- cx = round(px * w)
39
- cy = round(py * h)
40
- x0 = cx - sw // 2
41
- y0 = cy - sh // 2
42
-
43
- # Bounds checking
44
- xs0 = max(0, x0)
45
- ys0 = max(0, y0)
46
- xs1 = min(w, x0 + sw)
47
- ys1 = min(h, y0 + sh)
48
-
49
- # Check validity
50
- valid = xs1 > xs0 and ys1 > ys0
51
- clipped = x0 < 0 or y0 < 0 or (x0 + sw) > w or (y0 + sh) > h
52
-
53
- print(f" {test['name']:15} | Center: ({cx:4d}, {cy:4d}) | Size: {sw:4d}x{sh:4d} | Valid: {valid} | Clipped: {clipped}")
 
 
 
 
 
 
54
 
55
- if not valid:
56
- print(f" ERROR: Invalid bounds ({xs0}, {ys0}) to ({xs1}, {ys1})")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
- print()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- def create_test_visualization():
61
- """Create visual test to verify positioning logic"""
62
-
63
- print("=== CREATING POSITIONING VISUALIZATION ===\n")
64
-
65
- # Test canvas
66
- canvas_w, canvas_h = 800, 600
67
- canvas = np.zeros((canvas_h, canvas_w, 3), dtype=np.uint8)
68
- canvas.fill(50) # Dark gray background
69
-
70
- # Test subject dimensions (simulating scaled subject)
71
- subject_w, subject_h = 200, 300
72
-
73
- test_positions = [
74
- {"x": 0.2, "y": 0.3, "color": (255, 0, 0)}, # Red - top left
75
- {"x": 0.8, "y": 0.3, "color": (0, 255, 0)}, # Green - top right
76
- {"x": 0.5, "y": 0.5, "color": (0, 0, 255)}, # Blue - center
77
- {"x": 0.3, "y": 0.8, "color": (255, 255, 0)}, # Cyan - bottom left
78
- {"x": 0.7, "y": 0.8, "color": (255, 0, 255)}, # Magenta - bottom right
79
- ]
80
 
81
- for i, pos in enumerate(test_positions):
82
- px, py = pos["x"], pos["y"]
83
- color = pos["color"]
84
-
85
- # Calculate placement (same as app.py logic)
86
- cx = round(px * canvas_w)
87
- cy = round(py * canvas_h)
88
- x0 = cx - subject_w // 2
89
- y0 = cy - subject_h // 2
90
-
91
- # Bounds
92
- xs0 = max(0, x0)
93
- ys0 = max(0, y0)
94
- xs1 = min(canvas_w, x0 + subject_w)
95
- ys1 = min(canvas_h, y0 + subject_h)
96
-
97
- if xs1 > xs0 and ys1 > ys0:
98
- # Draw subject rectangle
99
- cv2.rectangle(canvas, (xs0, ys0), (xs1-1, ys1-1), color, 2)
100
-
101
- # Draw center cross
102
- cv2.line(canvas, (cx-10, cy), (cx+10, cy), color, 2)
103
- cv2.line(canvas, (cx, cy-10), (cx, cy+10), color, 2)
104
-
105
- # Add label
106
- cv2.putText(canvas, f"({px:.1f}, {py:.1f})", (cx-30, cy-20),
107
- cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1)
108
-
109
- print(f"Position {i+1}: ({px:.1f}, {py:.1f}) -> Center ({cx}, {cy}) -> Rect ({xs0}, {ys0}) to ({xs1}, {ys1})")
110
  else:
111
- print(f"Position {i+1}: ({px:.1f}, {py:.1f}) -> OUT OF BOUNDS")
112
-
113
- # Save test image
114
- output_path = "positioning_test_visualization.png"
115
- cv2.imwrite(output_path, canvas)
116
- print(f"\nVisualization saved: {output_path}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- return output_path
119
 
120
- def test_ui_parameter_flow():
121
- """Test that UI slider values correctly flow through to positioning"""
122
-
123
- print("=== UI PARAMETER FLOW TEST ===\n")
124
 
125
- # Simulate Gradio slider values
126
- gradio_values = {
127
- "place_x": 0.7, # 70% from left
128
- "place_y": 0.3, # 30% from top
129
- "place_scale": 1.2, # 120% scale
130
- "place_feather": 5 # 5px feather
131
- }
132
-
133
- # Simulate placement dict creation (from Gradio interface)
134
- placement = {
135
- "x": gradio_values["place_x"],
136
- "y": gradio_values["place_y"],
137
- "scale": gradio_values["place_scale"],
138
- "feather": gradio_values["place_feather"]
139
- }
140
-
141
- print("Gradio slider values:")
142
- for key, val in gradio_values.items():
143
- print(f" {key}: {val}")
144
-
145
- print(f"\nPlacement dict: {placement}")
146
-
147
- # Test parameter extraction (same as app.py)
148
- px = max(0.0, min(1.0, float(placement.get("x", 0.5))))
149
- py = max(0.0, min(1.0, float(placement.get("y", 0.75))))
150
- ps = max(0.3, min(2.0, float(placement.get("scale", 1.0))))
151
- feather_px = max(0, min(50, int(placement.get("feather", 3))))
152
-
153
- print("\nExtracted and clamped values:")
154
- print(f" px (x): {px}")
155
- print(f" py (y): {py}")
156
- print(f" ps (scale): {ps}")
157
- print(f" feather_px: {feather_px}")
158
-
159
- # Verify no unexpected transformations
160
- expected_x = gradio_values["place_x"]
161
- expected_y = gradio_values["place_y"]
162
- expected_scale = gradio_values["place_scale"]
163
- expected_feather = gradio_values["place_feather"]
164
 
165
- if abs(px - expected_x) < 0.001 and abs(py - expected_y) < 0.001:
166
- print("\n✅ Parameter flow is correct!")
167
- else:
168
- print(f"\n❌ Parameter flow issue!")
169
- print(f" Expected x: {expected_x}, got: {px}")
170
- print(f" Expected y: {expected_y}, got: {py}")
171
 
172
- return px == expected_x and py == expected_y
 
 
 
 
 
 
 
 
 
 
 
173
 
174
- def test_scaling_calculations():
175
- """Test scaling calculations for different scenarios"""
176
-
177
- print("=== SCALING CALCULATION TESTS ===\n")
178
-
179
- base_dims = [(720, 1280), (1080, 1920)]
180
- scale_factors = [0.5, 0.75, 1.0, 1.25, 1.5]
181
-
182
- for h, w in base_dims:
183
- print(f"Base dimensions: {w}x{h}")
184
- print("-" * 30)
185
-
186
- for scale in scale_factors:
187
- # Test both int() and round() approaches
188
- sw_int = max(1, int(w * scale))
189
- sh_int = max(1, int(h * scale))
190
- sw_round = max(1, round(w * scale))
191
- sh_round = max(1, round(h * scale))
 
 
 
 
 
 
 
 
 
192
 
193
- print(f" Scale {scale:4.2f} | int(): {sw_int:4d}x{sh_int:4d} | round(): {sw_round:4d}x{sh_round:4d}")
 
 
 
194
 
195
- if sw_int != sw_round or sh_int != sh_round:
196
- print(f" ^ Different results! Using round() is more accurate.")
197
-
198
- print()
199
-
200
- def main():
201
- """Run all positioning tests"""
202
-
203
- print("🎬 VIDEO BACKGROUND REPLACEMENT - POSITIONING TESTS")
204
- print("=" * 60)
205
-
206
- # Test 1: Coordinate calculations
207
- test_coordinate_calculations()
208
-
209
- # Test 2: UI parameter flow
210
- ui_flow_ok = test_ui_parameter_flow()
211
-
212
- # Test 3: Scaling calculations
213
- test_scaling_calculations()
214
-
215
- # Test 4: Visual verification
216
- viz_path = create_test_visualization()
217
-
218
- # Summary
219
- print("=" * 60)
220
- print("TEST SUMMARY:")
221
- print(f"✅ Coordinate calculations: Tested")
222
- print(f"{'✅' if ui_flow_ok else '❌'} UI parameter flow: {'OK' if ui_flow_ok else 'Issues detected'}")
223
- print(f"✅ Scaling calculations: Tested")
224
- print(f"✅ Visual verification: {viz_path}")
225
 
226
- if not ui_flow_ok:
227
- print("\n⚠️ UI parameter flow issues detected!")
228
- print(" Check that Gradio slider values are correctly passed to placement dict.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
- print(f"\n🔧 Next steps:")
231
- print("1. Review coordinate calculation results above")
232
- print("2. Check the generated visualization image")
233
- print("3. Apply the enhanced composite_frame function to your app.py")
234
- print("4. Test with a small video file first")
235
 
236
  if __name__ == "__main__":
237
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ Debug Enhanced Positioning System for Video Background Replacement
4
+
5
+ Key debugging areas:
6
+ 1. UI parameter flow validation
7
+ 2. Coordinate system debugging
8
+ 3. Alpha/video alignment verification
9
+ 4. Canvas placement mathematics
10
+ 5. Temporal synchronization checks
11
  """
12
 
 
13
  import cv2
14
+ import numpy as np
15
  import os
16
+ import time
17
+ import random
18
  from pathlib import Path
19
+ from moviepy.editor import VideoFileClip
20
+ import logging
21
 
22
+ # Enhanced logging for debugging
23
+ logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
24
+ debug_logger = logging.getLogger("positioning_debug")
25
+
26
+ class PositioningDebugger:
27
+ """Comprehensive debugging for positioning system"""
 
28
 
29
+ def __init__(self, debug_dir="debug_positioning"):
30
+ self.debug_dir = Path(debug_dir)
31
+ self.debug_dir.mkdir(exist_ok=True, parents=True)
32
+ self.frame_count = 0
33
+
34
+ def log_parameters(self, placement_dict):
35
+ """Log all positioning parameters"""
36
+ debug_logger.info("=== POSITIONING PARAMETERS ===")
37
+ debug_logger.info(f"Raw placement dict: {placement_dict}")
38
+
39
+ px = float(placement_dict.get("x", 0.5))
40
+ py = float(placement_dict.get("y", 0.75))
41
+ ps = float(placement_dict.get("scale", 1.0))
42
+ feather_px = int(placement_dict.get("feather", 3))
43
+
44
+ debug_logger.info(f"Parsed - px: {px}, py: {py}, ps: {ps}, feather: {feather_px}")
45
+ debug_logger.info(f"Clamped - px: {max(0.0, min(1.0, px))}, py: {max(0.0, min(1.0, py))}")
46
+ debug_logger.info(f"Scale range - ps: {max(0.3, min(2.0, ps))}")
47
+
48
+ return px, py, ps, feather_px
49
 
50
+ def debug_coordinate_calculation(self, frame_dims, alpha_dims, placement):
51
+ """Debug coordinate calculations step by step"""
52
+ hh, ww = frame_dims # Original video dimensions
53
+ alpha_h, alpha_w = alpha_dims # Alpha video dimensions
54
 
55
+ px, py, ps, feather_px = self.log_parameters(placement)
56
+
57
+ debug_logger.info("=== COORDINATE CALCULATIONS ===")
58
+ debug_logger.info(f"Original video dims: {ww}x{hh}")
59
+ debug_logger.info(f"Alpha video dims: {alpha_w}x{alpha_h}")
60
+
61
+ # Scale calculations
62
+ sw = max(1, int(ww * ps))
63
+ sh = max(1, int(hh * ps))
64
+ debug_logger.info(f"Scaled subject size: {sw}x{sh} (scale factor: {ps})")
65
+
66
+ # Center calculations
67
+ cx = int(px * ww)
68
+ cy = int(py * hh)
69
+ debug_logger.info(f"Target center: ({cx}, {cy})")
70
+
71
+ # Top-left corner calculations
72
+ x0 = int(cx - sw // 2)
73
+ y0 = int(cy - sh // 2)
74
+ debug_logger.info(f"Top-left corner: ({x0}, {y0})")
75
+
76
+ # Clipping calculations
77
+ xs0, ys0 = max(0, x0), max(0, y0)
78
+ xs1, ys1 = min(ww, x0 + sw), min(hh, y0 + sh)
79
+ debug_logger.info(f"Clipped bounds: ({xs0}, {ys0}) to ({xs1}, {ys1})")
80
+
81
+ if xs1 <= xs0 or ys1 <= ys0:
82
+ debug_logger.warning("INVALID BOUNDS - Subject will be clipped completely!")
83
 
84
+ # Source region calculations
85
+ src_x0 = xs0 - x0
86
+ src_y0 = ys0 - y0
87
+ src_x1 = src_x0 + (xs1 - xs0)
88
+ src_y1 = src_y0 + (ys1 - ys0)
89
+ debug_logger.info(f"Source region: ({src_x0}, {src_y0}) to ({src_x1}, {src_y1})")
90
+
91
+ return {
92
+ 'scaled_size': (sw, sh),
93
+ 'center': (cx, cy),
94
+ 'top_left': (x0, y0),
95
+ 'clipped_dest': (xs0, ys0, xs1, ys1),
96
+ 'source_region': (src_x0, src_y0, src_x1, src_y1)
97
+ }
98
+
99
+ def save_debug_frame(self, frame, alpha, composite, frame_idx, coords):
100
+ """Save debug visualization of frame processing"""
101
+ debug_path = self.debug_dir / f"frame_{frame_idx:04d}_debug.png"
102
+
103
+ # Create debug visualization
104
+ h, w = frame.shape[:2]
105
+ debug_viz = np.zeros((h * 2, w * 2, 3), dtype=np.uint8)
106
+
107
+ # Top-left: Original frame
108
+ debug_viz[0:h, 0:w] = cv2.cvtColor((frame * 255).astype(np.uint8), cv2.COLOR_RGB2BGR)
109
+
110
+ # Top-right: Alpha mask (as RGB)
111
+ alpha_viz = cv2.cvtColor((alpha * 255).astype(np.uint8), cv2.COLOR_GRAY2BGR)
112
+ debug_viz[0:h, w:w*2] = alpha_viz
113
 
114
+ # Bottom-left: Composite
115
+ debug_viz[h:h*2, 0:w] = cv2.cvtColor((composite * 255).astype(np.uint8), cv2.COLOR_RGB2BGR)
116
+
117
+ # Bottom-right: Coordinate overlay
118
+ coord_viz = np.zeros((h, w, 3), dtype=np.uint8)
119
+
120
+ # Draw placement indicators
121
+ cx, cy = coords['center']
122
+ sw, sh = coords['scaled_size']
123
+ x0, y0 = coords['top_left']
124
+
125
+ # Draw center cross
126
+ cv2.line(coord_viz, (cx-10, cy), (cx+10, cy), (0, 255, 0), 2)
127
+ cv2.line(coord_viz, (cx, cy-10), (cx, cy+10), (0, 255, 0), 2)
128
+
129
+ # Draw scaled subject bounds
130
+ cv2.rectangle(coord_viz, (x0, y0), (x0 + sw, y0 + sh), (255, 0, 0), 2)
131
+
132
+ # Draw frame bounds
133
+ cv2.rectangle(coord_viz, (0, 0), (w-1, h-1), (255, 255, 255), 1)
134
+
135
+ debug_viz[h:h*2, w:w*2] = coord_viz
136
+
137
+ # Add text annotations
138
+ font = cv2.FONT_HERSHEY_SIMPLEX
139
+ cv2.putText(debug_viz, "Original", (10, 30), font, 0.7, (255, 255, 255), 2)
140
+ cv2.putText(debug_viz, "Alpha", (w + 10, 30), font, 0.7, (255, 255, 255), 2)
141
+ cv2.putText(debug_viz, "Composite", (10, h + 30), font, 0.7, (255, 255, 255), 2)
142
+ cv2.putText(debug_viz, "Coordinates", (w + 10, h + 30), font, 0.7, (255, 255, 255), 2)
143
+
144
+ cv2.imwrite(str(debug_path), debug_viz)
145
+ debug_logger.info(f"Debug frame saved: {debug_path}")
146
 
147
+ def create_enhanced_composite_function(debugger=None):
148
+ """Create composite function with enhanced debugging"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
+ def composite_frame_debug(get_frame, t, original_clip, alpha_clip, bg_rgb, placement, feather_px):
151
+ """Enhanced composite function with comprehensive debugging"""
152
+
153
+ if debugger:
154
+ debugger.frame_count += 1
155
+ debug_logger.info(f"=== PROCESSING FRAME {debugger.frame_count} at t={t:.3f}s ===")
156
+
157
+ # Get original frame
158
+ frame = get_frame(t).astype(np.float32) / 255.0
159
+ hh, ww = frame.shape[:2]
160
+
161
+ # Get alpha with temporal bounds checking
162
+ alpha_duration = alpha_clip.duration or 0
163
+ if alpha_duration > 0:
164
+ alpha_t = min(t, max(0.0, alpha_duration - 0.01))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  else:
166
+ alpha_t = 0.0
167
+
168
+ debug_logger.info(f"Alpha lookup: t={t:.3f}, alpha_t={alpha_t:.3f}, alpha_duration={alpha_duration:.3f}")
169
+
170
+ try:
171
+ a = alpha_clip.get_frame(alpha_t)
172
+ if a.ndim == 3:
173
+ a = a[:, :, 0] # Take first channel if RGB
174
+ a = a.astype(np.float32) / 255.0
175
+ debug_logger.info(f"Alpha frame shape: {a.shape}, range: [{a.min():.3f}, {a.max():.3f}]")
176
+ except Exception as e:
177
+ debug_logger.error(f"Alpha frame error: {e}")
178
+ return (bg_rgb * 255).astype(np.uint8)
179
+
180
+ # Extract placement parameters
181
+ px = max(0.0, min(1.0, float(placement.get("x", 0.5))))
182
+ py = max(0.0, min(1.0, float(placement.get("y", 0.75))))
183
+ ps = max(0.3, min(2.0, float(placement.get("scale", 1.0))))
184
+
185
+ # Debug coordinate calculations
186
+ coords = None
187
+ if debugger:
188
+ coords = debugger.debug_coordinate_calculation((hh, ww), a.shape, placement)
189
+
190
+ # Scale subject and alpha
191
+ sw = max(1, int(ww * ps))
192
+ sh = max(1, int(hh * ps))
193
+
194
+ debug_logger.info(f"Scaling: {ww}x{hh} -> {sw}x{sh} (factor: {ps})")
195
+
196
+ fg_scaled = cv2.resize(frame, (sw, sh), interpolation=cv2.INTER_LINEAR)
197
+ a_scaled = cv2.resize(a, (sw, sh), interpolation=cv2.INTER_LINEAR)
198
+
199
+ # Create canvas
200
+ fg_canvas = np.zeros_like(frame, dtype=np.float32)
201
+ a_canvas = np.zeros((hh, ww), dtype=np.float32)
202
+
203
+ # Calculate placement
204
+ cx = int(px * ww)
205
+ cy = int(py * hh)
206
+ x0 = int(cx - sw // 2)
207
+ y0 = int(cy - sh // 2)
208
+
209
+ # Bounds checking
210
+ xs0, ys0 = max(0, x0), max(0, y0)
211
+ xs1, ys1 = min(ww, x0 + sw), min(hh, y0 + sh)
212
+
213
+ if xs1 <= xs0 or ys1 <= ys0:
214
+ debug_logger.warning("Subject completely outside frame bounds!")
215
+ if debugger:
216
+ debugger.save_debug_frame(frame, a, bg_rgb, debugger.frame_count, coords or {})
217
+ return (bg_rgb * 255).astype(np.uint8)
218
+
219
+ # Place scaled subject
220
+ src_x0 = xs0 - x0
221
+ src_y0 = ys0 - y0
222
+ src_x1 = src_x0 + (xs1 - xs0)
223
+ src_y1 = src_y0 + (ys1 - ys0)
224
+
225
+ try:
226
+ fg_canvas[ys0:ys1, xs0:xs1, :] = fg_scaled[src_y0:src_y1, src_x0:src_x1, :]
227
+ a_canvas[ys0:ys1, xs0:xs1] = a_scaled[src_y0:src_y1, src_x0:src_x1]
228
+ except Exception as e:
229
+ debug_logger.error(f"Canvas placement error: {e}")
230
+ debug_logger.error(f"Canvas region: [{ys0}:{ys1}, {xs0}:{xs1}]")
231
+ debug_logger.error(f"Source region: [{src_y0}:{src_y1}, {src_x0}:{src_x1}]")
232
+ if debugger:
233
+ debugger.save_debug_frame(frame, a, bg_rgb, debugger.frame_count, coords or {})
234
+ return (bg_rgb * 255).astype(np.uint8)
235
+
236
+ # Apply feathering
237
+ if feather_px > 0:
238
+ k = (feather_px * 2 + 1)
239
+ a_canvas = cv2.GaussianBlur(a_canvas, (k, k), feather_px)
240
+
241
+ # Composite
242
+ a3 = a_canvas[:, :, None]
243
+ comp = a3 * fg_canvas + (1.0 - a3) * bg_rgb
244
+ result = np.clip(comp * 255, 0, 255).astype(np.uint8)
245
+
246
+ # Save debug frame periodically
247
+ if debugger and debugger.frame_count % 30 == 1: # Every ~1 second at 30fps
248
+ debugger.save_debug_frame(frame, a_canvas, comp, debugger.frame_count, coords or {})
249
+
250
+ return result
251
 
252
+ return composite_frame_debug
253
 
254
+ def validate_ui_parameter_flow():
255
+ """Test function to validate UI parameters reach processing correctly"""
 
 
256
 
257
+ # Simulate UI parameter values
258
+ test_placements = [
259
+ {"x": 0.5, "y": 0.5, "scale": 1.0, "feather": 3}, # Center
260
+ {"x": 0.2, "y": 0.8, "scale": 0.7, "feather": 5}, # Bottom-left, smaller
261
+ {"x": 0.8, "y": 0.3, "scale": 1.5, "feather": 1}, # Top-right, larger
262
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
+ debugger = PositioningDebugger()
 
 
 
 
 
265
 
266
+ for i, placement in enumerate(test_placements):
267
+ print(f"\n=== TEST CASE {i+1} ===")
268
+
269
+ # Simulate frame dimensions
270
+ frame_dims = (720, 1280) # H, W
271
+ alpha_dims = (720, 1280)
272
+
273
+ coords = debugger.debug_coordinate_calculation(frame_dims, alpha_dims, placement)
274
+
275
+ print(f"Expected center: {coords['center']}")
276
+ print(f"Scaled size: {coords['scaled_size']}")
277
+ print(f"Placement bounds: {coords['clipped_dest']}")
278
 
279
+ def create_debug_enhanced_process_video_main():
280
+ """Enhanced version of process_video_main with debugging"""
281
+
282
+ def process_video_main_debug(
283
+ video_path: str,
284
+ background_path: str = None,
285
+ trim_duration: float = None,
286
+ crf: int = 18,
287
+ preserve_audio_flag: bool = True,
288
+ placement: dict = None,
289
+ use_chunked_processing: bool = False,
290
+ enable_debug: bool = True,
291
+ progress=None,
292
+ ):
293
+ """
294
+ Enhanced process_video_main with comprehensive positioning debugging
295
+ """
296
+
297
+ debugger = PositioningDebugger() if enable_debug else None
298
+ messages = []
299
+
300
+ try:
301
+ if debugger:
302
+ debug_logger.info("=== STARTING DEBUG SESSION ===")
303
+ debug_logger.info(f"Video: {video_path}")
304
+ debug_logger.info(f"Background: {background_path}")
305
+ debug_logger.info(f"Placement: {placement}")
306
 
307
+ # Validate placement parameters early
308
+ placement = placement or {}
309
+ if debugger:
310
+ debugger.log_parameters(placement)
311
 
312
+ # ... [Previous initialization code remains the same] ...
313
+
314
+ # Enhanced composite function with debugging
315
+ def composite_frame(get_frame, t):
316
+ return create_enhanced_composite_function(debugger)(
317
+ get_frame, t, original_clip, alpha_clip, bg_rgb, placement,
318
+ int(placement.get("feather", 3))
319
+ )
320
+
321
+ # ... [Rest of processing remains the same] ...
322
+
323
+ if debugger:
324
+ debug_logger.info("=== DEBUG SESSION COMPLETE ===")
325
+ debug_logger.info(f"Debug files saved to: {debugger.debug_dir}")
326
+
327
+ return result_path, "\n".join(messages)
328
+
329
+ except Exception as e:
330
+ if debugger:
331
+ debug_logger.error(f"Processing failed: {e}")
332
+ raise
 
 
 
 
 
 
 
 
 
333
 
334
+ return process_video_main_debug
335
+
336
+ # Quick fix suggestions for immediate testing
337
+ def quick_positioning_fixes():
338
+ """
339
+ Quick fixes to test for common positioning issues
340
+ """
341
+
342
+ fixes = {
343
+ "coordinate_system_flip": {
344
+ "description": "Test if Y coordinate should be flipped",
345
+ "change": "cy = int((1.0 - py) * hh) # Flip Y coordinate",
346
+ "original": "cy = int(py * hh)"
347
+ },
348
+
349
+ "anchor_point_adjustment": {
350
+ "description": "Test different anchor points for scaling",
351
+ "change": """
352
+ # Try bottom-center anchor instead of center-center
353
+ x0 = int(cx - sw // 2) # Keep X centered
354
+ y0 = int(cy - sh) # Anchor at bottom
355
+ """,
356
+ "original": """
357
+ x0 = int(cx - sw // 2)
358
+ y0 = int(cy - sh // 2)
359
+ """
360
+ },
361
+
362
+ "alpha_scaling_sync": {
363
+ "description": "Ensure alpha and frame scaling are synchronized",
364
+ "change": """
365
+ # Force exact same dimensions
366
+ if a.shape != (hh, ww):
367
+ a = cv2.resize(a, (ww, hh), interpolation=cv2.INTER_LINEAR)
368
+ """,
369
+ "original": "# No explicit resize check"
370
+ },
371
+
372
+ "ui_parameter_validation": {
373
+ "description": "Add parameter validation and logging",
374
+ "change": """
375
+ px = float(placement.get("x", 0.5))
376
+ py = float(placement.get("y", 0.75))
377
+ ps = float(placement.get("scale", 1.0))
378
+
379
+ print(f"DEBUG: px={px}, py={py}, ps={ps}")
380
+ print(f"DEBUG: cx={px*ww}, cy={py*hh}")
381
+ """,
382
+ "original": "# No debug logging"
383
+ }
384
+ }
385
 
386
+ return fixes
 
 
 
 
387
 
388
  if __name__ == "__main__":
389
+ print("=== Video Background Replacement - Positioning Debug ===")
390
+ print("\n1. Running UI parameter validation...")
391
+ validate_ui_parameter_flow()
392
+
393
+ print("\n2. Available quick fixes:")
394
+ fixes = quick_positioning_fixes()
395
+ for fix_name, fix_info in fixes.items():
396
+ print(f"\n{fix_name.upper()}:")
397
+ print(f" Description: {fix_info['description']}")
398
+ print(f" Change: {fix_info['change']}")
399
+
400
+ print("\n3. Debug system ready!")
401
+ print(" - Use PositioningDebugger class in your main pipeline")
402
+ print(" - Enable debug mode: enable_debug=True")
403
+ print(" - Check debug_positioning/ folder for output")