|
""" |
|
CompI Emotion Processing Utilities |
|
|
|
This module provides utilities for Phase 2.C: Emotional/Contextual Input Integration |
|
- Emotion detection and sentiment analysis |
|
- Mood mapping and emotional context processing |
|
- Color palette generation based on emotions |
|
- Contextual prompt enhancement |
|
- Emoji and text-based emotion recognition |
|
""" |
|
|
|
import re |
|
import json |
|
from typing import Dict, List, Optional, Tuple, Union, Any |
|
from dataclasses import dataclass |
|
from enum import Enum |
|
import logging |
|
|
|
|
|
try: |
|
from textblob import TextBlob |
|
TEXTBLOB_AVAILABLE = True |
|
except ImportError: |
|
TEXTBLOB_AVAILABLE = False |
|
TextBlob = None |
|
|
|
try: |
|
import emoji |
|
EMOJI_AVAILABLE = True |
|
except ImportError: |
|
EMOJI_AVAILABLE = False |
|
emoji = None |
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
class EmotionCategory(Enum): |
|
"""Primary emotion categories""" |
|
JOY = "joy" |
|
SADNESS = "sadness" |
|
ANGER = "anger" |
|
FEAR = "fear" |
|
SURPRISE = "surprise" |
|
DISGUST = "disgust" |
|
LOVE = "love" |
|
ANTICIPATION = "anticipation" |
|
TRUST = "trust" |
|
NEUTRAL = "neutral" |
|
|
|
@dataclass |
|
class EmotionAnalysis: |
|
"""Container for emotion analysis results""" |
|
|
|
|
|
primary_emotion: EmotionCategory |
|
emotion_confidence: float |
|
|
|
|
|
sentiment_polarity: float |
|
sentiment_subjectivity: float |
|
|
|
|
|
emotion_scores: Dict[str, float] |
|
|
|
|
|
detected_emojis: List[str] |
|
emotion_keywords: List[str] |
|
intensity_level: str |
|
|
|
|
|
color_palette: List[str] |
|
artistic_descriptors: List[str] |
|
mood_modifiers: List[str] |
|
|
|
def to_dict(self) -> Dict[str, Any]: |
|
"""Convert to dictionary for JSON serialization""" |
|
return { |
|
'primary_emotion': self.primary_emotion.value, |
|
'emotion_confidence': self.emotion_confidence, |
|
'sentiment_polarity': self.sentiment_polarity, |
|
'sentiment_subjectivity': self.sentiment_subjectivity, |
|
'emotion_scores': self.emotion_scores, |
|
'detected_emojis': self.detected_emojis, |
|
'emotion_keywords': self.emotion_keywords, |
|
'intensity_level': self.intensity_level, |
|
'color_palette': self.color_palette, |
|
'artistic_descriptors': self.artistic_descriptors, |
|
'mood_modifiers': self.mood_modifiers |
|
} |
|
|
|
class EmotionProcessor: |
|
"""Core emotion processing and analysis functionality""" |
|
|
|
def __init__(self): |
|
"""Initialize the emotion processor with predefined mappings""" |
|
|
|
|
|
self.preset_emotions = { |
|
"joyful": {"category": EmotionCategory.JOY, "intensity": "high", "emoji": "😊"}, |
|
"happy": {"category": EmotionCategory.JOY, "intensity": "medium", "emoji": "😄"}, |
|
"ecstatic": {"category": EmotionCategory.JOY, "intensity": "high", "emoji": "🤩"}, |
|
"sad": {"category": EmotionCategory.SADNESS, "intensity": "medium", "emoji": "😢"}, |
|
"melancholic": {"category": EmotionCategory.SADNESS, "intensity": "high", "emoji": "😔"}, |
|
"depressed": {"category": EmotionCategory.SADNESS, "intensity": "high", "emoji": "😞"}, |
|
"angry": {"category": EmotionCategory.ANGER, "intensity": "high", "emoji": "😡"}, |
|
"frustrated": {"category": EmotionCategory.ANGER, "intensity": "medium", "emoji": "😤"}, |
|
"furious": {"category": EmotionCategory.ANGER, "intensity": "high", "emoji": "🤬"}, |
|
"fearful": {"category": EmotionCategory.FEAR, "intensity": "high", "emoji": "😱"}, |
|
"anxious": {"category": EmotionCategory.FEAR, "intensity": "medium", "emoji": "😰"}, |
|
"nervous": {"category": EmotionCategory.FEAR, "intensity": "low", "emoji": "😬"}, |
|
"surprised": {"category": EmotionCategory.SURPRISE, "intensity": "medium", "emoji": "😲"}, |
|
"amazed": {"category": EmotionCategory.SURPRISE, "intensity": "high", "emoji": "🤯"}, |
|
"romantic": {"category": EmotionCategory.LOVE, "intensity": "high", "emoji": "💖"}, |
|
"loving": {"category": EmotionCategory.LOVE, "intensity": "medium", "emoji": "❤️"}, |
|
"peaceful": {"category": EmotionCategory.TRUST, "intensity": "medium", "emoji": "🕊️"}, |
|
"serene": {"category": EmotionCategory.TRUST, "intensity": "high", "emoji": "🌱"}, |
|
"mysterious": {"category": EmotionCategory.ANTICIPATION, "intensity": "medium", "emoji": "🕵️♂️"}, |
|
"nostalgic": {"category": EmotionCategory.SADNESS, "intensity": "medium", "emoji": "🕰️"}, |
|
"energetic": {"category": EmotionCategory.JOY, "intensity": "high", "emoji": "⚡"}, |
|
"whimsical": {"category": EmotionCategory.JOY, "intensity": "medium", "emoji": "🎠"}, |
|
"uplifting": {"category": EmotionCategory.JOY, "intensity": "high", "emoji": "🌞"}, |
|
"dark": {"category": EmotionCategory.SADNESS, "intensity": "high", "emoji": "🌑"}, |
|
"moody": {"category": EmotionCategory.SADNESS, "intensity": "medium", "emoji": "🌫️"} |
|
} |
|
|
|
|
|
self.emotion_colors = { |
|
EmotionCategory.JOY: ["#FFD700", "#FFA500", "#FF69B4", "#00CED1", "#32CD32"], |
|
EmotionCategory.SADNESS: ["#4169E1", "#6495ED", "#708090", "#2F4F4F", "#191970"], |
|
EmotionCategory.ANGER: ["#DC143C", "#B22222", "#8B0000", "#FF4500", "#FF6347"], |
|
EmotionCategory.FEAR: ["#800080", "#4B0082", "#2E2E2E", "#696969", "#A9A9A9"], |
|
EmotionCategory.SURPRISE: ["#FF1493", "#FF69B4", "#FFB6C1", "#FFC0CB", "#FFFF00"], |
|
EmotionCategory.LOVE: ["#FF69B4", "#DC143C", "#FF1493", "#C71585", "#DB7093"], |
|
EmotionCategory.TRUST: ["#00CED1", "#20B2AA", "#48D1CC", "#40E0D0", "#AFEEEE"], |
|
EmotionCategory.ANTICIPATION: ["#9370DB", "#8A2BE2", "#7B68EE", "#6A5ACD", "#483D8B"], |
|
EmotionCategory.NEUTRAL: ["#808080", "#A9A9A9", "#C0C0C0", "#D3D3D3", "#DCDCDC"] |
|
} |
|
|
|
|
|
self.artistic_descriptors = { |
|
EmotionCategory.JOY: ["vibrant", "luminous", "radiant", "effervescent", "sparkling"], |
|
EmotionCategory.SADNESS: ["muted", "somber", "melancholic", "wistful", "contemplative"], |
|
EmotionCategory.ANGER: ["intense", "fiery", "bold", "dramatic", "powerful"], |
|
EmotionCategory.FEAR: ["shadowy", "mysterious", "ethereal", "haunting", "enigmatic"], |
|
EmotionCategory.SURPRISE: ["dynamic", "explosive", "unexpected", "striking", "vivid"], |
|
EmotionCategory.LOVE: ["warm", "tender", "passionate", "romantic", "intimate"], |
|
EmotionCategory.TRUST: ["serene", "peaceful", "harmonious", "balanced", "tranquil"], |
|
EmotionCategory.ANTICIPATION: ["electric", "suspenseful", "charged", "expectant", "tense"], |
|
EmotionCategory.NEUTRAL: ["balanced", "calm", "steady", "composed", "neutral"] |
|
} |
|
|
|
|
|
self.emoji_emotions = { |
|
"😊": EmotionCategory.JOY, "😄": EmotionCategory.JOY, "😃": EmotionCategory.JOY, |
|
"🤩": EmotionCategory.JOY, "😍": EmotionCategory.LOVE, "🥰": EmotionCategory.LOVE, |
|
"😢": EmotionCategory.SADNESS, "😭": EmotionCategory.SADNESS, "😔": EmotionCategory.SADNESS, |
|
"😡": EmotionCategory.ANGER, "🤬": EmotionCategory.ANGER, "😤": EmotionCategory.ANGER, |
|
"😱": EmotionCategory.FEAR, "😰": EmotionCategory.FEAR, "😨": EmotionCategory.FEAR, |
|
"😲": EmotionCategory.SURPRISE, "😮": EmotionCategory.SURPRISE, "🤯": EmotionCategory.SURPRISE, |
|
"❤️": EmotionCategory.LOVE, "💖": EmotionCategory.LOVE, "💕": EmotionCategory.LOVE, |
|
"🕊️": EmotionCategory.TRUST, "🌱": EmotionCategory.TRUST, "☮️": EmotionCategory.TRUST |
|
} |
|
|
|
|
|
self.emotion_keywords = { |
|
EmotionCategory.JOY: ["happy", "joyful", "cheerful", "delighted", "elated", "euphoric", "blissful"], |
|
EmotionCategory.SADNESS: ["sad", "depressed", "melancholy", "sorrowful", "gloomy", "dejected"], |
|
EmotionCategory.ANGER: ["angry", "furious", "rage", "irritated", "annoyed", "livid", "irate"], |
|
EmotionCategory.FEAR: ["afraid", "scared", "terrified", "anxious", "worried", "nervous", "fearful"], |
|
EmotionCategory.SURPRISE: ["surprised", "amazed", "astonished", "shocked", "stunned", "bewildered"], |
|
EmotionCategory.LOVE: ["love", "romantic", "affectionate", "tender", "passionate", "adoring"], |
|
EmotionCategory.TRUST: ["peaceful", "serene", "calm", "tranquil", "secure", "confident"], |
|
EmotionCategory.ANTICIPATION: ["excited", "eager", "hopeful", "expectant", "anticipating"] |
|
} |
|
|
|
def analyze_emotion(self, text: str, selected_emotion: Optional[str] = None) -> EmotionAnalysis: |
|
""" |
|
Comprehensive emotion analysis of input text |
|
|
|
Args: |
|
text: Input text to analyze |
|
selected_emotion: Optional pre-selected emotion |
|
|
|
Returns: |
|
EmotionAnalysis object with complete analysis |
|
""" |
|
logger.info(f"Analyzing emotion for text: {text[:100]}...") |
|
|
|
|
|
detected_emojis = self._extract_emojis(text) |
|
emotion_keywords = self._extract_emotion_keywords(text) |
|
|
|
|
|
if selected_emotion and selected_emotion.lower() in self.preset_emotions: |
|
|
|
emotion_info = self.preset_emotions[selected_emotion.lower()] |
|
primary_emotion = emotion_info["category"] |
|
emotion_confidence = 0.9 |
|
intensity_level = emotion_info["intensity"] |
|
else: |
|
|
|
primary_emotion, emotion_confidence, intensity_level = self._analyze_text_emotion(text, detected_emojis, emotion_keywords) |
|
|
|
|
|
sentiment_polarity, sentiment_subjectivity = self._analyze_sentiment(text) |
|
|
|
|
|
emotion_scores = self._generate_emotion_scores(primary_emotion, emotion_confidence) |
|
|
|
|
|
color_palette = self.emotion_colors.get(primary_emotion, self.emotion_colors[EmotionCategory.NEUTRAL]) |
|
artistic_descriptors = self.artistic_descriptors.get(primary_emotion, ["neutral"]) |
|
mood_modifiers = self._generate_mood_modifiers(primary_emotion, intensity_level) |
|
|
|
return EmotionAnalysis( |
|
primary_emotion=primary_emotion, |
|
emotion_confidence=emotion_confidence, |
|
sentiment_polarity=sentiment_polarity, |
|
sentiment_subjectivity=sentiment_subjectivity, |
|
emotion_scores=emotion_scores, |
|
detected_emojis=detected_emojis, |
|
emotion_keywords=emotion_keywords, |
|
intensity_level=intensity_level, |
|
color_palette=color_palette[:3], |
|
artistic_descriptors=artistic_descriptors[:3], |
|
mood_modifiers=mood_modifiers |
|
) |
|
|
|
def _extract_emojis(self, text: str) -> List[str]: |
|
"""Extract emojis from text""" |
|
if not EMOJI_AVAILABLE: |
|
|
|
emoji_pattern = re.compile( |
|
"[" |
|
"\U0001F600-\U0001F64F" |
|
"\U0001F300-\U0001F5FF" |
|
"\U0001F680-\U0001F6FF" |
|
"\U0001F1E0-\U0001F1FF" |
|
"\U00002702-\U000027B0" |
|
"\U000024C2-\U0001F251" |
|
"]+", |
|
flags=re.UNICODE |
|
) |
|
return emoji_pattern.findall(text) |
|
else: |
|
return [char for char in text if char in emoji.UNICODE_EMOJI['en']] |
|
|
|
def _extract_emotion_keywords(self, text: str) -> List[str]: |
|
"""Extract emotion-related keywords from text""" |
|
text_lower = text.lower() |
|
found_keywords = [] |
|
|
|
for emotion, keywords in self.emotion_keywords.items(): |
|
for keyword in keywords: |
|
if keyword in text_lower: |
|
found_keywords.append(keyword) |
|
|
|
return found_keywords |
|
|
|
def _analyze_text_emotion(self, text: str, emojis: List[str], keywords: List[str]) -> Tuple[EmotionCategory, float, str]: |
|
"""Analyze emotion from text, emojis, and keywords""" |
|
|
|
|
|
for emoji_char in emojis: |
|
if emoji_char in self.emoji_emotions: |
|
return self.emoji_emotions[emoji_char], 0.8, "medium" |
|
|
|
|
|
emotion_votes = {} |
|
for keyword in keywords: |
|
for emotion, emotion_keywords in self.emotion_keywords.items(): |
|
if keyword in emotion_keywords: |
|
emotion_votes[emotion] = emotion_votes.get(emotion, 0) + 1 |
|
|
|
if emotion_votes: |
|
primary_emotion = max(emotion_votes, key=emotion_votes.get) |
|
confidence = min(emotion_votes[primary_emotion] * 0.3, 0.9) |
|
intensity = "high" if emotion_votes[primary_emotion] > 2 else "medium" |
|
return primary_emotion, confidence, intensity |
|
|
|
|
|
sentiment_polarity, _ = self._analyze_sentiment(text) |
|
|
|
if sentiment_polarity > 0.3: |
|
return EmotionCategory.JOY, 0.6, "medium" |
|
elif sentiment_polarity < -0.3: |
|
return EmotionCategory.SADNESS, 0.6, "medium" |
|
else: |
|
return EmotionCategory.NEUTRAL, 0.5, "low" |
|
|
|
def _analyze_sentiment(self, text: str) -> Tuple[float, float]: |
|
"""Analyze sentiment using TextBlob or fallback method""" |
|
if not text.strip(): |
|
return 0.0, 0.0 |
|
|
|
if TEXTBLOB_AVAILABLE: |
|
try: |
|
blob = TextBlob(text) |
|
return blob.sentiment.polarity, blob.sentiment.subjectivity |
|
except Exception as e: |
|
logger.warning(f"TextBlob sentiment analysis failed: {e}") |
|
|
|
|
|
positive_words = ["good", "great", "excellent", "amazing", "wonderful", "fantastic", "love", "like", "happy", "joy"] |
|
negative_words = ["bad", "terrible", "awful", "hate", "dislike", "sad", "angry", "fear", "worried", "depressed"] |
|
|
|
text_lower = text.lower() |
|
positive_count = sum(1 for word in positive_words if word in text_lower) |
|
negative_count = sum(1 for word in negative_words if word in text_lower) |
|
|
|
total_words = len(text.split()) |
|
if total_words == 0: |
|
return 0.0, 0.0 |
|
|
|
polarity = (positive_count - negative_count) / max(total_words, 1) |
|
subjectivity = (positive_count + negative_count) / max(total_words, 1) |
|
|
|
return max(-1.0, min(1.0, polarity)), max(0.0, min(1.0, subjectivity)) |
|
|
|
def _generate_emotion_scores(self, primary_emotion: EmotionCategory, confidence: float) -> Dict[str, float]: |
|
"""Generate scores for all emotions""" |
|
scores = {emotion.value: 0.1 for emotion in EmotionCategory} |
|
scores[primary_emotion.value] = confidence |
|
|
|
|
|
secondary_emotions = { |
|
EmotionCategory.JOY: [EmotionCategory.LOVE, EmotionCategory.TRUST], |
|
EmotionCategory.SADNESS: [EmotionCategory.FEAR, EmotionCategory.NEUTRAL], |
|
EmotionCategory.ANGER: [EmotionCategory.DISGUST, EmotionCategory.FEAR], |
|
EmotionCategory.FEAR: [EmotionCategory.SADNESS, EmotionCategory.SURPRISE], |
|
EmotionCategory.LOVE: [EmotionCategory.JOY, EmotionCategory.TRUST], |
|
EmotionCategory.TRUST: [EmotionCategory.JOY, EmotionCategory.LOVE] |
|
} |
|
|
|
if primary_emotion in secondary_emotions: |
|
for secondary in secondary_emotions[primary_emotion]: |
|
scores[secondary.value] = min(0.4, confidence * 0.5) |
|
|
|
return scores |
|
|
|
def _generate_mood_modifiers(self, emotion: EmotionCategory, intensity: str) -> List[str]: |
|
"""Generate mood modifiers for prompt enhancement""" |
|
base_modifiers = { |
|
EmotionCategory.JOY: ["bright", "cheerful", "uplifting", "radiant"], |
|
EmotionCategory.SADNESS: ["melancholic", "somber", "wistful", "contemplative"], |
|
EmotionCategory.ANGER: ["intense", "dramatic", "powerful", "bold"], |
|
EmotionCategory.FEAR: ["mysterious", "dark", "ethereal", "haunting"], |
|
EmotionCategory.SURPRISE: ["dynamic", "striking", "unexpected", "vivid"], |
|
EmotionCategory.LOVE: ["romantic", "warm", "tender", "passionate"], |
|
EmotionCategory.TRUST: ["peaceful", "serene", "harmonious", "tranquil"], |
|
EmotionCategory.ANTICIPATION: ["electric", "suspenseful", "charged", "expectant"], |
|
EmotionCategory.NEUTRAL: ["balanced", "calm", "neutral", "composed"] |
|
} |
|
|
|
modifiers = base_modifiers.get(emotion, ["neutral"]) |
|
|
|
|
|
if intensity == "high": |
|
intensity_modifiers = ["very", "extremely", "deeply", "intensely"] |
|
return [f"{intensity_modifiers[0]} {mod}" for mod in modifiers[:2]] |
|
elif intensity == "low": |
|
return [f"subtly {mod}" for mod in modifiers[:2]] |
|
else: |
|
return modifiers[:3] |
|
|
|
|
|
class EmotionalPromptEnhancer: |
|
"""Enhance prompts with emotional context""" |
|
|
|
def __init__(self): |
|
"""Initialize the prompt enhancer""" |
|
self.emotion_processor = EmotionProcessor() |
|
|
|
def enhance_prompt_with_emotion( |
|
self, |
|
base_prompt: str, |
|
style: str, |
|
emotion_analysis: EmotionAnalysis, |
|
enhancement_strength: float = 0.7 |
|
) -> str: |
|
""" |
|
Enhance prompt with emotional context |
|
|
|
Args: |
|
base_prompt: Original text prompt |
|
style: Art style |
|
emotion_analysis: Emotion analysis results |
|
enhancement_strength: How strongly to apply emotion (0-1) |
|
|
|
Returns: |
|
Enhanced prompt with emotional context |
|
""" |
|
enhanced_prompt = base_prompt.strip() |
|
|
|
|
|
if style: |
|
enhanced_prompt += f", {style}" |
|
|
|
|
|
if enhancement_strength > 0.5: |
|
|
|
descriptors = emotion_analysis.artistic_descriptors[:2] |
|
mood_modifiers = emotion_analysis.mood_modifiers[:2] |
|
|
|
enhanced_prompt += f", {', '.join(descriptors)}" |
|
enhanced_prompt += f", with a {', '.join(mood_modifiers)} atmosphere" |
|
|
|
|
|
if emotion_analysis.intensity_level == "high": |
|
enhanced_prompt += f", deeply {emotion_analysis.primary_emotion.value}" |
|
|
|
elif enhancement_strength > 0.2: |
|
|
|
descriptor = emotion_analysis.artistic_descriptors[0] |
|
mood = emotion_analysis.mood_modifiers[0] |
|
|
|
enhanced_prompt += f", {descriptor}, {mood}" |
|
|
|
else: |
|
|
|
if emotion_analysis.artistic_descriptors: |
|
enhanced_prompt += f", {emotion_analysis.artistic_descriptors[0]}" |
|
|
|
return enhanced_prompt |
|
|
|
def generate_emotion_tags(self, emotion_analysis: EmotionAnalysis) -> List[str]: |
|
"""Generate descriptive tags for the emotion""" |
|
tags = [] |
|
|
|
|
|
tags.append(emotion_analysis.primary_emotion.value) |
|
|
|
|
|
tags.append(f"{emotion_analysis.intensity_level}_intensity") |
|
|
|
|
|
if emotion_analysis.sentiment_polarity > 0.3: |
|
tags.append("positive_sentiment") |
|
elif emotion_analysis.sentiment_polarity < -0.3: |
|
tags.append("negative_sentiment") |
|
else: |
|
tags.append("neutral_sentiment") |
|
|
|
|
|
tags.extend(emotion_analysis.artistic_descriptors[:2]) |
|
|
|
return tags |
|
|