#3_hasil.py import streamlit as st import matplotlib.pyplot as plt import numpy as np from collections import Counter CATEGORIES = ['Angry', 'Sad', 'Happy', 'Fearful', 'Disgust', 'Neutral', 'Surprised'] st.set_page_config(page_title="Analisis Multimodal", layout="wide") st.markdown(""" """, unsafe_allow_html=True) st.markdown('
📊 Laporan Analisis Multimodal
', unsafe_allow_html=True) emotion_mapping = { 'Happy': 'bahagia', 'Surprised': 'terkejut', 'Neutral': 'netral', 'Angry': 'marah', 'Sad': 'sedih', 'Fearful': 'takut', 'Disgust': 'jijik', 'bahagia': 'bahagia', 'terkejut': 'terkejut', 'netral': 'netral', 'marah': 'marah', 'sedih': 'sedih', 'takut': 'takut', 'jijik': 'jijik' } positive_emotions = ['bahagia', 'terkejut', 'netral'] negative_emotions = ['marah', 'sedih', 'takut', 'jijik'] if 'data_manager' not in st.session_state or st.session_state.data_manager is None: st.error("Data emosi wajah tidak tersedia. Silakan lakukan analisis wajah terlebih dahulu.") st.markdown('
', unsafe_allow_html=True) if st.button("Lakukan Analisis Wajah"): try: st.switch_page("pages/1_analisis_wajah.py") except FileNotFoundError: st.error("Halaman analisis wajah tidak ditemukan.") st.markdown('
', unsafe_allow_html=True) st.stop() if 'text_analysis_result' not in st.session_state or not st.session_state.text_analysis_result: st.error("Data analisis teks tidak tersedia. Silakan lakukan analisis teks terlebih dahulu.") st.markdown('
', unsafe_allow_html=True) if st.button("Lakukan Analisis Teks"): try: st.switch_page("pages/2_analisis_teks.py") except FileNotFoundError: st.error("Halaman analisis teks tidak ditemukan.") st.markdown('
', unsafe_allow_html=True) st.stop() data_manager = st.session_state.data_manager if not data_manager.predictions or not data_manager.timestamps: st.error("Tidak ada data emosi wajah yang valid untuk analisis.") st.stop() face_emotion_data = [(timestamp, CATEGORIES[pred_idx]) for pred_idx, timestamp in zip(data_manager.predictions, data_manager.timestamps)] for timestamp, emotion in face_emotion_data: if not isinstance(timestamp, (int, float)) or not isinstance(emotion, str): st.error("Format data emosi wajah tidak valid.") st.stop() if emotion not in emotion_mapping: st.warning(f"Emosi tidak dikenal: {emotion}. Melewati emosi ini.") face_emotion_data = [(t, e) for t, e in face_emotion_data if e in emotion_mapping] if not face_emotion_data: st.error("Tidak ada data emosi wajah yang valid.") st.stop() text_result = st.session_state.text_analysis_result if not isinstance(text_result, dict) or 'text' not in text_result: st.error("Format data analisis teks tidak valid.") st.stop() # Process face emotions dominant_face_emotions = {} for _, emotion in face_emotion_data: standardized_emotion = emotion_mapping.get(emotion) if standardized_emotion: dominant_face_emotions[standardized_emotion] = dominant_face_emotions.get(standardized_emotion, 0) + 1 total_face_frames = len(face_emotion_data) if total_face_frames == 0: st.error("Tidak ada data emosi wajah yang valid untuk analisis.") st.stop() face_emotion_percentages = {emotion: (count / total_face_frames) * 100 for emotion, count in dominant_face_emotions.items()} top_face_emotions = sorted(face_emotion_percentages.items(), key=lambda x: x[1], reverse=True)[:3] text_emotions = {} if "emotions" in text_result: text_emotions = {emotion_mapping.get(emotion, emotion): score * 100 for emotion, score in text_result["emotions"].items() if emotion_mapping.get(emotion, emotion) in emotion_mapping.values()} elif "top_emotions" in text_result: text_emotions = {emotion_mapping.get(emotion, emotion): score * 100 for emotion, score in text_result["top_emotions"] if emotion_mapping.get(emotion, emotion) in emotion_mapping.values()} top_text_emotions = sorted(text_emotions.items(), key=lambda x: x[1], reverse=True)[:3] combined_emotions = {} for emotion, percentage in face_emotion_percentages.items(): combined_emotions[emotion] = combined_emotions.get(emotion, 0) + percentage standardized_text_emotions = {emotion_mapping.get(e, e): score for e, score in text_emotions.items()} for emotion, percentage in standardized_text_emotions.items(): if emotion in emotion_mapping.values(): combined_emotions[emotion] = combined_emotions.get(emotion, 0) + percentage for emotion in combined_emotions: appeared_in_face = emotion in face_emotion_percentages appeared_in_text = emotion in standardized_text_emotions divisor = 1 + (1 if appeared_in_face and appeared_in_text else 0) combined_emotions[emotion] /= divisor total_combined = sum(combined_emotions.values()) if total_combined > 0: combined_emotions = {emotion: (score / total_combined) * 100 for emotion, score in combined_emotions.items()} top_combined_emotions = sorted(combined_emotions.items(), key=lambda x: x[1], reverse=True)[:3] face_positive_score = sum(face_emotion_percentages.get(emotion, 0) for emotion in positive_emotions) face_negative_score = sum(face_emotion_percentages.get(emotion, 0) for emotion in negative_emotions) total_face_score = face_positive_score + face_negative_score if total_face_score > 0: face_positive_score = (face_positive_score / total_face_score) * 100 face_negative_score = (face_negative_score / total_face_score) * 100 else: face_positive_score = 0 face_negative_score = 0 st.warning("Tidak ada data emosi wajah yang valid untuk menghitung skor positif/negatif.") if "positive_score" in text_result and "negative_score" in text_result: text_positive_score = text_result['positive_score'] * 100 text_negative_score = text_result['negative_score'] * 100 else: text_positive_score = sum(text_emotions.get(emotion, 0) for emotion in positive_emotions) text_negative_score = sum(text_emotions.get(emotion, 0) for emotion in negative_emotions) total_text_score = text_positive_score + text_negative_score if total_text_score > 0: text_positive_score = (text_positive_score / total_text_score) * 100 text_negative_score = (text_negative_score / total_text_score) * 100 else: text_positive_score = 0 text_negative_score = 0 st.warning("Tidak ada data emosi teks yang valid untuk menghitung skor positif/negatif.") avg_positive_score = (face_positive_score + text_positive_score) / 2 avg_negative_score = (face_negative_score + text_negative_score) / 2 emotion_changes = {} for i in range(1, len(face_emotion_data)): if face_emotion_data[i][1] != face_emotion_data[i-1][1]: key = f"{face_emotion_data[i-1][1]} → {face_emotion_data[i][1]}" time_diff = face_emotion_data[i][0] - face_emotion_data[i-1][0] emotion_changes[key] = time_diff st.markdown('

📝 Ringkasan Umum

', unsafe_allow_html=True) st.write(f"**Durasi Analisis:** {face_emotion_data[-1][0]:.2f} detik") st.write(f"**Jumlah Perubahan Emosi:** {len(emotion_changes)}") st.markdown("### Skor Rata-rata Emosi (Wajah & Teks)") st.write(f"🟢 Rata-rata Emosi Positif: {avg_positive_score:.1f}%", unsafe_allow_html=True) st.write(f"🔴 Rata-rata Emosi Negatif: {avg_negative_score:.1f}%", unsafe_allow_html=True) if avg_positive_score > 0 or avg_negative_score > 0: fig, ax = plt.subplots(figsize=(10, 3)) labels = ['Positif', 'Negatif'] values = [avg_positive_score, avg_negative_score] colors = ['#2ecc71', '#e74c3c'] bars = ax.barh(labels, values, color=colors, height=0.5) for bar in bars: width = bar.get_width() ax.text(width + 1, bar.get_y() + bar.get_height()/2, f'{width:.1f}%', va='center', fontweight='bold') ax.set_xlim(0, 100) ax.set_xlabel('Persentase (%)') ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['bottom'].set_color('#DDDDDD') ax.spines['left'].set_color('#DDDDDD') ax.tick_params(bottom=False, left=False) ax.set_axisbelow(True) ax.grid(axis='x', linestyle='-', alpha=0.2) st.pyplot(fig) else: st.warning("Tidak cukup data untuk menampilkan grafik skor positif/negatif.") st.write("### Top 3 Emosi Gabungan (Wajah & Teks)") for emotion, score in top_combined_emotions: st.write(f"- {emotion.capitalize()}: {score:.1f}%") st.markdown('', unsafe_allow_html=True) st.markdown('

😀 Analisis Ekspresi Wajah

', unsafe_allow_html=True) st.write("### Top 3 Emosi Wajah") for emotion, percentage in top_face_emotions: count = dominant_face_emotions[emotion] st.write(f"- {emotion.capitalize()}: {percentage:.1f}% ({count} kali)") st.write(f"### Skor Emosi Wajah") st.write(f"🟢 Skor Positif: {face_positive_score:.1f}%", unsafe_allow_html=True) st.write(f"🔴 Skor Negatif: {face_negative_score:.1f}%", unsafe_allow_html=True) if len(face_emotion_data) > 1: fig, ax = plt.subplots(figsize=(12, 6)) fig.patch.set_facecolor('#f4f4f7') ax.set_facecolor('#f4f4f7') timestamps = [data[0] for data in face_emotion_data] emotions = [data[1] for data in face_emotion_data] ax.scatter(timestamps, emotions, c='#3a7aff', alpha=0.7, s=30) ax.plot(timestamps, emotions, color='#3a7aff', alpha=0.5, linewidth=1) ax.set_xlabel("Waktu (detik)", color='#111c4e', fontsize=12) ax.set_ylabel("Ekspresi", color='#111c4e', fontsize=12) ax.set_title("Timeline Perubahan Ekspresi", color='#111c4e', fontsize=14, fontweight='bold') ax.tick_params(axis='both', colors='#111c4e', labelsize=10) for spine in ax.spines.values(): spine.set_color('#111c4e') ax.grid(True, alpha=0.3) plt.tight_layout() st.pyplot(fig) plt.close(fig) if emotion_changes: st.write("### Perubahan Emosi Tercepat") for change, time in sorted(emotion_changes.items(), key=lambda x: x[1])[:3]: st.write(f"- {change} dalam {time:.2f} detik") st.markdown('', unsafe_allow_html=True) st.markdown('

📝 Analisis Teks Jurnaling

', unsafe_allow_html=True) st.write("### Top 3 Emosi Terdeteksi") for emotion, percentage in top_text_emotions: st.write(f"- {emotion.capitalize()}: {percentage:.1f}%") st.write(f"### Skor Emosi") st.write(f"🟢 Skor Positif: {text_positive_score:.1f}%", unsafe_allow_html=True) st.write(f"🔴 Skor Negatif: {text_negative_score:.1f}%", unsafe_allow_html=True) st.write("### Teks Jurnal") st.write(text_result['text']) st.markdown('', unsafe_allow_html=True) st.markdown('

🔍 Kesimpulan Akhir

', unsafe_allow_html=True) st.markdown("### Fusion dengan Majority Voting") st.write("Metode ini menggabungkan keputusan dari setiap modalitas dengan menerapkan prinsip voting mayoritas.") face_classification = 1 if face_negative_score >= face_positive_score else 0 text_classification = 1 if text_negative_score >= text_positive_score else 0 fused_score = np.mean([face_classification, text_classification]) if avg_negative_score >= 50: st.error("⚠️ Analisis menunjukkan potensi indikasi depresi.") st.write("Disarankan untuk berbicara dengan konselor atau psikolog.") elif avg_negative_score < 49: st.success("✅ Analisis menunjukkan kondisi emosi stabil.") st.write("Tetap jaga kesehatan mental dan lanjutkan kegiatan positif.") else: st.warning("⚖️ Analisis menunjukkan kondisi emosi netral (skor negatif 49%).") st.write("Pertimbangkan konsultasi jika merasa perlu.") st.write(f"- Klasifikasi dari analisis wajah: {'Indikasi Negatif' if face_classification == 1 else 'Indikasi Positif'}") st.write(f"- Klasifikasi dari analisis teks: {'Indikasi Negatif' if text_classification == 1 else 'Indikasi Positif'}") st.markdown('
', unsafe_allow_html=True) if st.button("Ulangi Analisis"): st.session_state.pop('data_manager', None) st.session_state.pop('text_analysis_result', None) try: st.switch_page("pages/1_analisis_wajah.py") except FileNotFoundError: st.error("Halaman analisis wajah tidak ditemukan.") st.markdown('
', unsafe_allow_html=True)