ANXLOG commited on
Commit
1d2cf54
·
verified ·
1 Parent(s): 5ddb5ac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +156 -113
app.py CHANGED
@@ -2,6 +2,7 @@ import gradio as gr
2
  import numpy as np
3
  import plotly.graph_objects as go
4
  import sympy
 
5
  from collections import Counter
6
 
7
  # --- HELPER: GPF CALCULATION ---
@@ -20,53 +21,75 @@ def get_gpf(n):
20
  gpf = n
21
  return gpf
22
 
23
- # --- MODULE 1: PRIME POTENTIALITY MATRIX (The Stream) ---
24
- def generate_potentiality_matrix(sequence_length):
25
- """Visualizes the Integer Stream aligned to Mod 10."""
26
- integers = np.arange(sequence_length)
27
- width = 10
28
- rows = int(np.ceil(sequence_length / width))
 
 
29
 
30
- padded_len = rows * width
31
- padded_ints = np.pad(integers, (0, padded_len - len(integers)), mode='constant')
 
32
 
33
- matrix_values = np.zeros(padded_len)
34
- hover_texts = []
35
-
36
- for i, val in enumerate(padded_ints):
37
- val = int(val)
38
- hover_texts.append(f"Integer: ±{val}<br>Mod 10: {val%10}")
 
 
 
 
 
 
 
39
 
40
- if sympy.isprime(val) and val > 5:
41
- matrix_values[i] = 1.0 # Kinetic Signal (Prime)
42
- elif val % 2 == 0 or val % 5 == 0:
43
- matrix_values[i] = 0.0 # Ground State (Composite)
44
  else:
45
- matrix_values[i] = 0.2 # Potential Energy (Odd Composite)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- grid = matrix_values.reshape(rows, width)
48
- text_grid = np.array(hover_texts).reshape(rows, width)
49
-
50
- fig = go.Figure(data=go.Heatmap(
51
- z=grid,
52
- x=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
53
- colorscale=[[0.0, "#0a0a0a"], [0.2, "#1a1a40"], [1.0, "#00ffea"]],
54
- showscale=False,
55
- text=text_grid,
56
- hoverinfo='text'
57
- ))
58
-
59
  fig.update_layout(
60
- title=f"Prime Potentiality Matrix (Integers ±N)",
61
- xaxis_title="Modulus 10 Index",
62
- yaxis_title="Integer Depth",
63
  template="plotly_dark",
64
- yaxis=dict(autorange="reversed"),
65
- height=800
66
  )
67
  return fig
68
 
69
- # --- MODULE 2: WEIGHTED CONNECTIVITY TOPOLOGY (The Network) ---
70
  def visualize_prime_network(max_integer, show_links):
71
  """Plots ALL integers. Connects Composites to their GPF Base."""
72
  fig = go.Figure()
@@ -75,7 +98,9 @@ def visualize_prime_network(max_integer, show_links):
75
  gpf_map = {}
76
  prime_children_count = Counter()
77
 
 
78
  for n in range(1, max_integer + 1):
 
79
  angle = np.pi/2 - (2 * np.pi * (n % 10)) / 10
80
  radius = n
81
  x = radius * np.cos(angle)
@@ -83,13 +108,12 @@ def visualize_prime_network(max_integer, show_links):
83
  positions[n] = (x, y)
84
 
85
  if n > 1:
86
- if sympy.isprime(n):
87
- pass
88
- else:
89
  gpf = get_gpf(n)
90
  gpf_map[n] = gpf
91
  prime_children_count[gpf] += 1
92
 
 
93
  if show_links:
94
  edge_x, edge_y = [], []
95
  for n, base in gpf_map.items():
@@ -102,11 +126,12 @@ def visualize_prime_network(max_integer, show_links):
102
  fig.add_trace(go.Scatter(
103
  x=edge_x, y=edge_y,
104
  mode='lines',
105
- line=dict(color='rgba(100, 100, 100, 0.15)', width=1),
106
  hoverinfo='none',
107
- name='GPF Links'
108
  ))
109
 
 
110
  prime_x, prime_y, prime_size, prime_text = [], [], [], []
111
  comp_x, comp_y, comp_text = [], [], []
112
 
@@ -116,44 +141,48 @@ def visualize_prime_network(max_integer, show_links):
116
  prime_x.append(x)
117
  prime_y.append(y)
118
  weight = prime_children_count[n]
119
- size = 6 + (np.log(weight + 1) * 5)
 
120
  prime_size.append(size)
121
- prime_text.append(f"<b>PRIME: {n}</b><br>Constituents: {weight}")
122
  else:
123
  comp_x.append(x)
124
  comp_y.append(y)
125
- comp_text.append(f"Composite: {n}<br>GPF Base: {gpf_map.get(n)}")
126
 
 
127
  fig.add_trace(go.Scatter(
128
  x=comp_x, y=comp_y,
129
  mode='markers',
130
- marker=dict(size=4, color='#ff0055', opacity=0.5),
131
  text=comp_text,
132
  hoverinfo='text',
133
  name='Composites'
134
  ))
135
 
 
136
  fig.add_trace(go.Scatter(
137
  x=prime_x, y=prime_y,
138
  mode='markers',
139
  marker=dict(size=prime_size, color='#00ffea', line=dict(width=1, color='white')),
140
  text=prime_text,
141
  hoverinfo='text',
142
- name='Primes (Weighted)'
143
  ))
144
 
 
145
  for i in range(10):
146
  angle = np.pi/2 - (2 * np.pi * i) / 10
147
  fig.add_trace(go.Scatter(
148
- x=[0, max_integer * 1.05 * np.cos(angle)],
149
- y=[0, max_integer * 1.05 * np.sin(angle)],
150
  mode='lines',
151
  line=dict(color='#222', width=1, dash='dot'),
152
  showlegend=False
153
  ))
154
 
155
  fig.update_layout(
156
- title=f"Radial Connectivity Network (Weighted by Composite Generation)",
157
  template="plotly_dark",
158
  xaxis=dict(showgrid=False, zeroline=False, visible=False),
159
  yaxis=dict(showgrid=False, zeroline=False, visible=False),
@@ -182,120 +211,134 @@ def visualize_gpf_counts(sequence_length):
182
  ))
183
 
184
  fig.update_layout(
185
- title="Composite Density by Greatest Prime Factor (GPF)",
186
- xaxis_title="Greatest Prime Factor (P)",
187
- yaxis_title="Count of Constituents",
188
  template="plotly_dark",
189
  xaxis=dict(type='category')
190
  )
191
  return fig
192
 
193
- # --- MODULE 4: VARIABLE SIZE STREAM CHUNKING (Quadtree Logic) ---
194
- def recursive_quadtree(grid, x, y, w, h, tolerance, chunks):
195
- """
196
- Simulates the LOGOS Baker.
197
- Recursively splits the grid based on Variance (Heat).
198
- """
199
- # 1. Extract the region
200
- region = grid[y:y+h, x:x+w]
201
- # 2. Calculate Heat (Standard Deviation of the signal)
202
  heat = np.std(region)
203
 
204
- # 3. Decision: Split or Persist?
205
- # If heat > tolerance and we aren't at atomic limit (4px), SPLIT
206
- if heat > tolerance and w > 4:
 
 
207
  hw, hh = w // 2, h // 2
208
- # Recursive Descent (Quadtree)
209
- recursive_quadtree(grid, x, y, hw, hh, tolerance, chunks) # TL
210
- recursive_quadtree(grid, x+hw, y, w-hw, hh, tolerance, chunks) # TR
211
- recursive_quadtree(grid, x, y+hh, hw, h-hh, tolerance, chunks) # BL
212
- recursive_quadtree(grid, x+hw, y+hh, w-hw, h-hh, tolerance, chunks) # BR
213
  else:
214
- # PERSIST (00 State): Save as a single chunk
215
  chunks.append((x, y, w, h, heat))
216
 
217
- def visualize_chunking(tolerance, complexity):
218
- """Generates a synthetic 'Heat Map' and applies Variable Chunking."""
219
- # 1. Generate Synthetic Data (The "Stream")
220
- size = 512
221
- x = np.linspace(0, 10, size)
222
- y = np.linspace(0, 10, size)
223
- X, Y = np.meshgrid(x, y)
224
 
225
- # Base: Smooth gradient (Persistence Zone)
226
- base = np.sin(X) + np.cos(Y)
227
- # Noise: High frequency spikes (Heat Zone) concentrated in center
228
- noise = np.random.rand(size, size) * complexity * np.exp(-((X-5)**2 + (Y-5)**2)/2)
229
- stream_data = base + noise
 
 
 
230
 
231
- # 2. Perform LOGOS Decomposition
232
  chunks = []
233
- recursive_quadtree(stream_data, 0, 0, size, size, tolerance, chunks)
234
 
235
- # 3. Visualization
236
  fig = go.Figure()
237
 
238
- # Background Heatmap
239
- fig.add_trace(go.Heatmap(z=stream_data, colorscale='Viridis', showscale=False, hoverinfo='skip'))
 
 
240
 
241
- # Draw Quadtree Boxes (The Atoms)
242
  shapes = []
243
  for (cx, cy, cw, ch, heat) in chunks:
244
- # Color logic: Small boxes = High Heat (Red), Large boxes = Persistence (Cyan)
245
- color = '#ff0055' if cw < 16 else '#00ffea'
246
- width = 1 if cw < 16 else 2
247
- opacity = 0.6 if cw < 16 else 0.8
 
 
 
 
 
248
 
249
  shapes.append(dict(
250
- type="rect", x0=cx, y0=cy, x1=cx+cw, y1=cy+ch,
 
 
251
  line=dict(color=color, width=width),
252
- fillcolor=color, opacity=0.1
253
  ))
254
 
255
  fig.update_layout(
256
- title=f"Adaptive Stream Chunking (Atoms: {len(chunks)})",
257
  shapes=shapes,
258
  template="plotly_dark",
259
  height=800,
260
  width=800,
261
- xaxis=dict(showgrid=False, visible=False),
262
- yaxis=dict(showgrid=False, visible=False, autorange="reversed")
263
  )
264
  return fig
265
 
266
  # --- THE INTERFACE ---
267
  def build_demo():
268
  with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
269
- gr.Markdown("# LOGOS: Prime-Indexed Topology & Thermal Compression")
270
 
271
- with gr.Tab("1. Potentiality Matrix"):
272
- seq_len = gr.Slider(100, 5000, value=1000, label="Stream Depth")
273
- matrix_plot = gr.Plot(label="Mod 10 Matrix")
274
- btn_matrix = gr.Button("Generate Matrix")
275
- btn_matrix.click(generate_potentiality_matrix, inputs=[seq_len], outputs=matrix_plot)
 
276
 
277
- with gr.Tab("2. Radial Topology"):
278
- gr.Markdown("Primes sized by gravity. Lines connect Composites to GPF Base.")
279
- rad_len = gr.Slider(100, 1000, value=300, label="Integer Range")
280
  link_toggle = gr.Checkbox(value=True, label="Show Connectivity")
281
  matroska_plot = gr.Plot(label="Radial View")
282
  btn_net = gr.Button("Build Network")
283
  btn_net.click(visualize_prime_network, inputs=[rad_len, link_toggle], outputs=matroska_plot)
284
 
285
  with gr.Tab("3. GPF Density"):
 
286
  gpf_len = gr.Slider(100, 10000, value=2500, label="Stream Depth")
287
  gpf_plot = gr.Plot(label="GPF Distribution")
288
  btn_gpf = gr.Button("Calculate Density")
289
  btn_gpf.click(visualize_gpf_counts, inputs=[gpf_len], outputs=gpf_plot)
290
 
291
- with gr.Tab("4. Variable Stream Chunking"):
292
- gr.Markdown("Visualizing **Thermal-Aware Compression**. High heat (Variance) = Small Atoms. Low heat = Large Atoms.")
293
  with gr.Row():
294
- tol_slider = gr.Slider(0.01, 1.0, value=0.1, label="Heat Tolerance (Persistence Threshold)")
295
- comp_slider = gr.Slider(1.0, 10.0, value=5.0, label="Signal Complexity (Noise)")
 
296
  chunk_plot = gr.Plot(label="Adaptive Decomposition")
297
- btn_chunk = gr.Button("Dissolve Stream")
298
- btn_chunk.click(visualize_chunking, inputs=[tol_slider, comp_slider], outputs=chunk_plot)
299
 
300
  return demo
301
 
 
2
  import numpy as np
3
  import plotly.graph_objects as go
4
  import sympy
5
+ import cv2
6
  from collections import Counter
7
 
8
  # --- HELPER: GPF CALCULATION ---
 
21
  gpf = n
22
  return gpf
23
 
24
+ # --- MODULE 1: PRIME POTENTIALITY FLOW (The "Arrows" Matrix) ---
25
+ def generate_potentiality_flow(depth):
26
+ """
27
+ Visualizes the Prime Potentiality as a Directed Flow (Sankey/Tree).
28
+ Shows how last digits (1,3,7,9) propagate potentiality to the next magnitude.
29
+ """
30
+ # Nodes: Layers of magnitude (10s, 100s, etc.)
31
+ # Links: Valid transitions where P_n could exist
32
 
33
+ # Simplified visual logic: Mod 10 transitions
34
+ # Source: The digit (1, 3, 7, 9)
35
+ # Target: The next prime candidate
36
 
37
+ sources = []
38
+ targets = []
39
+ values = []
40
+ colors = []
41
+ labels = ["Start"] + [f"Mod {i}" for i in range(10)] + ["Potential Prime"]
42
+
43
+ # Logic: Only 1, 3, 7, 9 allow entry into the "Prime" state
44
+ prime_endings = [1, 3, 7, 9]
45
+
46
+ # Layer 1: Start -> Mod 10 Buckets
47
+ for i in range(10):
48
+ sources.append(0) # Start Node
49
+ targets.append(i + 1) # Mod Nodes
50
 
51
+ if i in prime_endings:
52
+ values.append(5) # High flow
53
+ colors.append("#00ffea") # Cyan (Open Path)
 
54
  else:
55
+ values.append(1) # Blocked/Low flow
56
+ colors.append("#333333") # Grey (Blocked)
57
+
58
+ # Layer 2: Mod Buckets -> Prime Potential
59
+ final_node = 11
60
+ for i in range(10):
61
+ if i in prime_endings:
62
+ sources.append(i + 1)
63
+ targets.append(final_node)
64
+ values.append(5)
65
+ colors.append("#00ffea") # Flow continues
66
+ else:
67
+ # Dead ends (no link to Prime Potential)
68
+ pass
69
+
70
+ fig = go.Figure(data=[go.Sankey(
71
+ node = dict(
72
+ pad = 15,
73
+ thickness = 20,
74
+ line = dict(color = "black", width = 0.5),
75
+ label = labels,
76
+ color = ["white"] + ["#00ffea" if i in prime_endings else "#ff0055" for i in range(10)] + ["#00ffea"]
77
+ ),
78
+ link = dict(
79
+ source = sources,
80
+ target = targets,
81
+ value = values,
82
+ color = colors
83
+ ))])
84
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  fig.update_layout(
86
+ title="Prime Potentiality Flow (Digit Constraints)",
 
 
87
  template="plotly_dark",
88
+ height=600
 
89
  )
90
  return fig
91
 
92
+ # --- MODULE 2: WEIGHTED CONNECTIVITY TOPOLOGY (The Web) ---
93
  def visualize_prime_network(max_integer, show_links):
94
  """Plots ALL integers. Connects Composites to their GPF Base."""
95
  fig = go.Figure()
 
98
  gpf_map = {}
99
  prime_children_count = Counter()
100
 
101
+ # Pre-calculate positions to ensure density
102
  for n in range(1, max_integer + 1):
103
+ # Orientation: 0 at TOP (pi/2), Clockwise
104
  angle = np.pi/2 - (2 * np.pi * (n % 10)) / 10
105
  radius = n
106
  x = radius * np.cos(angle)
 
108
  positions[n] = (x, y)
109
 
110
  if n > 1:
111
+ if not sympy.isprime(n):
 
 
112
  gpf = get_gpf(n)
113
  gpf_map[n] = gpf
114
  prime_children_count[gpf] += 1
115
 
116
+ # DRAW CONNECTIVITY (The Tessellation)
117
  if show_links:
118
  edge_x, edge_y = [], []
119
  for n, base in gpf_map.items():
 
126
  fig.add_trace(go.Scatter(
127
  x=edge_x, y=edge_y,
128
  mode='lines',
129
+ line=dict(color='rgba(100, 100, 100, 0.2)', width=0.5),
130
  hoverinfo='none',
131
+ name='GPF Gravity'
132
  ))
133
 
134
+ # DRAW NODES
135
  prime_x, prime_y, prime_size, prime_text = [], [], [], []
136
  comp_x, comp_y, comp_text = [], [], []
137
 
 
141
  prime_x.append(x)
142
  prime_y.append(y)
143
  weight = prime_children_count[n]
144
+ # Logarithmic size scaling
145
+ size = 5 + (np.log(weight + 1) * 6)
146
  prime_size.append(size)
147
+ prime_text.append(f"<b>PRIME: {n}</b><br>Gravity: {weight}")
148
  else:
149
  comp_x.append(x)
150
  comp_y.append(y)
151
+ comp_text.append(f"Composite: {n}<br>Base: {gpf_map.get(n)}")
152
 
153
+ # Composites (Red/Pink Dust)
154
  fig.add_trace(go.Scatter(
155
  x=comp_x, y=comp_y,
156
  mode='markers',
157
+ marker=dict(size=3, color='#ff0055', opacity=0.6),
158
  text=comp_text,
159
  hoverinfo='text',
160
  name='Composites'
161
  ))
162
 
163
+ # Primes (Cyan Anchors)
164
  fig.add_trace(go.Scatter(
165
  x=prime_x, y=prime_y,
166
  mode='markers',
167
  marker=dict(size=prime_size, color='#00ffea', line=dict(width=1, color='white')),
168
  text=prime_text,
169
  hoverinfo='text',
170
+ name='Prime Anchors'
171
  ))
172
 
173
+ # Radial Spokes Background
174
  for i in range(10):
175
  angle = np.pi/2 - (2 * np.pi * i) / 10
176
  fig.add_trace(go.Scatter(
177
+ x=[0, max_integer * 1.1 * np.cos(angle)],
178
+ y=[0, max_integer * 1.1 * np.sin(angle)],
179
  mode='lines',
180
  line=dict(color='#222', width=1, dash='dot'),
181
  showlegend=False
182
  ))
183
 
184
  fig.update_layout(
185
+ title=f"Radial Prime Connectivity (Max: {max_integer})",
186
  template="plotly_dark",
187
  xaxis=dict(showgrid=False, zeroline=False, visible=False),
188
  yaxis=dict(showgrid=False, zeroline=False, visible=False),
 
211
  ))
212
 
213
  fig.update_layout(
214
+ title="Composite Density by GPF Base",
215
+ xaxis_title="Prime Base (P)",
216
+ yaxis_title="Composites Anchored",
217
  template="plotly_dark",
218
  xaxis=dict(type='category')
219
  )
220
  return fig
221
 
222
+ # --- MODULE 4: REAL IMAGE CHUNKING (USER INPUT) ---
223
+ def recursive_quadtree_image(img_gray, x, y, w, h, tolerance, chunks):
224
+ """Recursive decomposition on real image data."""
225
+ # 1. Get Region
226
+ region = img_gray[y:y+h, x:x+w]
227
+ if region.size == 0: return
228
+
229
+ # 2. Measure Heat (Standard Deviation)
 
230
  heat = np.std(region)
231
 
232
+ # 3. Decision
233
+ # Scale tolerance to 0-255 range approximately
234
+ tol_val = tolerance * 100
235
+
236
+ if heat > tol_val and w > 4: # Min size 4px
237
  hw, hh = w // 2, h // 2
238
+ recursive_quadtree_image(img_gray, x, y, hw, hh, tolerance, chunks)
239
+ recursive_quadtree_image(img_gray, x+hw, y, w-hw, hh, tolerance, chunks)
240
+ recursive_quadtree_image(img_gray, x, y+hh, hw, h-hh, tolerance, chunks)
241
+ recursive_quadtree_image(img_gray, x+hw, y+hh, w-hw, h-hh, tolerance, chunks)
 
242
  else:
243
+ # Persist Atom
244
  chunks.append((x, y, w, h, heat))
245
 
246
+ def process_uploaded_image(image, tolerance):
247
+ """
248
+ Takes user uploaded image -> Grayscale -> Quadtree -> Visualization.
249
+ """
250
+ if image is None:
251
+ return None
 
252
 
253
+ # 1. Preprocess
254
+ # Convert to grayscale for heat analysis (variance is scalar)
255
+ if len(image.shape) == 3:
256
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
257
+ else:
258
+ gray = image
259
+
260
+ h, w = gray.shape
261
 
262
+ # 2. Run LOGOS Baker
263
  chunks = []
264
+ recursive_quadtree_image(gray, 0, 0, w, h, tolerance, chunks)
265
 
266
+ # 3. Visualize
267
  fig = go.Figure()
268
 
269
+ # Background: The original image (dimmed)
270
+ # Plotly Image needs to be base64 or array.
271
+ # For speed in heatmap, we invert the y-axis logic.
272
+ fig.add_trace(go.Heatmap(z=np.flipud(gray), colorscale='Gray', showscale=False, opacity=0.3))
273
 
274
+ # Draw The Atoms (Rectangles)
275
  shapes = []
276
  for (cx, cy, cw, ch, heat) in chunks:
277
+ # High Heat (Small) = Red/Orange
278
+ # Low Heat (Large) = Cyan/Blue
279
+ is_hot = cw < 16
280
+ color = '#ff0055' if is_hot else '#00ffea'
281
+ width = 1
282
+
283
+ # Plotly shapes use bottom-left origin for some things, but rects are cartesian
284
+ # We need to map image coords (y down) to plot coords (y up)
285
+ # Or just tell layout to reverse y.
286
 
287
  shapes.append(dict(
288
+ type="rect",
289
+ x0=cx, y0=cy,
290
+ x1=cx+cw, y1=cy+ch,
291
  line=dict(color=color, width=width),
292
+ fillcolor=color, opacity=0.1 if is_hot else 0.05
293
  ))
294
 
295
  fig.update_layout(
296
+ title=f"LOGOS Adaptive Compression (Atoms: {len(chunks)})",
297
  shapes=shapes,
298
  template="plotly_dark",
299
  height=800,
300
  width=800,
301
+ xaxis=dict(showgrid=False, visible=False, range=[0, w]),
302
+ yaxis=dict(showgrid=False, visible=False, range=[h, 0], autorange="reversed") # Image coords
303
  )
304
  return fig
305
 
306
  # --- THE INTERFACE ---
307
  def build_demo():
308
  with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
309
+ gr.Markdown("# LOGOS: Prime-Indexed Topology & Compression Validator")
310
 
311
+ with gr.Tab("1. Prime Potentiality Flow"):
312
+ gr.Markdown("Visualizing the **Digit Constraints**: Only 1, 3, 7, 9 allow Prime formation.")
313
+ depth_slider = gr.Slider(10, 100, value=10, label="Visual Depth") # Just for trigger
314
+ flow_plot = gr.Plot(label="Potentiality Flow")
315
+ btn_flow = gr.Button("Generate Flow")
316
+ btn_flow.click(generate_potentiality_flow, inputs=[depth_slider], outputs=flow_plot)
317
 
318
+ with gr.Tab("2. Radial Topology (The Web)"):
319
+ gr.Markdown("**The Natural Tessellation:** Composites connected to their Prime Base.")
320
+ rad_len = gr.Slider(100, 2000, value=500, label="Integer Range")
321
  link_toggle = gr.Checkbox(value=True, label="Show Connectivity")
322
  matroska_plot = gr.Plot(label="Radial View")
323
  btn_net = gr.Button("Build Network")
324
  btn_net.click(visualize_prime_network, inputs=[rad_len, link_toggle], outputs=matroska_plot)
325
 
326
  with gr.Tab("3. GPF Density"):
327
+ gr.Markdown("Counts of composites anchored by each Prime.")
328
  gpf_len = gr.Slider(100, 10000, value=2500, label="Stream Depth")
329
  gpf_plot = gr.Plot(label="GPF Distribution")
330
  btn_gpf = gr.Button("Calculate Density")
331
  btn_gpf.click(visualize_gpf_counts, inputs=[gpf_len], outputs=gpf_plot)
332
 
333
+ with gr.Tab("4. Live Stream Baker"):
334
+ gr.Markdown("Upload an image to test **Thermal-Aware Chunking**. Drag 'Heat Tolerance' to adjust compression.")
335
  with gr.Row():
336
+ inp_img = gr.Image(label="Input Stream (Image)", type="numpy")
337
+ tol_slider = gr.Slider(0.01, 1.0, value=0.15, label="Heat Tolerance (Persistence)")
338
+
339
  chunk_plot = gr.Plot(label="Adaptive Decomposition")
340
+ btn_bake = gr.Button("Bake Stream")
341
+ btn_bake.click(process_uploaded_image, inputs=[inp_img, tol_slider], outputs=chunk_plot)
342
 
343
  return demo
344