AnimeOverlord commited on
Commit
94f0003
·
1 Parent(s): 0c7612d

still initial commit

Browse files
Files changed (1) hide show
  1. app.py +71 -126
app.py CHANGED
@@ -3,7 +3,7 @@ import cv2
3
  import numpy as np
4
  import gradio as gr
5
  import modal
6
- from fastrtc import WebRTC, get_cloudflare_turn_credentials
7
 
8
  # ── Modal Backend ───────────────────────────────────────────────────────────
9
  try:
@@ -18,17 +18,28 @@ except Exception as e:
18
  print(f"❌ Modal backend offline: {ex}")
19
  voxel_backend = None
20
 
 
 
 
21
 
22
  # ── Helpers ─────────────────────────────────────────────────────────────────
23
- def _run_voxel_backend(frame: np.ndarray, prompt: str, strength: float) -> np.ndarray:
 
 
 
 
 
 
 
 
 
 
24
  success, encoded = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 85])
25
  if not success:
26
  return frame
27
  try:
28
- try:
29
- processed_bytes = voxel_backend.remote(encoded.tobytes(), prompt, strength)
30
- except TypeError:
31
- processed_bytes = voxel_backend.remote(encoded.tobytes())
32
  result = cv2.imdecode(np.frombuffer(processed_bytes, dtype=np.uint8), cv2.IMREAD_COLOR)
33
  return result if result is not None else frame
34
  except Exception as err:
@@ -38,134 +49,68 @@ def _run_voxel_backend(frame: np.ndarray, prompt: str, strength: float) -> np.nd
38
  return out
39
 
40
 
41
- def _offline_frame(frame: np.ndarray, message: str) -> np.ndarray:
42
- h, w = (frame.shape[:2] if frame is not None else (480, 640))
43
- out = np.zeros((h, w, 3), dtype=np.uint8)
44
- cv2.putText(out, message, (max(10, w // 8), h // 2),
45
- cv2.FONT_HERSHEY_DUPLEX, 0.8, (0, 0, 220), 2)
46
- return out
47
-
48
-
49
- # ── Stream Handlers ─────────────────────────────────────────────────────────
50
- def process_video_stream(frame: np.ndarray, prompt: str, strength: float) -> np.ndarray:
51
  if frame is None:
52
  return None
53
- if voxel_backend is None:
54
- return _offline_frame(frame, "Modal Backend Offline")
55
- return _run_voxel_backend(frame, prompt, strength)
56
-
57
 
58
- def process_demo_stream(frame: np.ndarray) -> np.ndarray:
59
- if frame is None:
60
- return None
61
  if voxel_backend is None:
62
- err = _offline_frame(frame, "Backend Offline")
63
- return np.hstack([frame, err])
64
- processed = _run_voxel_backend(
65
- frame,
66
- prompt="vanilla minecraft voxel landscape, 3d blocky style, cube aesthetic",
67
- strength=0.55,
68
- )
69
- if processed.shape[0] != frame.shape[0]:
70
- processed = cv2.resize(processed, (frame.shape[1], frame.shape[0]))
71
- raw_labeled = frame.copy()
72
- cv2.putText(raw_labeled, "RAW", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
73
- cv2.putText(processed, "MINECRAFT", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
74
- return np.hstack([raw_labeled, processed])
75
-
76
-
77
- # ── CSS: official Gradio WebRTC pattern (from gradio.app/guides) ───────────
78
- # Target gr.Group and gr.Column via elem_classes — NOT the WebRTC component
79
- css = """
80
- .webrtc-group { max-width: 600px !important; max-height: 600px !important; }
81
- .webrtc-col { display: flex !important; justify-content: center !important; align-items: center !important; }
82
- .demo-group { max-width: 1000px !important; max-height: 600px !important; }
83
- """
84
-
85
- status_color = "🟢" if voxel_backend is not None else "🔴"
86
- status_text = "Connected" if voxel_backend is not None else "Offline"
87
-
88
- # ── UI ───────────────────────────────────────────────────────────────────────
89
- with gr.Blocks(title="Minecraft Spatial Voxel Filter", css=css) as demo:
90
-
91
- gr.HTML("""
92
- <div style='text-align:center; padding: 16px 0'>
93
- <h1 style='margin:0'>⛏️ MINECRAFT SPATIAL VOXEL FILTER ⛏️</h1>
94
- <p style='margin:6px 0 0 0'>Transform your environment into a real-time blocky landscape</p>
95
- </div>
96
- """)
97
-
98
- # ── Shared controls (always visible) ─────────────────────────────────────
99
- with gr.Row():
100
- mode_dropdown = gr.Dropdown(
101
  choices=["Minecraft Filter", "Streaming Demo"],
102
  value="Minecraft Filter",
103
  label="🎯 Pipeline Mode",
104
  interactive=True,
 
 
 
 
 
105
  )
106
- prompt_input = gr.Textbox(
107
- value="vanilla minecraft voxel landscape, 3d blocky style, retro game cube aesthetic",
108
- label="Biome Blueprint",
109
- lines=1,
110
- )
111
- denoise_strength = gr.Slider(
112
- minimum=0.1, maximum=1.0, step=0.05, value=0.55,
113
- label="Denoising Strength",
114
- )
115
- gr.Markdown(f"**Backend:** {status_color} `{status_text}`")
116
-
117
- # ── Mode 1: Minecraft Filter ──────────────────────────────────────────────
118
- # OFFICIAL PATTERN: gr.Column(elem_classes) → gr.Group(elem_classes) → WebRTC
119
- with gr.Column(elem_classes=["webrtc-col"], visible=True) as minecraft_layout:
120
- gr.Markdown("### 📺 Spatial Render Pipeline")
121
- with gr.Group(elem_classes=["webrtc-group"]):
122
- webrtc_single = WebRTC(
123
- label="Live Voxel Viewport",
124
- modality="video",
125
- mode="send-receive",
126
- rtc_configuration=get_cloudflare_turn_credentials,
127
- )
128
-
129
- # ── Mode 2: Streaming Demo ────────────────────────────────────────────────
130
- with gr.Column(elem_classes=["webrtc-col"], visible=False) as demo_layout:
131
- gr.Markdown("### 📺 Dual-Feed · _Left: Raw · Right: Minecraft_")
132
- with gr.Group(elem_classes=["demo-group"]):
133
- webrtc_demo = WebRTC(
134
- label="Live Side-by-Side Feed",
135
- modality="video",
136
- mode="send-receive",
137
- rtc_configuration=get_cloudflare_turn_credentials,
138
- )
139
-
140
- # ── Mode Switch ───────────────────────────────────────────────────────────
141
- def switch_layout(selected_mode):
142
- return (
143
- gr.update(visible=selected_mode == "Minecraft Filter"),
144
- gr.update(visible=selected_mode == "Streaming Demo"),
145
- )
146
-
147
- mode_dropdown.change(
148
- fn=switch_layout,
149
- inputs=[mode_dropdown],
150
- outputs=[minecraft_layout, demo_layout],
151
- )
152
-
153
- # ── Stream Bindings ───────────────────────────────────────────────────────
154
- webrtc_single.stream(
155
- fn=process_video_stream,
156
- inputs=[webrtc_single, prompt_input, denoise_strength],
157
- outputs=[webrtc_single],
158
- time_limit=150,
159
- concurrency_limit=4,
160
- )
161
-
162
- webrtc_demo.stream(
163
- fn=process_demo_stream,
164
- inputs=[webrtc_demo],
165
- outputs=[webrtc_demo],
166
- time_limit=150,
167
- concurrency_limit=4,
168
- )
169
 
170
  if __name__ == "__main__":
171
- demo.launch()
 
3
  import numpy as np
4
  import gradio as gr
5
  import modal
6
+ from fastrtc import Stream, get_cloudflare_turn_credentials
7
 
8
  # ── Modal Backend ───────────────────────────────────────────────────────────
9
  try:
 
18
  print(f"❌ Modal backend offline: {ex}")
19
  voxel_backend = None
20
 
21
+ status_color = "🟢" if voxel_backend is not None else "🔴"
22
+ status_text = "Connected" if voxel_backend is not None else "Offline"
23
+
24
 
25
  # ── Helpers ─────────────────────────────────────────────────────────────────
26
+ def _offline_frame(frame: np.ndarray, message: str) -> np.ndarray:
27
+ """Generates a styled placeholder frame when backend is offline."""
28
+ h, w = (frame.shape[:2] if frame is not None else (480, 640))
29
+ out = np.zeros((h, w, 3), dtype=np.uint8)
30
+ cv2.putText(out, message, (max(10, w // 8), h // 2),
31
+ cv2.FONT_HERSHEY_DUPLEX, 0.8, (0, 0, 220), 2)
32
+ return out
33
+
34
+
35
+ def _run_voxel_backend(frame: np.ndarray) -> np.ndarray:
36
+ """Encodes and ships raw image bytes to the Modal worker without extra parameters."""
37
  success, encoded = cv2.imencode(".jpg", frame, [int(cv2.IMWRITE_JPEG_QUALITY), 85])
38
  if not success:
39
  return frame
40
  try:
41
+ # Prompt and strength arguments have been fully stripped here
42
+ processed_bytes = voxel_backend.remote(encoded.tobytes())
 
 
43
  result = cv2.imdecode(np.frombuffer(processed_bytes, dtype=np.uint8), cv2.IMREAD_COLOR)
44
  return result if result is not None else frame
45
  except Exception as err:
 
49
  return out
50
 
51
 
52
+ # ── Unified Core Stream Handler ─────────────────────────────────────────────
53
+ def process_video_stream(frame: np.ndarray, mode: str) -> np.ndarray:
54
+ """
55
+ Unified real-time handler mapping directly to FastRTC's stream pipeline.
56
+ Accepts incoming frame along with the UI's selected operating mode.
57
+ """
 
 
 
 
58
  if frame is None:
59
  return None
 
 
 
 
60
 
 
 
 
61
  if voxel_backend is None:
62
+ err_frame = _offline_frame(frame, "Modal Backend Offline")
63
+ if mode == "Streaming Demo":
64
+ return np.hstack([frame, err_frame])
65
+ return err_frame
66
+
67
+ # Process via the lightweight single-image pipeline
68
+ processed = _run_voxel_backend(frame)
69
+
70
+ # Mode A: Full view rendering
71
+ if mode == "Minecraft Filter":
72
+ return processed
73
+
74
+ # Mode B: Side-by-side split rendering
75
+ elif mode == "Streaming Demo":
76
+ if processed.shape[0] != frame.shape[0]:
77
+ processed = cv2.resize(processed, (frame.shape[1], frame.shape[0]))
78
+
79
+ raw_labeled = frame.copy()
80
+ cv2.putText(raw_labeled, "RAW", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
81
+ cv2.putText(processed, "MINECRAFT", (10, 28), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 255, 255), 2)
82
+ return np.hstack([raw_labeled, processed])
83
+
84
+ return processed
85
+
86
+
87
+ # ── Setup Stream and Auto-UI Generation ──────────────────────────────────────
88
+ stream = Stream(
89
+ handler=process_video_stream,
90
+ modality="video",
91
+ mode="send-receive",
92
+ rtc_configuration=get_cloudflare_turn_credentials(),
93
+ additional_inputs=[
94
+ gr.Dropdown(
 
 
 
 
 
 
95
  choices=["Minecraft Filter", "Streaming Demo"],
96
  value="Minecraft Filter",
97
  label="🎯 Pipeline Mode",
98
  interactive=True,
99
+ ),
100
+ gr.Markdown(
101
+ f"### ⚡ Backend Connection Status\n"
102
+ f"Status: {status_color} **{status_text}**\n\n"
103
+ f"Switching between pipeline modes instantly updates your active viewport feed below."
104
  )
105
+ ],
106
+ ui_args={
107
+ "title": "⛏️ Minecraft Spatial Voxel Filter",
108
+ "pulse_color": "rgb(40, 167, 69)" if voxel_backend else "rgb(220, 53, 69)",
109
+ "icon_button_color": "rgb(40, 167, 69)" if voxel_backend else "rgb(220, 53, 69)",
110
+ },
111
+ time_limit=150,
112
+ concurrency_limit=4,
113
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  if __name__ == "__main__":
116
+ stream.ui.launch()