Проблемы распознавания эмоций в современных условиях
Защита через месяц, а работа не готова?
Наши эксперты выполнят ВКР по распознаванию эмоций всего за 14 дней! Напишите в Telegram прямо сейчас и получите бесплатную консультацию по выбору архитектуры системы.
Современные системы взаимодействия человека и компьютера все чаще требуют понимания эмоционального состояния пользователя для повышения качества взаимодействия. Согласно исследованию MIT Technology Review (2024), системы, способные распознавать эмоции, повышают удовлетворенность пользователей на 35-40% и эффективность взаимодействия на 25-30%. Однако точное распознавание эмоций остается сложной задачей из-за их субъективности, культурных различий и многообразия проявлений. Традиционные подходы, основанные только на анализе изображений или аудио, часто не учитывают контекст и семантику, что приводит к ошибкам в распознавании.
Актуальность реализации распознавания эмоций на основе нейронных сетей и логико-семантических технологий обусловлена растущей потребностью в более точных и контекстно-зависимых системах распознавания эмоций. Это особенно важно для студентов ФИТ НГУ, изучающих прикладную информатику и методы искусственного интеллекта, так как позволяет применить теоретические знания на практике и получить навыки работы с современными технологиями компьютерного зрения, обработки естественного языка и логического вывода.
В данной статье мы подробно рассмотрим современные подходы к распознаванию эмоций с использованием нейронных сетей и логико-семантических технологий. Вы узнаете о ключевых архитектурных решениях, практических методах реализации и рекомендациях по созданию эффективных систем. Мы также разберем типичные ошибки, которые допускают студенты при работе с этой сложной темой, и предложим проверенные решения для успешного выполнения ВКР.
Эта тема особенно важна для студентов ФИТ НГУ, так как требует комплексного применения знаний в области машинного обучения, компьютерного зрения и логических систем. Успешная реализация подобного проекта не только поможет в написании качественной выпускной квалификационной работы, но и станет ценным навыком для будущей профессиональной деятельности в области разработки систем искусственного интеллекта и анализа поведения.
Если вы испытываете трудности с пониманием архитектуры нейронных сетей или реализацией конкретных компонентов системы распознавания эмоций, рекомендуем ознакомиться с нашими гарантиями и отзывами клиентов, которые подтверждают высокое качество наших услуг.
Дополнительный список тем для ВКР ФИТ НГУ на 2025-2026 учебный год можно найти здесь.
Срочная помощь по вашей теме: Получите консультацию за 10 минут! Telegram: @Diplomit Телефон/WhatsApp: +7 (987) 915-99-32, Email: admin@diplom-it.ru
Оформите заказ онлайн: Заказать ВКР ФИТ НГУ
Основы распознавания эмоций
Ключевые проблемы распознавания эмоций
Проблема | Описание | Требования к решению |
---|---|---|
Субъективность эмоций | Эмоции субъективны и могут по-разному проявляться у разных людей | Индивидуальная адаптация, учет личных особенностей пользователя |
Культурные различия | Выражение эмоций варьируется в зависимости от культуры | Многоязычная и межкультурная поддержка, адаптация к культурным нормам |
Контекстная зависимость | Одно и то же выражение может означать разные эмоции в разных контекстах | Интеграция контекстной информации, логико-семантический анализ |
Много модальностей | Эмоции проявляются через лицо, голос, язык тела, текст | Мультимодальный анализ, интеграция данных из разных источников |
Этические вопросы | Проблемы приватности и этичности распознавания эмоций | Системы согласия, анонимизация данных, прозрачность алгоритмов |
Технические основы распознавания эмоций
Реализация распознавания эмоций на основе нейронных сетей и логико-семантических технологий основывается на ряде ключевых концепций:
Основы распознавания эмоций
- Компьютерное зрение — методы анализа изображений и видео для распознавания выражений лица
- Обработка речи — анализ аудиосигнала для определения эмоциональной окраски речи
- Обработка естественного языка — анализ текста для определения эмоциональной окраски
- Мультимодальный анализ — интеграция данных из разных источников для повышения точности
- Логико-семантические системы — использование логических правил и семантических моделей для интерпретации данных
- Глубокое обучение — применение нейронных сетей для распознавания сложных паттернов
Эти концепции лежат в основе современных систем распознавания эмоций и должны быть хорошо поняты при разработке таких систем.
Современные подходы к распознаванию эмоций
В последние годы в области распознавания эмоций наблюдается несколько ключевых тенденций:
Подход | Описание | Примеры применения |
---|---|---|
Мультимодальный анализ | Интеграция данных из разных источников (лицо, голос, текст, жесты) | Системы анализа клиентского опыта, виртуальные собеседования, образовательные платформы |
Контекстно-зависимый анализ | Учет контекста взаимодействия для интерпретации эмоций | Анализ встреч, мониторинг психоэмоционального состояния, персонализированные рекомендации |
Логико-семантические модели | Использование логических правил и семантических моделей для интерпретации | Системы принятия решений, анализ социальных взаимодействий, диагностика эмоциональных расстройств |
Персонализированный анализ | Адаптация системы к индивидуальным особенностям пользователя | Персональные ассистенты, системы мониторинга здоровья, образовательные платформы |
Реальный-time анализ | Анализ эмоций в реальном времени с минимальной задержкой | Интерактивные системы, геймификация, виртуальная реальность |
Архитектура и реализация системы распознавания эмоций
Выбор архитектурного подхода
Для эффективной реализации системы распознавания эмоций на основе нейронных сетей и логико-семантических технологий рекомендуется использовать следующую архитектуру:
Архитектура системы распознавания эмоций
- Слой сбора данных — получение данных из различных источников (камера, микрофон, текстовый ввод)
- Слой предварительной обработки — очистка, нормализация и подготовка данных
- Слой модального анализа — распознавание эмоций в каждом модальном канале
- Слой интеграции модальностей — объединение результатов из разных источников
- Слой логико-семантического анализа — интерпретация результатов с учетом контекста
- Слой принятия решений — формирование выводов и рекомендаций на основе анализа
- Слой обратной связи — сбор данных для улучшения системы
Эта многоуровневая архитектура обеспечивает гибкость и возможность расширения функциональности без переработки всей системы.
Пример реализации системы распознавания эмоций на Python
Рассмотрим пример реализации ключевых компонентов системы распознавания эмоций:
# emotion_recognition.py # Реализация системы распознавания эмоций на основе нейронных сетей и логико-семантических технологий import cv2 import numpy as np import torch import torch.nn as nn import torch.nn.functional as F from transformers import pipeline, AutoModelForSequenceClassification, AutoTokenizer import logging from typing import Dict, List, Tuple, Optional, Any import json from sklearn.metrics.pairwise import cosine_similarity import re # Настройка логирования logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class FaceEmotionAnalyzer: """Анализ эмоций по изображению лица""" def __init__(self, model_path: str = "fer2013_resnet50.pth"): self.model = self._load_model(model_path) self.emotion_labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral'] def _load_model(self, model_path: str): """Загрузка предобученной модели для распознавания эмоций по лицу""" # В реальной системе здесь будет загрузка модели # Для примера создаем заглушку class MockModel(nn.Module): def __init__(self): super().__init__() def forward(self, x): # Генерируем случайные вероятности для демонстрации batch_size = x.shape[0] return torch.rand(batch_size, 7) return MockModel() def detect_faces(self, frame: np.ndarray) -> List[Tuple[int, int, int, int]]: """Обнаружение лиц на изображении""" # Используем каскад Хаара для обнаружения лиц face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.3, 5) return faces def preprocess_face(self, face_img: np.ndarray, size: tuple = (48, 48)) -> torch.Tensor: """Предварительная обработка изображения лица""" # Конвертация в оттенки серого if len(face_img.shape) == 3: face_img = cv2.cvtColor(face_img, cv2.COLOR_BGR2GRAY) # Изменение размера face_img = cv2.resize(face_img, size) # Нормализация face_img = face_img / 255.0 # Добавление размерностей для батча и каналов face_img = np.expand_dims(face_img, axis=0) face_img = np.expand_dims(face_img, axis=0) # Конвертация в тензор return torch.tensor(face_img, dtype=torch.float32) def analyze_frame(self, frame: np.ndarray) -> List[Dict]: """ Анализ кадра на наличие эмоций Args: frame: Кадр видео в формате numpy array Returns: Список обнаруженных эмоций с координатами лиц и вероятностями """ faces = self.detect_faces(frame) results = [] for (x, y, w, h) in faces: # Извлечение области лица face_img = frame[y:y+h, x:x+w] # Предварительная обработка processed_face = self.preprocess_face(face_img) # Предсказание эмоции with torch.no_grad(): outputs = self.model(processed_face) probabilities = F.softmax(outputs, dim=1)[0] # Формирование результата emotion_probs = {self.emotion_labels[i]: float(probabilities[i]) for i in range(len(self.emotion_labels))} dominant_emotion = max(emotion_probs, key=emotion_probs.get) results.append({ "bbox": (int(x), int(y), int(w), int(h)), "dominant_emotion": dominant_emotion, "emotion_probs": emotion_probs, "confidence": float(emotion_probs[dominant_emotion]) }) return results class SpeechEmotionAnalyzer: """Анализ эмоций по речи""" def __init__(self, model_name: str = "jungjin5/emotion_recognition_wav2vec2"): # В реальной системе здесь будет загрузка модели # Для примера создаем заглушку self.emotion_labels = ['neutral', 'calm', 'happy', 'sad', 'angry', 'fearful', 'disgust', 'surprised'] logger.info(f"Speech emotion analyzer initialized with model {model_name}") def analyze_audio(self, audio_path: str) -> Dict: """ Анализ аудио на наличие эмоций Args: audio_path: Путь к аудиофайлу Returns: Словарь с результатами анализа """ # В реальной системе здесь будет обработка аудио # Для примера генерируем случайные результаты # Имитация анализа аудио emotion_probs = {emotion: np.random.random() for emotion in self.emotion_labels} total = sum(emotion_probs.values()) emotion_probs = {k: v/total for k, v in emotion_probs.items()} dominant_emotion = max(emotion_probs, key=emotion_probs.get) return { "dominant_emotion": dominant_emotion, "emotion_probs": emotion_probs, "confidence": emotion_probs[dominant_emotion] } class TextEmotionAnalyzer: """Анализ эмоций по тексту""" def __init__(self, model_name: str = "j-hartmann/emotion-english-distilroberta-base"): self.tokenizer = AutoTokenizer.from_pretrained(model_name) self.model = AutoModelForSequenceClassification.from_pretrained(model_name) self.emotion_labels = ['anger', 'disgust', 'fear', 'joy', 'neutral', 'sadness', 'surprise'] def analyze_text(self, text: str) -> Dict: """ Анализ текста на наличие эмоций Args: text: Текст для анализа Returns: Словарь с результатами анализа """ # Токенизация inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512, padding=True) # Предсказание with torch.no_grad(): outputs = self.model(**inputs) probabilities = F.softmax(outputs.logits, dim=1)[0] # Формирование результата emotion_probs = {self.emotion_labels[i]: float(probabilities[i]) for i in range(len(self.emotion_labels))} dominant_emotion = max(emotion_probs, key=emotion_probs.get) return { "dominant_emotion": dominant_emotion, "emotion_probs": emotion_probs, "confidence": float(emotion_probs[dominant_emotion]) } class MultimodalFusion: """Интеграция результатов из разных модальностей""" def __init__(self, weights: Dict[str, float] = None): # Веса для разных модальностей self.weights = weights or { "face": 0.4, "speech": 0.3, "text": 0.3 } # Проверка, что сумма весов равна 1 total_weight = sum(self.weights.values()) if not np.isclose(total_weight, 1.0): # Нормализация весов self.weights = {k: v/total_weight for k, v in self.weights.items()} def fuse_emotions(self, face_results: List[Dict], speech_result: Dict, text_result: Dict) -> Dict: """ Объединение результатов из разных модальностей Args: face_results: Результаты анализа лица (может быть несколько лиц) speech_result: Результаты анализа речи text_result: Результаты анализа текста Returns: Объединенные результаты анализа """ # Для простоты предполагаем, что анализируем одно лицо if not face_results: face_emotion_probs = {emotion: 0.0 for emotion in self._get_all_emotions()} else: # Берем первое лицо для примера face_emotion_probs = face_results[0]["emotion_probs"] # Приведение к общему набору эмоций face_probs = self._normalize_emotions(face_emotion_probs, "face") speech_probs = self._normalize_emotions(speech_result["emotion_probs"], "speech") text_probs = self._normalize_emotions(text_result["emotion_probs"], "text") # Объединение вероятностей fused_probs = {} all_emotions = set(face_probs.keys()) | set(speech_probs.keys()) | set(text_probs.keys()) for emotion in all_emotions: weighted_sum = ( self.weights["face"] * face_probs.get(emotion, 0.0) + self.weights["speech"] * speech_probs.get(emotion, 0.0) + self.weights["text"] * text_probs.get(emotion, 0.0) ) fused_probs[emotion] = weighted_sum # Нормализация total = sum(fused_probs.values()) if total > 0: fused_probs = {k: v/total for k, v in fused_probs.items()} # Определение доминирующей эмоции dominant_emotion = max(fused_probs, key=fused_probs.get) return { "dominant_emotion": dominant_emotion, "fused_emotion_probs": fused_probs, "face_probs": face_probs, "speech_probs": speech_probs, "text_probs": text_probs, "confidence": fused_probs[dominant_emotion] } def _get_all_emotions(self) -> List[str]: """Получение общего набора эмоций""" return ['anger', 'disgust', 'fear', 'happy', 'sad', 'surprise', 'neutral'] def _normalize_emotions(self, probs: Dict[str, float], modality: str) -> Dict[str, float]: """Нормализация вероятностей эмоций к общему набору""" normalized = {} common_emotions = self._get_all_emotions() # Сопоставление эмоций разных модальностей mapping = { "face": { "angry": "anger", "disgust": "disgust", "fear": "fear", "happy": "happy", "sad": "sad", "surprise": "surprise", "neutral": "neutral" }, "speech": { "angry": "anger", "disgust": "disgust", "fearful": "fear", "happy": "happy", "sad": "sad", "surprised": "surprise", "neutral": "neutral" }, "text": { "anger": "anger", "disgust": "disgust", "fear": "fear", "joy": "happy", "sadness": "sad", "surprise": "surprise", "neutral": "neutral" } } for src_emotion, prob in probs.items(): # Пытаемся найти соответствие в общем наборе target_emotion = mapping[modality].get(src_emotion, None) if target_emotion and target_emotion in common_emotions: normalized[target_emotion] = normalized.get(target_emotion, 0.0) + prob # Нормализация полученных вероятностей total = sum(normalized.values()) if total > 0: normalized = {k: v/total for k, v in normalized.items()} # Добавляем отсутствующие эмоции с нулевой вероятностью for emotion in common_emotions: if emotion not in normalized: normalized[emotion] = 0.0 return normalized class SemanticEmotionInterpreter: """Логико-семантическая интерпретация эмоций""" def __init__(self, knowledge_base_path: str = "emotion_knowledge_base.json"): self.knowledge_base = self._load_knowledge_base(knowledge_base_path) def _load_knowledge_base(self, path: str) -> Dict: """Загрузка базы знаний для семантической интерпретации""" # В реальной системе здесь будет загрузка из файла # Для примера создаем базу знаний в коде return { "context_rules": [ { "condition": "topic == 'bad_news'", "effects": {"sad": 0.3, "fear": 0.2} }, { "condition": "topic == 'good_news'", "effects": {"happy": 0.4, "surprise": 0.1} }, { "condition": "interruption_count > 3", "effects": {"anger": 0.3, "frustration": 0.2} } ], "cultural_rules": { "western": { "smiling": {"happy": 0.7, "polite": 0.3}, "eye_contact": {"confidence": 0.6, "aggression": 0.1} }, "eastern": { "smiling": {"polite": 0.6, "happy": 0.4}, "eye_contact": {"respect": 0.5, "disrespect": 0.2} } }, "temporal_patterns": [ { "pattern": "happy -> sad", "interpretation": "disappointment", "weight": 0.7 }, { "pattern": "neutral -> angry", "interpretation": "sudden frustration", "weight": 0.8 } ] } def interpret_emotions(self, fused_emotion: Dict, context: Dict = None, cultural_background: str = "western") -> Dict: """ Семантическая интерпретация эмоций с учетом контекста Args: fused_emotion: Объединенные результаты анализа эмоций context: Контекстная информация cultural_background: Культурный контекст Returns: Интерпретированные результаты с учетом контекста """ context = context or {} result = { "base_emotions": fused_emotion["fused_emotion_probs"], "interpreted_emotions": dict(fused_emotion["fused_emotion_probs"]), "explanations": [], "context_influences": [] } # 1. Применение контекстных правил for rule in self.knowledge_base["context_rules"]: try: # Оценка условия правила if self._evaluate_condition(rule["condition"], context): # Применение эффектов for emotion, effect in rule["effects"].items(): if emotion in result["interpreted_emotions"]: result["interpreted_emotions"][emotion] = min(1.0, result["interpreted_emotions"][emotion] + effect) result["context_influences"].append(f"Контекст '{rule['condition']}' увеличил вероятность '{emotion}' на {effect}") except Exception as e: logger.error(f"Ошибка при применении контекстного правила: {str(e)}") # 2. Применение культурных правил cultural_rules = self.knowledge_base["cultural_rules"].get(cultural_background, {}) if "smiling" in context and cultural_rules.get("smiling"): for emotion, weight in cultural_rules["smiling"].items(): if emotion in result["interpreted_emotions"]: # Адаптация интерпретации улыбки в зависимости от культуры smiling_intensity = context.get("smiling_intensity", 0.5) adjustment = weight * smiling_intensity result["interpreted_emotions"][emotion] = min(1.0, result["interpreted_emotions"][emotion] + adjustment) result["context_influences"].append(f"Культурный контекст скорректировал интерпретацию улыбки для '{emotion}' на {adjustment}") # 3. Нормализация после корректировок total = sum(result["interpreted_emotions"].values()) if total > 0: result["interpreted_emotions"] = {k: v/total for k, v in result["interpreted_emotions"].items()} # 4. Определение доминирующей интерпретированной эмоции dominant_emotion = max(result["interpreted_emotions"], key=result["interpreted_emotions"].get) result["interpreted_dominant_emotion"] = dominant_emotion result["interpreted_confidence"] = result["interpreted_emotions"][dominant_emotion] return result def _evaluate_condition(self, condition: str, context: Dict) -> bool: """Оценка условия контекстного правила""" # Упрощенная реализация для примера # В реальной системе нужно использовать безопасный парсер выражений try: # Замена операторов для безопасного выполнения safe_condition = condition.replace("==", " == ").replace(">", " > ").replace("<", " < ") # Разбор условия parts = safe_condition.split() if len(parts) == 3: var, op, val = parts if var in context: # Преобразование значения контекста ctx_val = context[var] # Преобразование значения условия if val.replace('.', '').isdigit(): val = float(val) if '.' in val else int(val) elif val in ["True", "False"]: val = val == "True" # Оценка условия if op == "==": return ctx_val == val elif op == ">": return ctx_val > val elif op == "<": return ctx_val < val return False except Exception as e: logger.error(f"Ошибка при оценке условия '{condition}': {str(e)}") return False class EmotionRecognitionSystem: """Основная система распознавания эмоций""" def __init__(self, face_model_path: str = "fer2013_resnet50.pth", text_model_name: str = "j-hartmann/emotion-english-distilroberta-base", cultural_background: str = "western"): self.face_analyzer = FaceEmotionAnalyzer(face_model_path) self.speech_analyzer = SpeechEmotionAnalyzer() self.text_analyzer = TextEmotionAnalyzer(text_model_name) self.multimodal_fusion = MultimodalFusion() self.semantic_interpreter = SemanticEmotionInterpreter() self.cultural_background = cultural_background def analyze_interaction(self, video_frame: np.ndarray = None, audio_path: str = None, text_input: str = None, context: Dict = None) -> Dict: """ Анализ взаимодействия на основе различных модальностей Args: video_frame: Кадр видео audio_path: Путь к аудиофайлу text_input: Текстовый ввод context: Контекстная информация Returns: Результаты анализа """ result = { "timestamp": str(datetime.now()), "raw_analysis": {}, "fused_analysis": None, "interpreted_analysis": None, "status": "success" } try: # Анализ по лицу if video_frame is not None: face_results = self.face_analyzer.analyze_frame(video_frame) result["raw_analysis"]["face"] = face_results else: result["raw_analysis"]["face"] = [] # Анализ по речи if audio_path is not None: speech_result = self.speech_analyzer.analyze_audio(audio_path) result["raw_analysis"]["speech"] = speech_result else: result["raw_analysis"]["speech"] = { "dominant_emotion": "neutral", "emotion_probs": {e: 0.0 for e in self.speech_analyzer.emotion_labels}, "confidence": 0.0 } # Анализ по тексту if text_input and text_input.strip(): text_result = self.text_analyzer.analyze_text(text_input) result["raw_analysis"]["text"] = text_result else: result["raw_analysis"]["text"] = { "dominant_emotion": "neutral", "emotion_probs": {e: 0.0 for e in self.text_analyzer.emotion_labels}, "confidence": 0.0 } # Объединение результатов fused_result = self.multimodal_fusion.fuse_emotions( result["raw_analysis"]["face"], result["raw_analysis"]["speech"], result["raw_analysis"]["text"] ) result["fused_analysis"] = fused_result # Семантическая интерпретация interpreted_result = self.semantic_interpreter.interpret_emotions( fused_result, context, self.cultural_background ) result["interpreted_analysis"] = interpreted_result except Exception as e: logger.error(f"Ошибка анализа взаимодействия: {str(e)}") result["status"] = "error" result["error"] = str(e) return result def process_video_stream(self, video_source: int = 0, callback=None): """ Обработка видеопотока в реальном времени Args: video_source: Источник видео (0 - веб-камера) callback: Функция обратного вызова для обработки результатов """ cap = cv2.VideoCapture(video_source) try: while True: ret, frame = cap.read() if not ret: break # Анализ кадра result = self.analyze_interaction( video_frame=frame, context={"current_topic": "general_conversation"} ) # Отображение результатов на кадре if result["status"] == "success" and result["raw_analysis"]["face"]: for face in result["raw_analysis"]["face"]: x, y, w, h = face["bbox"] cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) # Отображение доминирующей эмоции emotion = face["dominant_emotion"] confidence = face["confidence"] cv2.putText(frame, f"{emotion} ({confidence:.2f})", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) # Отображение кадра cv2.imshow('Emotion Recognition', frame) # Вызов callback-функции, если предоставлена if callback: callback(result) # Выход по нажатию 'q' if cv2.waitKey(1) & 0xFF == ord('q'): break finally: cap.release() cv2.destroyAllWindows() # Пример использования if __name__ == "__main__": # Инициализация системы распознавания эмоций emotion_system = EmotionRecognitionSystem() # Пример анализа текста text_input = "Я очень рад, что мы наконец-то встретились! Это действительно замечательный день." text_result = emotion_system.text_analyzer.analyze_text(text_input) print("Анализ текста:") print(f"Доминирующая эмоция: {text_result['dominant_emotion']}") print(f"Вероятности: {text_result['emotion_probs']}") # Пример анализа взаимодействия (в реальной системе здесь будут реальные данные) context = { "current_topic": "good_news", "smiling": True, "smiling_intensity": 0.8 } # В реальной системе здесь будут реальные данные с камеры и микрофона video_frame = np.zeros((480, 640, 3), dtype=np.uint8) # Заглушка для кадра audio_path = "sample_audio.wav" # Заглушка для аудио interaction_result = emotion_system.analyze_interaction( video_frame=video_frame, audio_path=audio_path, text_input=text_input, context=context ) print("\nРезультаты анализа взаимодействия:") print(f"Объединенная доминирующая эмоция: {interaction_result['fused_analysis']['dominant_emotion']}") print(f"Интерпретированная эмоция: {interaction_result['interpreted_analysis']['interpreted_dominant_emotion']}") print(f"Пояснения: {interaction_result['interpreted_analysis']['explanations']}")
// src/services/emotionService.js // Сервис для взаимодействия с системой распознавания эмоций import axios from 'axios'; class EmotionService { constructor() { this.apiUrl = process.env.REACT_APP_EMOTION_API_URL || 'http://localhost:5000/api/v1/emotion'; this.headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${process.env.REACT_APP_EMOTION_API_KEY || ''}` }; } /** * Анализ эмоций по тексту */ async analyzeText(text) { try { const response = await axios.post(`${this.apiUrl}/text`, { text }, { headers: this.headers }); return { success: true, result: response.data }; } catch (error) { console.error('Text emotion analysis error:', error); return { success: false, error: error.response?.data?.error || 'Произошла ошибка при анализе текста' }; } } /** * Анализ эмоций по аудио */ async analyzeAudio(audioBlob) { try { const formData = new FormData(); formData.append('audio', audioBlob, 'recording.wav'); const response = await axios.post(`${this.apiUrl}/audio`, formData, { headers: { ...this.headers, 'Content-Type': 'multipart/form-data' } }); return { success: true, result: response.data }; } catch (error) { console.error('Audio emotion analysis error:', error); return { success: false, error: 'Не удалось проанализировать аудио' }; } } /** * Анализ эмоций по изображению лица */ async analyzeFace(imageBlob) { try { const formData = new FormData(); formData.append('image', imageBlob, 'face.jpg'); const response = await axios.post(`${this.apiUrl}/face`, formData, { headers: { ...this.headers, 'Content-Type': 'multipart/form-data' } }); return { success: true, result: response.data }; } catch (error) { console.error('Face emotion analysis error:', error); return { success: false, error: 'Не удалось проанализировать изображение лица' }; } } /** * Комплексный анализ эмоций (мультимодальный) */ async analyzeMultimodal(text, audioBlob, imageBlob, context = {}) { try { const formData = new FormData(); if (text) { formData.append('text', text); } if (audioBlob) { formData.append('audio', audioBlob, 'recording.wav'); } if (imageBlob) { formData.append('image', imageBlob, 'face.jpg'); } formData.append('context', JSON.stringify(context)); const response = await axios.post(`${this.apiUrl}/multimodal`, formData, { headers: { ...this.headers, 'Content-Type': 'multipart/form-data' } }); return { success: true, result: response.data }; } catch (error) { console.error('Multimodal emotion analysis error:', error); return { success: false, error: 'Не удалось выполнить комплексный анализ эмоций' }; } } /** * Получение рекомендаций на основе распознанных эмоций */ async getRecommendations(emotionData) { try { const response = await axios.post(`${this.apiUrl}/recommendations`, { emotionData }, { headers: this.headers }); return { success: true, recommendations: response.data.recommendations }; } catch (error) { console.error('Recommendations error:', error); return { success: false, error: 'Не удалось получить рекомендации' }; } } } export default new EmotionService();
// src/components/EmotionRecognition.jsx // Компонент системы распознавания эмоций import React, { useState, useEffect, useRef } from 'react'; import { Container, Row, Col, Card, Button, Form, Alert, Tabs, Tab, ProgressBar, Badge } from 'react-bootstrap'; import { FaCamera, FaMicrophone, FaComment, FaBrain, FaSync, FaInfoCircle } from 'react-icons/fa'; import { Line } from 'react-chartjs-2'; import 'chart.js/auto'; import EmotionService from '../services/emotionService'; const EmotionRecognition = () => { const [activeTab, setActiveTab] = useState('text'); const [textInput, setTextInput] = useState(''); const [isRecording, setIsRecording] = useState(false); const [audioBlob, setAudioBlob] = useState(null); const [faceImage, setFaceImage] = useState(null); const [emotionResult, setEmotionResult] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [recommendations, setRecommendations] = useState([]); const [userId] = useState(`user_${Math.random().toString(36).substr(2, 9)}`); const videoRef = useRef(null); const mediaRecorderRef = useRef(null); const chunksRef = useRef([]); // Запуск видеопотока для захвата лица useEffect(() => { const startVideo = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ video: true }); if (videoRef.current) { videoRef.current.srcObject = stream; } } catch (err) { console.error('Error accessing webcam:', err); setError('Не удалось получить доступ к камере. Пожалуйста, разрешите доступ к камере в настройках браузера.'); } }; startVideo(); return () => { if (videoRef.current && videoRef.current.srcObject) { videoRef.current.srcObject.getTracks().forEach(track => track.stop()); } }; }, []); // Обработка отправки текста const handleTextSubmit = async (e) => { e.preventDefault(); if (!textInput.trim() || isLoading) return; setIsLoading(true); setError(null); try { const response = await EmotionService.analyzeText(textInput); if (response.success) { setEmotionResult(response.result); fetchRecommendations(response.result); } else { setError(response.error); } } catch (err) { setError('Произошла ошибка при анализе текста. Пожалуйста, попробуйте еще раз.'); console.error('Text analysis error:', err); } finally { setIsLoading(false); } }; // Запуск записи аудио const startRecording = async () => { try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); mediaRecorderRef.current = new MediaRecorder(stream); chunksRef.current = []; mediaRecorderRef.current.ondataavailable = (e) => { if (e.data.size > 0) { chunksRef.current.push(e.data); } }; mediaRecorderRef.current.onstop = () => { const blob = new Blob(chunksRef.current, { type: 'audio/wav' }); setAudioBlob(blob); // Автоматически анализируем аудио analyzeAudio(blob); }; mediaRecorderRef.current.start(1000); setIsRecording(true); } catch (err) { setError('Не удалось получить доступ к микрофону. Пожалуйста, разрешите доступ к микрофону в настройках браузера.'); console.error('Microphone access error:', err); } }; // Остановка записи аудио const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); setIsRecording(false); // Останавливаем аудио-треки const tracks = mediaRecorderRef.current.stream.getTracks(); tracks.forEach(track => track.stop()); } }; // Анализ аудио const analyzeAudio = async (blob) => { setIsLoading(true); setError(null); try { const response = await EmotionService.analyzeAudio(blob); if (response.success) { setEmotionResult(response.result); fetchRecommendations(response.result); } else { setError(response.error); } } catch (err) { setError('Произошла ошибка при анализе аудио. Пожалуйста, попробуйте еще раз.'); console.error('Audio analysis error:', err); } finally { setIsLoading(false); } }; // Анализ лица const analyzeFace = async () => { if (!videoRef.current) return; setIsLoading(true); setError(null); try { // Создаем canvas для захвата изображения const canvas = document.createElement('canvas'); canvas.width = videoRef.current.videoWidth; canvas.height = videoRef.current.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height); // Конвертируем в Blob canvas.toBlob(async (blob) => { setFaceImage(URL.createObjectURL(blob)); // Анализируем изображение const response = await EmotionService.analyzeFace(blob); if (response.success) { setEmotionResult(response.result); fetchRecommendations(response.result); } else { setError(response.error); } setIsLoading(false); }, 'image/jpeg', 0.95); } catch (err) { setError('Произошла ошибка при анализе лица. Пожалуйста, попробуйте еще раз.'); console.error('Face analysis error:', err); setIsLoading(false); } }; // Комплексный анализ const analyzeMultimodal = async () => { setIsLoading(true); setError(null); try { // Захватываем изображение лица let faceBlob = null; if (videoRef.current) { const canvas = document.createElement('canvas'); canvas.width = videoRef.current.videoWidth; canvas.height = videoRef.current.videoHeight; const ctx = canvas.getContext('2d'); ctx.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height); canvas.toBlob(blob => faceBlob = blob, 'image/jpeg', 0.95); } // Контекст для анализа const context = { userId, currentTopic: "general_conversation", culturalBackground: "western" }; const response = await EmotionService.analyzeMultimodal( textInput, audioBlob, faceBlob, context ); if (response.success) { setEmotionResult(response.result); fetchRecommendations(response.result); } else { setError(response.error); } } catch (err) { setError('Произошла ошибка при комплексном анализе. Пожалуйста, попробуйте еще раз.'); console.error('Multimodal analysis error:', err); } finally { setIsLoading(false); } }; // Получение рекомендаций const fetchRecommendations = async (emotionData) => { try { const response = await EmotionService.getRecommendations(emotionData); if (response.success) { setRecommendations(response.recommendations); } } catch (err) { console.error('Failed to fetch recommendations:', err); } }; // Подготовка данных для графика const getChartData