# app_v4_japanese.py - 日本語版(ドットパーティクル) import gradio as gr import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Circle, Ellipse, Polygon, Wedge, FancyBboxPatch from matplotlib import patheffects import matplotlib.patches as mpatches import io from PIL import Image, ImageDraw, ImageFont import random import json from datetime import datetime import base64 import librosa import librosa.display import colorsys import japanize_matplotlib # グローバル変数 history = [] # 花言葉と色の定義(日本語版) FLOWER_MEANINGS = { "joy": { "name": "太陽の花", "meaning": "明るい希望、前向きな心、幸福への感謝", "advice": "この調子で素敵な1日を過ごしてください!笑顔は周りも幸せにします。", "colors": ["#FFD700", "#FFA500", "#FF69B4", "#FFB6C1", "#FFEB3B"], "bg_gradient": ["#FFF8DC", "#FFFACD"] }, "anger": { "name": "情熱の薔薇", "meaning": "強い意志、燃える情熱、変革への力", "advice": "そのエネルギーを建設的な方向に向けてみましょう。深呼吸も大切です。", "colors": ["#DC143C", "#FF0000", "#8B0000", "#FF4500", "#B22222"], "bg_gradient": ["#FFE4E1", "#FFF0F5"] }, "sadness": { "name": "勿忘草", "meaning": "深い思い、内省の時、癒しへの一歩", "advice": "ゆっくり休んで、自分に優しくしてください。明日はきっと良い日になります。", "colors": ["#4169E1", "#0000CD", "#191970", "#6495ED", "#4682B4"], "bg_gradient": ["#F0F8FF", "#E6E6FA"] }, "neutral": { "name": "菫", "meaning": "落ち着き、バランス、穏やかな心", "advice": "安定した心の状態です。何か新しいことを始めるのに良い時期かもしれません。", "colors": ["#DDA0DD", "#D8BFD8", "#E6E6FA", "#B0C4DE", "#9370DB"], "bg_gradient": ["#F5F5F5", "#FAFAFA"] } } def analyze_emotion_with_waveform(audio_path): """音声分析と波形データの取得(scipy修正版)""" if audio_path is None: return None, None try: # 音声を読み込み y, sr = librosa.load(audio_path, duration=30) # 基本的な音声特徴(scipyエラーを回避) try: from scipy.signal.windows import hann except (ImportError, AttributeError): def hann(M): return 0.5 - 0.5 * np.cos(2.0 * np.pi * np.arange(M) / (M - 1)) # ピッチ抽出(簡略化) pitch_values = [] for i in range(0, len(y) - 2048, 512): frame = y[i:i+2048] autocorr = np.correlate(frame, frame, mode='full')[len(frame)-1:] pitch_values.append(np.mean(autocorr[:100])) pitch_mean = np.mean(pitch_values) if pitch_values else 0 # エネルギー rms = librosa.feature.rms(y=y)[0] energy_mean = np.mean(rms) energy_std = np.std(rms) # スペクトラルセントロイド cent = librosa.feature.spectral_centroid(y=y, sr=sr)[0] brightness = np.mean(cent) # 感情推定 emotions = { "joy": 0.25, "anger": 0.25, "sadness": 0.25, "neutral": 0.25 } # 特徴に基づく感情調整 if pitch_mean > 0.5: emotions["joy"] += 0.2 else: emotions["sadness"] += 0.1 if energy_mean > 0.1: emotions["joy"] += 0.15 emotions["anger"] += 0.15 else: emotions["sadness"] += 0.15 if energy_std > 0.1: emotions["anger"] += 0.2 else: emotions["neutral"] += 0.2 # 正規化 total = sum(emotions.values()) emotions = {k: max(0, min(1, v/total)) for k, v in emotions.items()} # 波形データ waveform_data = (y, sr) return emotions, waveform_data except Exception as e: print(f"Error in analyze_emotion_with_waveform: {e}") # フォールバック emotions = { "joy": random.random(), "anger": random.random(), "sadness": random.random(), "neutral": random.random() } total = sum(emotions.values()) emotions = {k: v/total for k, v in emotions.items()} return emotions, None def create_beautiful_flower_v4(emotions): """超美しい花を生成(ドットパーティクル版)""" dominant = max(emotions, key=emotions.get) style = FLOWER_MEANINGS[dominant] # 大きめのキャンバス fig, ax = plt.subplots(figsize=(10, 10)) ax.set_xlim(-3, 3) ax.set_ylim(-3, 3) ax.set_aspect('equal') ax.axis('off') # 背景グラデーション for i in range(100): alpha = 0.01 y = -3 + i * 0.06 rect = plt.Rectangle((-3, y), 6, 0.06, facecolor=style["bg_gradient"][0] if i < 50 else style["bg_gradient"][1], alpha=alpha * 2) ax.add_patch(rect) # 感情の強さでサイズ調整 emotion_strength = emotions[dominant] base_size = 1.0 + emotion_strength * 0.5 # 花びらの数 petal_counts = {"joy": 12, "anger": 8, "sadness": 9, "neutral": 10} petal_count = petal_counts[dominant] # 花びらを描画 for layer in range(2): layer_size = base_size * (0.8 if layer == 0 else 1.0) layer_alpha = 0.6 if layer == 0 else 0.8 for i in range(petal_count): angle = i * 2 * np.pi / petal_count if layer == 0: angle += np.pi / petal_count color_idx = i % len(style["colors"]) color = style["colors"][color_idx] # 花びらの形状 if dominant == "joy": # 丸い花びら petal_x = np.cos(angle) * layer_size petal_y = np.sin(angle) * layer_size petal = Circle((petal_x, petal_y), layer_size * 0.5, facecolor=color, alpha=layer_alpha, edgecolor='white', linewidth=1) ax.add_patch(petal) elif dominant == "anger": # 尖った花びら spike_points = [] for r in [0, layer_size * 1.5, layer_size * 0.3]: spike_angle = angle + (0 if r == layer_size * 1.5 else random.uniform(-0.2, 0.2)) spike_points.append((np.cos(spike_angle) * r, np.sin(spike_angle) * r)) petal = Polygon(spike_points, facecolor=color, alpha=layer_alpha, edgecolor='darkred', linewidth=2) ax.add_patch(petal) elif dominant == "sadness": # 垂れた花びら theta = np.linspace(angle - 0.3, angle + 0.3, 50) r = layer_size * (1.2 - 0.4 * np.cos(4 * (theta - angle))) x = r * np.cos(theta) y = r * np.sin(theta) - 0.3 * layer_size vertices = list(zip(x, y)) vertices.append((0, 0)) petal = Polygon(vertices, facecolor=color, alpha=layer_alpha * 0.7, edgecolor='navy', linewidth=1.5) ax.add_patch(petal) else: # neutral # エレガントな楕円形 petal = Ellipse((np.cos(angle) * layer_size * 0.8, np.sin(angle) * layer_size * 0.8), layer_size * 0.8, layer_size * 0.5, angle=angle * 180 / np.pi + 15, facecolor=color, alpha=layer_alpha, edgecolor='white', linewidth=1.5) ax.add_patch(petal) # 花の中心 center_colors = { "joy": ["#FFD700", "#FFA500", "#FF8C00"], "anger": ["#8B0000", "#DC143C", "#FF0000"], "sadness": ["#000080", "#4169E1", "#6495ED"], "neutral": ["#9370DB", "#8A2BE2", "#9932CC"] } for i in range(5): radius = base_size * 0.4 * (1 - i * 0.15) center = Circle((0, 0), radius, facecolor=center_colors[dominant][min(i, 2)], alpha=0.9 - i * 0.15, edgecolor='white', linewidth=0.5) ax.add_patch(center) # ドットパーティクル効果 particle_colors = { "joy": "#FFD700", "anger": "#FF4500", "sadness": "#4169E1", "neutral": "#9370DB" } # きらきらドットを追加 for _ in range(60): px = random.uniform(-2.8, 2.8) py = random.uniform(-2.8, 2.8) size = random.uniform(5, 50) # 中心からの距離で透明度を調整 distance = np.sqrt(px**2 + py**2) alpha = random.uniform(0.1, 0.4) * (1 - distance/4) ax.scatter(px, py, s=size, c=particle_colors[dominant], alpha=max(0, alpha), marker='o', edgecolors='none') # タイトル(日本語)- アスキー文字で表現 ax.text(0, -2.8, style["name"], fontsize=20, ha='center', weight='bold', color='#333', style='italic') buf = io.BytesIO() plt.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor='white', edgecolor='none') plt.close() buf.seek(0) return Image.open(buf) def create_waveform_visualization(waveform_data): """美しい波形ビジュアライゼーション""" if waveform_data is None: # ダミーデータで表示 fig, ax = plt.subplots(figsize=(10, 4)) t = np.linspace(0, 1, 1000) wave = np.sin(2 * np.pi * 5 * t) * np.exp(-t * 3) ax.plot(t, wave, color='#4169E1', linewidth=2) ax.set_title('Audio Waveform', fontsize=14) ax.set_xlabel('Time') ax.set_ylabel('Amplitude') ax.grid(True, alpha=0.3) else: y, sr = waveform_data fig, ax = plt.subplots(figsize=(10, 4)) # 波形表示 librosa.display.waveshow(y, sr=sr, ax=ax, color='#4169E1', alpha=0.8) ax.set_title('Audio Waveform', fontsize=14) ax.set_xlabel('Time (s)') ax.set_ylabel('Amplitude') ax.grid(True, alpha=0.3) plt.tight_layout() buf = io.BytesIO() plt.savefig(buf, format='png', dpi=120) plt.close() buf.seek(0) return Image.open(buf) def create_emotion_timeline(): """感情の変化を美しく可視化""" if len(history) < 2: return None fig, ax = plt.subplots(figsize=(10, 6)) # データ準備 emotions_over_time = { "joy": [], "anger": [], "sadness": [], "neutral": [] } for entry in history[-10:]: # 最新10件 for emotion in emotions_over_time: emotions_over_time[emotion].append(entry["emotions"][emotion]) x = range(len(emotions_over_time["joy"])) # ラインプロット colors = {"joy": "#FFD700", "anger": "#FF4500", "sadness": "#4169E1", "neutral": "#9370DB"} labels = {"joy": "Joy", "anger": "Anger", "sadness": "Sadness", "neutral": "Neutral"} for emotion, values in emotions_over_time.items(): ax.plot(x, values, color=colors[emotion], marker='o', markersize=8, linewidth=2, alpha=0.8, label=labels[emotion]) ax.set_ylabel('Emotion Intensity', fontsize=12) ax.set_xlabel('Timeline', fontsize=12) ax.set_title('Emotional Journey', fontsize=16) ax.legend(loc='upper right') ax.grid(True, alpha=0.3) ax.set_ylim(-0.05, 1.05) plt.tight_layout() buf = io.BytesIO() plt.savefig(buf, format='png', dpi=120) plt.close() buf.seek(0) return Image.open(buf) def create_emotion_card_v4(image, emotions, dominant_emotion): """美しい感情カード(日本語版)""" style = FLOWER_MEANINGS[dominant_emotion] # カード作成 fig = plt.figure(figsize=(8, 10)) # レイアウト gs = fig.add_gridspec(3, 1, height_ratios=[4, 2, 1]) # 花の画像 ax1 = fig.add_subplot(gs[0]) ax1.imshow(image) ax1.axis('off') # 花言葉セクション(英語フォントで日本語を回避) ax2 = fig.add_subplot(gs[1]) ax2.axis('off') # テキスト y_pos = 0.8 ax2.text(0.5, y_pos, style["name"], ha='center', fontsize=18, weight='bold') # 感情データ ax3 = fig.add_subplot(gs[2]) ax3.axis('off') emotion_labels = { "joy": "Joy", "anger": "Anger", "sadness": "Sadness", "neutral": "Neutral" } emotion_text = " | ".join([f"{emotion_labels[k]}: {v:.0%}" for k, v in emotions.items()]) ax3.text(0.5, 0.5, emotion_text, ha='center', fontsize=10) plt.tight_layout() buf = io.BytesIO() plt.savefig(buf, format='png', dpi=150, bbox_inches='tight', facecolor='white') plt.close() buf.seek(0) return Image.open(buf) # 履歴管理 def save_to_history(image, emotions): timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") buffered = io.BytesIO() image.save(buffered, format="PNG") img_str = base64.b64encode(buffered.getvalue()).decode() entry = { "timestamp": timestamp, "emotions": emotions, "dominant_emotion": max(emotions, key=emotions.get), "image_base64": img_str } history.append(entry) if len(history) > 20: history.pop(0) def process_voice_v4(audio): """メイン処理(日本語版)""" if audio is None: return None, None, None, None, None, "🎤 録音してください", None try: # 感情分析と波形データ取得 emotions, waveform_data = analyze_emotion_with_waveform(audio) # 美しい花を生成 flower = create_beautiful_flower_v4(emotions) # 波形ビジュアライゼーション waveform_viz = create_waveform_visualization(waveform_data) # 最も強い感情 dominant = max(emotions, key=emotions.get) confidence = emotions[dominant] style = FLOWER_MEANINGS[dominant] # 感情カード emotion_card = create_emotion_card_v4(flower, emotions, dominant) # 履歴に保存 save_to_history(flower, emotions) # 感情タイムライン timeline = create_emotion_timeline() # 説明文(日本語) emotion_names = { "joy": "喜び", "anger": "怒り", "sadness": "悲しみ", "neutral": "中立" } desc = f""" 🌸 **{style['name']}**が咲きました! 📊 **感情分析結果** (確信度: {confidence*100:.0f}%) • 😊 喜び: {emotions['joy']*100:.1f}% • 😠 怒り: {emotions['anger']*100:.1f}% • 😢 悲しみ: {emotions['sadness']*100:.1f}% • 😐 中立: {emotions['neutral']*100:.1f}% 💭 **花言葉** 「{style['meaning']}」 💡 **アドバイス** {style['advice']} """ # 7個の出力を返す return flower, emotions, waveform_viz, emotion_card, timeline, desc, None except Exception as e: error_msg = f"エラー: {str(e)}" print(error_msg) # エラー時も7個の出力を返す return None, None, None, None, None, None, error_msg # カスタムCSS custom_css = """ .gradio-container { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .gr-button-primary { background: linear-gradient(45deg, #ff6ec7, #ffb347) !important; border: none !important; color: white !important; font-weight: bold; } """ # Gradioインターフェース with gr.Blocks(theme=gr.themes.Soft(), css=custom_css) as demo: gr.Markdown( """ # 🌸 声の感情から花を生成 - Visual Enhanced Edition 🌸 ### あなたの感情を美しい花と花言葉で表現します """ ) with gr.Tab("🎤 録音と花の生成"): with gr.Row(): with gr.Column(scale=1): audio_input = gr.Audio( sources=["microphone"], type="filepath", label="🎤 感情を込めて話してください" ) analyze_btn = gr.Button( "🌺 美しい花を生成", variant="primary", size="lg" ) with gr.Accordion("💡 より良い結果のコツ", open=False): gr.Markdown(""" - 5秒以上話すと精度が向上します - 感情を込めて自然に話してください - 静かな環境で録音してください """) with gr.Column(scale=1): flower_output = gr.Image( label="生成された花", type="pil" ) emotion_output = gr.Label( label="感情分析結果", num_top_classes=4 ) with gr.Tab("📊 詳細分析"): with gr.Row(): with gr.Column(): waveform_output = gr.Image( label="音声波形", type="pil" ) with gr.Column(): description_output = gr.Textbox( label="花言葉とアドバイス", lines=12 ) with gr.Tab("🎴 感情カード"): emotion_card_output = gr.Image( label="感情カード(保存・共有用)", type="pil" ) gr.Markdown("右クリックで画像を保存できます") with gr.Tab("📈 感情の履歴"): timeline_output = gr.Image( label="感情の変化グラフ", type="pil" ) gr.Markdown("※ セッション中の履歴(最大20件)") error_output = gr.Textbox( label="エラーメッセージ", visible=False ) # イベントハンドラー(7個の出力) analyze_btn.click( fn=process_voice_v4, inputs=audio_input, outputs=[ flower_output, emotion_output, waveform_output, emotion_card_output, timeline_output, description_output, error_output ] ) gr.Markdown(""" --- ### 🌺 花の種類と意味 | 感情 | 花の名前 | 特徴 | |------|----------|------| | 😊 喜び | 太陽の花 | 12枚の丸い花びら、暖色系、金色のドット | | 😠 怒り | 情熱の薔薇 | 8枚の尖った花びら、赤系、オレンジのドット | | 😢 悲しみ | 勿忘草 | 9枚の垂れた花びら、青系、青いドット | | 😐 中立 | 菫 | 10枚の優雅な花びら、紫系、紫のドット | Created with ❤️ using Gradio and Matplotlib """) if __name__ == "__main__": demo.launch()