Team Ai
Apppublic

danielritchie/generative-affect-engine

sourceHugging Facecc-by-4.0updated 8mo agoView on Hugging Face
0likes
app.py265 linesDownload Raw Back to root
1# app.py — Affection 👁️ (Hugging Face Space)2 3import os4import gradio as gr5import matplotlib.pyplot as plt6 7os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"8os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"9os.environ["SPACES_DISABLE_RELOAD"] = "1"10 11from utils.presets import EMOTION_PRESETS12from utils.drama import apply_drama13from utils.color_model import infer_color, render_color14 15 16# ------------------------------------------------------------17# Passion (Radial Amplification)18# ------------------------------------------------------------19def apply_passion(raw: dict, passion: float) -> dict:20    passion = max(0.0, min(3.5, float(passion)))21    out = {}22 23    for k, v in raw.items():24        v = float(v)25        if k in ("V", "A", "D"):26            delta = v - 0.527            magnitude = abs(delta)28            gain = 1.0 + passion * magnitude29            out[k] = max(0.0, min(1.0, 0.5 + delta * gain))30        else:31            out[k] = max(0.0, min(1.0, v))32 33    return out34 35 36# ------------------------------------------------------------37# Valence–Arousal Visualization (2D Projection)38# ------------------------------------------------------------39def generate_scatter(raw, amplified, cinematic, target, target_name, passion, drama):40 41    fig, ax = plt.subplots(figsize=(6, 7))  # slightly taller42    plt.subplots_adjust(right=0.75)  # leave room for legend43 44    # ----------------------------------45    # Background Anchors46    # ----------------------------------47    for name, preset in EMOTION_PRESETS.items():48        t = preset["target"]49        ax.scatter(t["V"], t["A"], alpha=0.06, s=90, color="#DDDDDD")50 51    # ----------------------------------52    # Trajectory Points (Styled)53    # ----------------------------------54 55    # 1️⃣ Natural — light grey thin border56    ax.scatter(57        raw["V"], raw["A"],58        s=180,59        facecolor="#F0F0F0",60        edgecolor="#CCCCCC",61        linewidth=1,62        label="Natural"63    )64 65    # 2️⃣ After Passion — medium grey66    ax.scatter(67        amplified["V"], amplified["A"],68        s=180,69        facecolor="#9E9E9E",70        edgecolor="#666666",71        linewidth=1.5,72        label="After Passion"73    )74 75    # 3️⃣ After Drama — dark grey thin border76    ax.scatter(77        cinematic["V"], cinematic["A"],78        s=220,79        facecolor="#2F2F2F",80        edgecolor="black",81        linewidth=1,82        label="After Drama"83    )84 85    # Cinematic Anchor86    ax.scatter(87        target["V"],88        target["A"],89        s=180,90        marker="X",91        color="#E74C3C",92        edgecolor="black",93        linewidth=1.2,94        label=f"Anchor ({target_name})"95    )96 97    # ----------------------------------98    # Dynamic Zoom (20% padded)99    # ----------------------------------100    xs = [raw["V"], amplified["V"], cinematic["V"], target["V"]]101    ys = [raw["A"], amplified["A"], cinematic["A"], target["A"]]102 103    min_x, max_x = min(xs), max(xs)104    min_y, max_y = min(ys), max(ys)105 106    span_x = max_x - min_x107    span_y = max_y - min_y108    span = max(span_x, span_y)109    span = max(span, 0.05)110 111    padding = span * 0.20112    center_x = (min_x + max_x) / 2113    center_y = (min_y + max_y) / 2114 115    # shift center slightly upward116    center_y += span * 0.10117 118    half_range = (span / 2) + padding119 120    ax.set_xlim(center_x - half_range, center_x + half_range)121    ax.set_ylim(center_y - half_range, center_y + half_range)122 123    ax.set_aspect('equal', adjustable='box')124 125    # ----------------------------------126    # Proportional Arrows127    # ----------------------------------128    arrow_head = span * 0.035129 130    ax.arrow(131        raw["V"], raw["A"],132        amplified["V"] - raw["V"],133        amplified["A"] - raw["A"],134        head_width=arrow_head,135        length_includes_head=True,136        color="#888888",137        linestyle="--",138        linewidth=1.8,139        alpha=0.7140    )141 142    ax.arrow(143        amplified["V"], amplified["A"],144        cinematic["V"] - amplified["V"],145        cinematic["A"] - amplified["A"],146        head_width=arrow_head,147        length_includes_head=True,148        color="#444444",149        linestyle="-",150        linewidth=2,151        alpha=0.9152    )153 154    # ----------------------------------155    # Labels & Legend156    # ----------------------------------157    ax.set_xlabel("Valence")158    ax.set_ylabel("Arousal")159    ax.set_title(f"{target_name}\nPassion={round(passion,2)} | Drama={round(drama,2)}")160 161    ax.grid(alpha=0.12)162 163    # Move legend outside plot164    ax.legend(loc="center left", bbox_to_anchor=(1.02, 0.5), frameon=False)165 166    plt.tight_layout()167    return fig168 169 170# ------------------------------------------------------------171# Fast-Loop Simulation172# ------------------------------------------------------------173def run_pipeline(preset_name, passion, drama):174 175    preset = EMOTION_PRESETS[preset_name]176 177    text = preset["text"]178    natural = preset["raw"]179    target = preset["target"]180 181    amplified = apply_passion(natural, passion)182    cinematic = apply_drama(amplified, target, drama)183 184    color_params = infer_color(cinematic)185    color_block = render_color(color_params)186 187    fig = generate_scatter(188        natural,189        amplified,190        cinematic,191        target,192        preset_name,193        passion,194        drama195    )196 197    return (198        text,199        natural,200        amplified,201        cinematic,202        color_params,203        color_block,204        fig205    )206 207 208# ------------------------------------------------------------209# UI210# ------------------------------------------------------------211with gr.Blocks(title="Affection 👁️ — Edge Emotional Intelligence") as demo:212 213    gr.Markdown("# Affection 👁️")214    gr.Markdown("## Simulation Layer for an Edge AI Emotional Robotics System")215 216    gr.Markdown("### 🗣 Robot Speech")217 218    preset_selector = gr.Radio(219        choices=list(EMOTION_PRESETS.keys()),220        label="Select Transcript Sample",221        value=list(EMOTION_PRESETS.keys())[0],222    )223 224    transcript_output = gr.Textbox(label="Input Transcript", interactive=False)225 226    gr.Markdown("---")227 228    gr.Markdown("### ⚡ Edge Affect Processing")229 230    with gr.Row():231        passion = gr.Slider(0.0, 3.0, value=2.25, step=0.1, label="Passion")232        drama = gr.Slider(0.0, 1.5, value=0.65, step=0.05, label="Drama")233 234    with gr.Row():235        natural_output = gr.JSON(label="Natural")236        amplified_output = gr.JSON(label="After Passion")237        cinematic_output = gr.JSON(label="After Drama")238 239    scatter_output = gr.Plot(label="Valence–Arousal Projection")240 241    gr.Markdown("---")242 243    gr.Markdown("### 💡 Emotional Expression")244 245    rgb_output = gr.JSON(label="Model Output")246    color_display = gr.HTML(label="Rendered Expression")247 248    outputs = [249        transcript_output,250        natural_output,251        amplified_output,252        cinematic_output,253        rgb_output,254        color_display,255        scatter_output256    ]257 258    preset_selector.change(fn=run_pipeline, inputs=[preset_selector, passion, drama], outputs=outputs)259    passion.change(fn=run_pipeline, inputs=[preset_selector, passion, drama], outputs=outputs)260    drama.change(fn=run_pipeline, inputs=[preset_selector, passion, drama], outputs=outputs)261 262    demo.load(fn=run_pipeline, inputs=[preset_selector, passion, drama], outputs=outputs)263 264demo.launch(server_name="0.0.0.0", server_port=7860)265