Spaces:
Running on Zero
Running on Zero
File size: 8,534 Bytes
2eeb7a5 a8292c4 2eeb7a5 a8292c4 2eeb7a5 a8292c4 2eeb7a5 a8292c4 2eeb7a5 2469bd0 2eeb7a5 d910f6e 2eeb7a5 a8292c4 2eeb7a5 a8292c4 2eeb7a5 a8292c4 2eeb7a5 2469bd0 2eeb7a5 2469bd0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | import os
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
os.environ.setdefault("IMAGE_MAX_TOKEN_NUM", "1024")
os.environ.setdefault("VIDEO_MAX_TOKEN_NUM", "128")
os.environ.setdefault("FPS_MAX_FRAMES", "16")
import spaces # MUST come before torch / any CUDA-touching import
import torch
import gradio as gr
from transformers import Qwen3_5ForConditionalGeneration, AutoProcessor
from qwen_vl_utils import process_vision_info
MODEL_ID = "Jiaha0Hu4ng/OneEmo"
model = Qwen3_5ForConditionalGeneration.from_pretrained(
MODEL_ID,
torch_dtype=torch.bfloat16,
attn_implementation="sdpa",
).to("cuda").eval()
processor = AutoProcessor.from_pretrained(MODEL_ID)
# ββ Task prompts (ported 1:1 from the OneEmo repo prompts/ directory) βββββββ
MER_PROMPT = "[MER]<video>Please identify the emotions of the person in the video. Answer with a single emotion."
OVMER_PROMPT = "[OVMER]<video>What feelings does the character show? List them briefly."
MSA_PROMPT = "[MSA]<video>Please analyze the sentiment of the characters in the video and label them as positive, neutral or negative."
MHD_PROMPT = "[MHD]<video>Based on the context of the speaker and visual cues, determine whether there is a humorous expression, answer with yes or no."
MSD_PROMPT = "[MSD]<video>Please judge based on the context whether the speaker in this round is being sarcastic, answer with yes or no."
ERG_PROMPT = (
"[ERG]<video>You are an empathetic listener, your goal is to understand the user's emotions "
"and intentions, and respond or comfort them with appropriate language that helps them feel "
"understood and cared for.\n Avoid rushing into your response; instead, carefully engage in "
"a step-by-step, in-depth analysis before providing an answer.\n Please analyze using Chain "
"of Empathy (Firstly, Event scenario:Reflect on the event scenarios that arise from the "
"ongoing dialogue. Secondly, User's emotion:Analyze both the implicit and explicit emotions "
"conveyed by the user. Thirdly, the emotion cause:Infer the underlying reasons for the "
"user's emotions. Fourthly, determine the goal of your response in this particular instance, "
"such as alleviating anxiety, offering reassurance, or expressing understanding.) in imd "
"monospace tags and with Line break, then provide your empathetic response."
)
TASK_PROMPTS = {
"MER β Basic Emotion Recognition": MER_PROMPT,
"OVMER β Open-Vocabulary Emotion Recognition": OVMER_PROMPT,
"MSA β Multimodal Sentiment Analysis": MSA_PROMPT,
"MHD β Humor Detection": MHD_PROMPT,
"MSD β Sarcasm Detection": MSD_PROMPT,
"ERG β Empathetic Response Generation": ERG_PROMPT,
}
@spaces.GPU(duration=60)
def analyze_emotion(
video,
task: str,
transcript: str = "",
enable_thinking: bool = True,
max_tokens: int = 2048,
temperature: float = 0.7,
progress=gr.Progress(track_tqdm=True),
):
"""Analyze emotions in a video using OneEmo, a unified multimodal reasoning model.
Args:
video: Input video file path.
task: Emotion analysis task type.
transcript: Optional transcript of speech in the video.
enable_thinking: Whether to enable chain-of-thought reasoning.
max_tokens: Maximum number of new tokens to generate.
temperature: Sampling temperature.
"""
if video is None:
return "Please upload a video file."
base_prompt = TASK_PROMPTS[task]
if transcript.strip():
if task.startswith("ERG"):
prompt = f"{base_prompt}\n{transcript}"
else:
prompt = f"{base_prompt}\nHere is what the character says: {transcript}"
else:
prompt = base_prompt
messages = [
{
"role": "user",
"content": [
{
"type": "video",
"video": video,
"max_pixels": 360 * 420,
"fps": 2.0,
},
{
"type": "text",
"text": prompt,
},
],
}
]
text = processor.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=enable_thinking,
)
image_inputs, video_inputs = process_vision_info(messages)
inputs = processor(
text=[text],
images=image_inputs,
videos=video_inputs,
padding=True,
return_tensors="pt",
).to("cuda")
with torch.no_grad():
generated_ids = model.generate(
**inputs,
max_new_tokens=max_tokens,
temperature=temperature,
top_p=0.9,
top_k=50,
do_sample=temperature > 0,
)
generated_ids_trimmed = [
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
]
output_text = processor.batch_decode(
generated_ids_trimmed,
skip_special_tokens=True,
clean_up_tokenization_spaces=False,
)
return output_text[0]
# ββ UI βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
CSS = """
#col-container { max-width: 1100px; margin: 0 auto; }
.dark .gradio-container { color: var(--body-text-color); }
"""
with gr.Blocks() as demo:
gr.Markdown(
"""
# OneEmo: Unified Multimodal Reasoning for Emotion Perception
Upload a video and select an emotion analysis task. OneEmo is a unified
multimodal reasoning model that supports emotion recognition, sentiment
analysis, humor/sarcasm detection, and empathetic response generation.
[Paper](https://arxiv.org/abs/2608.06013) Β· [Model](https://huggingface.co/Jiaha0Hu4ng/OneEmo) Β· [Code](https://github.com/waHAHJIAHAO/OneEmo)
"""
)
with gr.Column(elem_id="col-container"):
with gr.Row():
with gr.Column(scale=1):
video_input = gr.Video(label="Input Video")
task_dropdown = gr.Dropdown(
choices=list(TASK_PROMPTS.keys()),
value="MER β Basic Emotion Recognition",
label="Task",
info="Choose the emotion analysis task",
)
transcript_input = gr.Textbox(
label="Transcript (optional)",
placeholder="Optional transcript of speech in the videoβ¦",
lines=2,
)
run_btn = gr.Button("Analyze", variant="primary")
with gr.Column(scale=1):
output = gr.Markdown(
label="Result",
value="Upload a video and click **Analyze** to see the emotion analysis.",
)
with gr.Accordion("Advanced settings", open=False):
enable_thinking = gr.Checkbox(
label="Enable thinking (chain-of-thought)",
value=True,
)
max_tokens = gr.Slider(
label="Max new tokens",
minimum=256,
maximum=4096,
value=2048,
step=256,
)
temperature = gr.Slider(
label="Temperature",
minimum=0.0,
maximum=1.5,
value=0.7,
step=0.1,
)
gr.Examples(
examples=[
["examples/man_laughing.mp4", "MER β Basic Emotion Recognition", ""],
["examples/man_sad.mp4", "OVMER β Open-Vocabulary Emotion Recognition", ""],
["examples/2_women_arguing.mp4", "MSA β Multimodal Sentiment Analysis", ""],
["examples/man_smiling_studio.mp4", "ERG β Empathetic Response Generation", ""],
],
inputs=[video_input, task_dropdown, transcript_input],
outputs=output,
fn=analyze_emotion,
cache_examples=True,
cache_mode="lazy",
)
run_btn.click(
fn=analyze_emotion,
inputs=[video_input, task_dropdown, transcript_input, enable_thinking, max_tokens, temperature],
outputs=output,
api_name="analyze",
)
if __name__ == "__main__":
demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS) |