AnimeOverlord commited on
Commit
85803c6
·
1 Parent(s): 4874ac3

still initial commit

Browse files
Files changed (1) hide show
  1. app.py +117 -41
app.py CHANGED
@@ -12,84 +12,137 @@ USE_GPU_INFERENCE = os.getenv("USE_GPU_INFERENCE", "false").lower() == "true"
12
  try:
13
  if USE_GPU_INFERENCE:
14
  print("🚀 Mode: Full GPU FLUX.2 Klein Inference")
15
- # Modern Class binding syntax for Modal 1.0+
16
  voxel_model_cls = modal.Cls.from_name("flux-klein-voxel-backend", "VoxelModel")
17
  voxel_backend = voxel_model_cls().process_frame
18
  else:
19
  print("🏎️ Mode: Zero-latency WebRTC Passthrough Demo")
20
- # Modern Standalone function lookup syntax for Modal 1.0+
21
  voxel_backend = modal.Function.from_name("flux-klein-voxel-backend", "demo_stream_frame")
22
  except Exception as e:
23
  print(f"⚠️ Could not bind Modal backend function layout: {e}")
24
  voxel_backend = None
25
 
 
 
26
  def process_video_stream(frame: np.ndarray, prompt: str, strength: float) -> np.ndarray:
27
  """
28
- Receives real-time video frames from the browser via WebRTC, compresses them,
29
- ships them to the Modal GPU cluster, and returns the voxelized matrix.
30
  """
31
  if frame is None:
32
  return None
33
 
34
- # Fallback state if the backend app isn't active or authenticated yet
35
  if voxel_backend is None:
36
  output_frame = frame.copy()
37
  cv2.putText(output_frame, "ERROR: Backend App Offline", (20, 40),
38
  cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
39
- cv2.putText(output_frame, "Check HF Space Secrets for MODAL keys.", (20, 70),
40
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
41
  return output_frame
42
 
43
- # Step 1: Compress high-res frames to a lean JPEG byte stream to prevent browser pipe congestion
44
  success, encoded_image = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 85])
45
  if not success:
46
  return frame
47
 
48
  frame_bytes = encoded_image.tobytes()
49
 
50
- # Step 2: Route request to serverless GPU infrastructure
51
  try:
52
- # Dynamically matches positional signature parameters to prevent signature TypeErrors
53
  if USE_GPU_INFERENCE:
54
  processed_bytes = voxel_backend.remote(frame_bytes, prompt, strength)
55
  else:
56
  processed_bytes = voxel_backend.remote(frame_bytes)
57
 
58
- # Step 3: Reconstruction of the returned processed image array
59
  numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
60
- voxel_frame = cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
61
- return voxel_frame
62
-
63
  except Exception as err:
64
- # Handle serverless cold starts visually instead of freezing or crashing the stream
65
  fallback_frame = frame.copy()
66
- cv2.putText(fallback_frame, "⚡ Modal Serverless Warm-up (15-30s)...", (20, 40),
67
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
68
- cv2.putText(fallback_frame, "Loading FLUX Klein weights into cloud VRAM", (20, 70),
69
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
70
  return fallback_frame
71
 
72
- # --- CUSTOM CSS FOR HIGH-TECH SPATIAL AESTHETICS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  custom_css = """
74
- #container { max-width: 1100px; margin: 0 auto; padding-top: 20px; }
75
  .header-text { text-align: center; margin-bottom: 25px; }
76
  .header-text h1 { color: #5c8e32; font-family: 'Courier New', Courier, monospace; font-weight: bold; margin-bottom: 5px; }
77
  .header-text p { color: #666; font-size: 1.1em; }
 
 
 
 
 
 
 
 
 
 
78
  """
79
 
80
- # --- GRADIO INTERFACE ARCHITECTURE ---
81
  with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
82
 
83
- # gr.Group behaves exactly like a <div> tag container for CSS styling wrappers
84
  with gr.Group(elem_id="container"):
85
  with gr.Group(elem_classes="header-text"):
86
  gr.Markdown("# ⛏️ MINECRAFT SPATIAL VOXEL FILTER ⛏️")
87
  gr.Markdown("Transform your physical environment into an interactive, real-time 3D blocky landscape running on FLUX.2 Klein.")
88
 
89
- gr.HTML("<hr style='border: 1px solid #ddd; margin-bottom: 25px;'>")
 
 
 
 
 
 
 
 
90
 
91
  with gr.Row():
92
- # Left Hand Side: Dynamic Parameters & Controls
93
  with gr.Column(scale=1):
94
  gr.Markdown("### 🎛️ Environmental Filters")
95
 
@@ -115,25 +168,48 @@ with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
115
  """
116
  )
117
 
118
- # Right Hand Side: High-Speed WebRTC Viewport
119
  with gr.Column(scale=2):
120
- gr.Markdown("### 📺 Spatial Render Pipeline")
121
 
122
- # FastRTC custom WebRTC component with auto-configured cloudflare turn discovery
123
- webrtc_stream = WebRTC(
124
- label="Live Voxel Viewport",
125
- modality="video",
126
- mode="send-receive",
127
- rtc_configuration=get_cloudflare_turn_credentials
128
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
- # Establish the bidirectional stream wire link
131
- webrtc_stream.stream(
132
- fn=process_video_stream,
133
- inputs=[webrtc_stream, prompt_input, denoise_strength],
134
- outputs=[webrtc_stream],
135
- time_limit=150 # Automatically closes connection after inactivity to prevent runaway token spend
136
  )
137
 
138
- if __name__ == "__main__":
139
- demo.launch()
 
12
  try:
13
  if USE_GPU_INFERENCE:
14
  print("🚀 Mode: Full GPU FLUX.2 Klein Inference")
 
15
  voxel_model_cls = modal.Cls.from_name("flux-klein-voxel-backend", "VoxelModel")
16
  voxel_backend = voxel_model_cls().process_frame
17
  else:
18
  print("🏎️ Mode: Zero-latency WebRTC Passthrough Demo")
 
19
  voxel_backend = modal.Function.from_name("flux-klein-voxel-backend", "demo_stream_frame")
20
  except Exception as e:
21
  print(f"⚠️ Could not bind Modal backend function layout: {e}")
22
  voxel_backend = None
23
 
24
+ # --- STREAM HANDLING ARCHITECTURE ---
25
+
26
  def process_video_stream(frame: np.ndarray, prompt: str, strength: float) -> np.ndarray:
27
  """
28
+ Handles single viewport mode (Minecraft Filter).
29
+ Receives frames, processes them via Modal, and returns them to the same component.
30
  """
31
  if frame is None:
32
  return None
33
 
 
34
  if voxel_backend is None:
35
  output_frame = frame.copy()
36
  cv2.putText(output_frame, "ERROR: Backend App Offline", (20, 40),
37
  cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
 
 
38
  return output_frame
39
 
 
40
  success, encoded_image = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 85])
41
  if not success:
42
  return frame
43
 
44
  frame_bytes = encoded_image.tobytes()
45
 
 
46
  try:
 
47
  if USE_GPU_INFERENCE:
48
  processed_bytes = voxel_backend.remote(frame_bytes, prompt, strength)
49
  else:
50
  processed_bytes = voxel_backend.remote(frame_bytes)
51
 
 
52
  numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
53
+ return cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
 
 
54
  except Exception as err:
 
55
  fallback_frame = frame.copy()
56
+ cv2.putText(fallback_frame, "⚡ Serverless Warm-up...", (20, 40),
57
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
 
 
58
  return fallback_frame
59
 
60
+
61
+ def process_demo_stream(frame: np.ndarray) -> np.ndarray:
62
+ """
63
+ Handles separate multi-viewport tracking (Streaming Demo).
64
+ Ingests frames from the raw hardware box and outputs onto the separate Modal display container.
65
+ """
66
+ if frame is None:
67
+ return None
68
+
69
+ # High-visibility on-screen error rendering for Demo Mode
70
+ if voxel_backend is None:
71
+ error_frame = np.zeros((480, 640, 3), dtype=np.uint8)
72
+ error_frame[:] = 30 # Dark slate gray canvas
73
+ cv2.putText(error_frame, "⚠️ BACKEND APP OFFLINE", (40, 200),
74
+ cv2.FONT_HERSHEY_DUPLEX, 0.9, (0, 0, 255), 2)
75
+ cv2.putText(error_frame, "Status: Inactive or unauthorized Modal keys.", (40, 250),
76
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (180, 180, 180), 1)
77
+ return error_frame
78
+
79
+ success, encoded_image = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 85])
80
+ if not success:
81
+ return frame
82
+
83
+ frame_bytes = encoded_image.tobytes()
84
+
85
+ try:
86
+ if USE_GPU_INFERENCE:
87
+ processed_bytes = voxel_backend.remote(frame_bytes, "vanilla minecraft voxel landscape", 0.55)
88
+ else:
89
+ processed_bytes = voxel_backend.remote(frame_bytes)
90
+
91
+ numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
92
+ return cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
93
+
94
+ except Exception as err:
95
+ # Format and write runtime cluster/cold-start exceptions right onto the video frame bounding container
96
+ error_frame = np.zeros((480, 640, 3), dtype=np.uint8)
97
+ error_frame[:] = 20
98
+ cv2.putText(error_frame, "⚡ MODAL RUNTIME EXCEPTION", (40, 180),
99
+ cv2.FONT_HERSHEY_DUPLEX, 0.8, (0, 140, 255), 2)
100
+
101
+ err_msg = str(err)
102
+ cv2.putText(error_frame, f"Error trace: {err_msg[:50]}...", (40, 240),
103
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
104
+ cv2.putText(error_frame, "Check cluster logs for container scaling behavior.", (40, 290),
105
+ cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 255), 1)
106
+ return error_frame
107
+
108
+
109
+ # --- UI LAYOUT & STYLE RULES ---
110
  custom_css = """
111
+ #container { max-width: 1200px; margin: 0 auto; padding-top: 20px; }
112
  .header-text { text-align: center; margin-bottom: 25px; }
113
  .header-text h1 { color: #5c8e32; font-family: 'Courier New', Courier, monospace; font-weight: bold; margin-bottom: 5px; }
114
  .header-text p { color: #666; font-size: 1.1em; }
115
+
116
+ /* Constrain video bounding tracks to stop screen-flooding layout shifts */
117
+ video, .webrtc-video, div[class*="webrtc"] {
118
+ max-height: 440px !important;
119
+ width: 100% !important;
120
+ max-width: 580px !important;
121
+ margin: 0 auto !important;
122
+ object-fit: contain !important;
123
+ border-radius: 10px;
124
+ }
125
  """
126
 
 
127
  with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
128
 
 
129
  with gr.Group(elem_id="container"):
130
  with gr.Group(elem_classes="header-text"):
131
  gr.Markdown("# ⛏️ MINECRAFT SPATIAL VOXEL FILTER ⛏️")
132
  gr.Markdown("Transform your physical environment into an interactive, real-time 3D blocky landscape running on FLUX.2 Klein.")
133
 
134
+ gr.HTML("<hr style='border: 1px solid #ddd; margin-bottom: 20px;'>")
135
+
136
+ # Context switcher selection interface
137
+ mode_dropdown = gr.Dropdown(
138
+ choices=["Streaming Demo", "Minecraft Filter"],
139
+ value="Minecraft Filter",
140
+ label="🎯 Pipeline View Configuration",
141
+ interactive=True
142
+ )
143
 
144
  with gr.Row():
145
+ # Left Hand Side Settings Column
146
  with gr.Column(scale=1):
147
  gr.Markdown("### 🎛️ Environmental Filters")
148
 
 
168
  """
169
  )
170
 
171
+ # Right Hand Side Viewport Area
172
  with gr.Column(scale=2):
 
173
 
174
+ # --- LAYOUT VARIANT 1: MINECRAFT FILTER MODE (Standard Single Viewport) ---
175
+ with gr.Column(visible=True) as minecraft_layout:
176
+ gr.Markdown("### 📺 Spatial Render Pipeline")
177
+ webrtc_single = WebRTC(
178
+ label="Live Voxel Viewport",
179
+ modality="video",
180
+ mode="send-receive",
181
+ rtc_configuration=get_cloudflare_turn_credentials
182
+ )
183
+
184
+ # --- LAYOUT VARIANT 2: STREAMING DEMO MODE (Decoupled Dual Viewports) ---
185
+ with gr.Column(visible=False) as demo_layout:
186
+ gr.Markdown("### 📺 Dual-Feed Stream Monitor")
187
+ with gr.Row():
188
+ webrtc_raw = WebRTC(
189
+ label="1. Your Camera Feed (Raw Local)",
190
+ modality="video",
191
+ mode="send",
192
+ rtc_configuration=get_cloudflare_turn_credentials
193
+ )
194
+ webrtc_processed = WebRTC(
195
+ label="2. Streamed from Modal (Processed Backend)",
196
+ modality="video",
197
+ mode="receive",
198
+ rtc_configuration=get_cloudflare_turn_credentials
199
+ )
200
+
201
+ # --- ROUTING LOGIC & EVENT WIRES ---
202
+
203
+ def switch_layout(selected_mode):
204
+ if selected_mode == "Minecraft Filter":
205
+ return gr.update(visible=True), gr.update(visible=False)
206
+ else:
207
+ return gr.update(visible=False), gr.update(visible=True)
208
 
209
+ mode_dropdown.change(
210
+ fn=switch_layout,
211
+ inputs=[mode_dropdown],
212
+ outputs=[minecraft_layout, demo_layout]
 
 
213
  )
214
 
215
+ # Wire