danielritchie commited on
Commit
78631f6
·
verified ·
1 Parent(s): a788606

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -107
app.py CHANGED
@@ -1,15 +1,13 @@
1
  # app.py — Affection 👁️ (Hugging Face Space)
2
 
3
  import os
 
 
4
 
5
- # Disable Spaces reload BEFORE importing gradio
6
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
7
  os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
8
  os.environ["SPACES_DISABLE_RELOAD"] = "1"
9
 
10
- import gradio as gr
11
- import matplotlib.pyplot as plt
12
-
13
  from utils.presets import EMOTION_PRESETS
14
  from utils.drama import apply_drama
15
  from utils.color_model import infer_color, render_color
@@ -38,22 +36,17 @@ def apply_passion(raw: dict, passion: float) -> dict:
38
  # ------------------------------------------------------------
39
  # Valence–Arousal Visualization (2D Projection)
40
  # ------------------------------------------------------------
41
- def generate_scatter(raw, amplified, cinematic, target, label, passion, drama):
42
 
43
  fig, ax = plt.subplots(figsize=(6, 6))
44
- base_color = "#2C3E50"
45
 
46
- # ----------------------------------
47
- # Background Cinematic Anchors
48
- # ----------------------------------
49
  for _, preset in EMOTION_PRESETS.items():
50
  t = preset["target"]
51
  ax.scatter(t["V"], t["A"], alpha=0.1, s=90, color="#BBBBBB")
52
 
53
- # ----------------------------------
54
- # Active Emotional Trajectory
55
- # ----------------------------------
56
-
57
  # Natural
58
  ax.scatter(
59
  raw["V"], raw["A"],
@@ -84,75 +77,13 @@ def generate_scatter(raw, amplified, cinematic, target, label, passion, drama):
84
  label="After Drama (Cinematic Alignment)"
85
  )
86
 
87
- # Cinematic Anchor (Target)
88
- ax.scatter(
89
- target["V"],
90
- target["A"],
91
- s=160,
92
- marker="X",
93
- color="#E74C3C",
94
- edgecolor="black",
95
- linewidth=1.2,
96
- label="Cinematic Anchor"
97
- )
98
-
99
- ax.text(
100
- target["V"],
101
- target["A"],
102
- f" {label} Target",
103
- verticalalignment="center",
104
- fontsize=9,
105
- weight="bold",
106
- color="#E74C3C"
107
- )
108
-
109
- # ----------------------------------
110
- # Dynamic Zoom (20% padded, centered)
111
- # ----------------------------------
112
- xs = [
113
- raw["V"],
114
- amplified["V"],
115
- cinematic["V"],
116
- target["V"]
117
- ]
118
-
119
- ys = [
120
- raw["A"],
121
- amplified["A"],
122
- cinematic["A"],
123
- target["A"]
124
- ]
125
-
126
- min_x, max_x = min(xs), max(xs)
127
- min_y, max_y = min(ys), max(ys)
128
-
129
- span_x = max_x - min_x
130
- span_y = max_y - min_y
131
-
132
- span = max(span_x, span_y)
133
- span = max(span, 0.05) # prevent collapse
134
-
135
- padding = span * 0.20
136
- center_x = (min_x + max_x) / 2
137
- center_y = (min_y + max_y) / 2
138
- half_range = (span / 2) + padding
139
-
140
- ax.set_xlim(center_x - half_range, center_x + half_range)
141
- ax.set_ylim(center_y - half_range, center_y + half_range)
142
- ax.set_aspect("equal", adjustable="box")
143
-
144
- # ----------------------------------
145
- # Proportional Arrow Heads
146
- # ----------------------------------
147
- arrow_head = span * 0.04
148
-
149
- # Raw → Amplified
150
  ax.arrow(
151
  raw["V"],
152
  raw["A"],
153
  amplified["V"] - raw["V"],
154
  amplified["A"] - raw["A"],
155
- head_width=arrow_head,
156
  length_includes_head=True,
157
  color=base_color,
158
  linestyle="--",
@@ -160,13 +91,13 @@ def generate_scatter(raw, amplified, cinematic, target, label, passion, drama):
160
  alpha=0.6
161
  )
162
 
163
- # Amplified → Cinematic
164
  ax.arrow(
165
  amplified["V"],
166
  amplified["A"],
167
  cinematic["V"] - amplified["V"],
168
  cinematic["A"] - amplified["A"],
169
- head_width=arrow_head,
170
  length_includes_head=True,
171
  color=base_color,
172
  linestyle="-",
@@ -174,6 +105,36 @@ def generate_scatter(raw, amplified, cinematic, target, label, passion, drama):
174
  alpha=0.9
175
  )
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  ax.set_xlabel("Valence")
178
  ax.set_ylabel("Arousal")
179
  ax.set_title(f"{label}\nPassion={round(passion,2)} | Drama={round(drama,2)}")
@@ -202,15 +163,7 @@ def run_pipeline(preset_name, passion, drama):
202
  color_params = infer_color(cinematic)
203
  color_block = render_color(color_params)
204
 
205
- fig = generate_scatter(
206
- natural,
207
- amplified,
208
- cinematic,
209
- target,
210
- preset_name,
211
- passion,
212
- drama
213
- )
214
 
215
  return (
216
  text,
@@ -253,17 +206,23 @@ with gr.Blocks(title="Affection 👁️ — Edge Emotional Intelligence") as dem
253
 
254
  gr.Markdown(
255
  """
256
- This demo isolates a single loop transformation for clarity.
257
 
258
- Hardware deployment performs:
259
- • Transcript ingestion
260
- • VAD extraction
261
- • Structural metrics
262
- • Radial amplification (Passion)
263
- • Cinematic alignment (Drama)
264
- • Continuous emotional state streaming
265
 
266
- Edge loop capable of ~200Hz execution.
 
 
 
 
 
 
 
 
 
 
 
 
267
  """
268
  )
269
 
@@ -294,8 +253,8 @@ Edge loop capable of ~200Hz execution.
294
  gr.Markdown(
295
  """
296
  **Note:**
297
- This 2D VA projection is for visualization only.
298
- The actual transformation operates in 5D VAD+CC space.
299
  """
300
  )
301
 
@@ -308,10 +267,15 @@ The actual transformation operates in 5D VAD+CC space.
308
 
309
  gr.Markdown(
310
  """
311
- VAD+CC Embedded Model Color Rendering
 
 
 
312
 
313
- Model:
314
  https://huggingface.co/danielritchie/vibe-color-model
 
 
315
  """
316
  )
317
 
@@ -334,9 +298,4 @@ https://huggingface.co/danielritchie/vibe-color-model
334
 
335
  demo.load(fn=run_pipeline, inputs=[preset_selector, passion, drama], outputs=outputs)
336
 
337
-
338
- demo.launch(
339
- server_name="0.0.0.0",
340
- server_port=7860,
341
- ssr_mode=False
342
- )
 
1
  # app.py — Affection 👁️ (Hugging Face Space)
2
 
3
  import os
4
+ import gradio as gr
5
+ import matplotlib.pyplot as plt
6
 
 
7
  os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
8
  os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
9
  os.environ["SPACES_DISABLE_RELOAD"] = "1"
10
 
 
 
 
11
  from utils.presets import EMOTION_PRESETS
12
  from utils.drama import apply_drama
13
  from utils.color_model import infer_color, render_color
 
36
  # ------------------------------------------------------------
37
  # Valence–Arousal Visualization (2D Projection)
38
  # ------------------------------------------------------------
39
+ def generate_scatter(raw, amplified, cinematic, label, passion, drama):
40
 
41
  fig, ax = plt.subplots(figsize=(6, 6))
 
42
 
43
+ base_color = "#2C3E50" # neutral deep tone
44
+
45
+ # Plot cinematic anchors faintly
46
  for _, preset in EMOTION_PRESETS.items():
47
  t = preset["target"]
48
  ax.scatter(t["V"], t["A"], alpha=0.1, s=90, color="#BBBBBB")
49
 
 
 
 
 
50
  # Natural
51
  ax.scatter(
52
  raw["V"], raw["A"],
 
77
  label="After Drama (Cinematic Alignment)"
78
  )
79
 
80
+ # Arrow 1 — Raw → Amplified
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  ax.arrow(
82
  raw["V"],
83
  raw["A"],
84
  amplified["V"] - raw["V"],
85
  amplified["A"] - raw["A"],
86
+ head_width=0.02,
87
  length_includes_head=True,
88
  color=base_color,
89
  linestyle="--",
 
91
  alpha=0.6
92
  )
93
 
94
+ # Arrow 2 — Amplified → Cinematic
95
  ax.arrow(
96
  amplified["V"],
97
  amplified["A"],
98
  cinematic["V"] - amplified["V"],
99
  cinematic["A"] - amplified["A"],
100
+ head_width=0.02,
101
  length_includes_head=True,
102
  color=base_color,
103
  linestyle="-",
 
105
  alpha=0.9
106
  )
107
 
108
+ # ----------------------------------
109
+ # Dynamic Zoom (Centered + 20% Padding)
110
+ # ----------------------------------
111
+ xs = [raw["V"], amplified["V"], cinematic["V"]]
112
+ ys = [raw["A"], amplified["A"], cinematic["A"]]
113
+
114
+ min_x, max_x = min(xs), max(xs)
115
+ min_y, max_y = min(ys), max(ys)
116
+
117
+ span_x = max_x - min_x
118
+ span_y = max_y - min_y
119
+
120
+ # Use the larger span to keep square framing
121
+ span = max(span_x, span_y)
122
+
123
+ # Avoid zero-span collapse
124
+ span = max(span, 0.05)
125
+
126
+ padding = span * 0.20 # 20% larger
127
+
128
+ center_x = (min_x + max_x) / 2
129
+ center_y = (min_y + max_y) / 2
130
+
131
+ half_range = (span / 2) + padding
132
+
133
+ ax.set_xlim(center_x - half_range, center_x + half_range)
134
+ ax.set_ylim(center_y - half_range, center_y + half_range)
135
+
136
+ ax.set_aspect('equal', adjustable='box')
137
+
138
  ax.set_xlabel("Valence")
139
  ax.set_ylabel("Arousal")
140
  ax.set_title(f"{label}\nPassion={round(passion,2)} | Drama={round(drama,2)}")
 
163
  color_params = infer_color(cinematic)
164
  color_block = render_color(color_params)
165
 
166
+ fig = generate_scatter(natural, amplified, cinematic, preset_name, passion, drama)
 
 
 
 
 
 
 
 
167
 
168
  return (
169
  text,
 
206
 
207
  gr.Markdown(
208
  """
209
+ This section provides a simplified visualization of a more complex on-device architecture.
210
 
211
+ In hardware deployment, the NVIDIA Jetson Orin Nano performs all of the following:
 
 
 
 
 
 
212
 
213
+ Robot hardware daemon service
214
+ • Interactive conversational application
215
+ • Real-time transcript ingestion
216
+ • VAD extraction (NRC-VAD lexicon)
217
+ • Structural language metrics (Complexity + Coherence)
218
+ • Radial emotional amplification (Passion)
219
+ • Cinematic nearest-exemplar alignment (Drama)
220
+ • Dual-timescale blending (fast burst + slow baseline via Nemotron/Ollama)
221
+ • Continuous emotional state streaming for display on an expression module
222
+
223
+ This demo isolates a single loop transformation for clarity.
224
+
225
+ Our NVIDIA edge device is capable of running this loop 200x per second.
226
  """
227
  )
228
 
 
253
  gr.Markdown(
254
  """
255
  **Note:**
256
+ This plot shows a 2D Valence–Arousal projection for visualization only, but results are from the actual model.
257
+ Actual transformation and color inference are more complex and operate on the full 5D VAD+CC vector.
258
  """
259
  )
260
 
 
267
 
268
  gr.Markdown(
269
  """
270
+ The finalized VAD+CC vector is transmitted to an expressive display module. In this example, we are converting to colors to be used for eyes.
271
+
272
+ The module does not compute emotion.
273
+ It receives the 5D emotional state and runs a trained neural model to convert it into expressive color.
274
 
275
+ Model used here (same as deployment):
276
  https://huggingface.co/danielritchie/vibe-color-model
277
+
278
+ VAD+CC (Affect Engine) → Embedded Model → Color Rendering (Expression)
279
  """
280
  )
281
 
 
298
 
299
  demo.load(fn=run_pipeline, inputs=[preset_selector, passion, drama], outputs=outputs)
300
 
301
+ demo.launch(server_name="0.0.0.0", server_port=7860)