Files
Depression_Detection_APP/pages/3_hasil.py
Achillean-1 5d2b8bb14d first commit
2025-07-11 15:28:18 +07:00

340 lines
14 KiB
Python

#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("""
<style>
.main-header {
color: white;
background-color: #1E1E5A;
padding: 1.5rem;
text-align: center;
border-radius: 10px 10px 0 0;
margin-bottom: 2rem;
font-size: 2rem;
font-weight: bold;
}
.section-header {
color: #FFFFFF;
font-weight: bold;
margin-bottom: 20px;
border-bottom: 2px solid #3498db;
padding-bottom: 10px;
}
.positive-score {
color: #2ecc71;
font-weight: bold;
}
.negative-score {
color: #e74c3c;
font-weight: bold;
}
.stButton > button {
background-color: #007bff;
color: white;
padding: 10px 24px;
border-radius: 8px;
font-size: 1rem;
border: none;
cursor: pointer;
transition: 0.3s;
display: block;
margin: 0 auto;
width: 100%;
}
.stButton > button:hover {
background-color: #0056b3;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.custom-button-container {
margin-top: 20px;
margin-bottom: 20px;
}
.divider {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
border-top: 1px solid #eee;
}
</style>
""", unsafe_allow_html=True)
st.markdown('<div class="main-header">📊 Laporan Analisis Multimodal</div>', 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('<div class="custom-button-container">', 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('</div>', 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('<div class="custom-button-container">', 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('</div>', 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('<h2 class="section-header">📝 Ringkasan Umum</h2>', 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: <span class='positive-score'>{avg_positive_score:.1f}%</span>", unsafe_allow_html=True)
st.write(f"🔴 Rata-rata Emosi Negatif: <span class='negative-score'>{avg_negative_score:.1f}%</span>", 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('</div>', unsafe_allow_html=True)
st.markdown('<h2 class="section-header">😀 Analisis Ekspresi Wajah</h2>', 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: <span class='positive-score'>{face_positive_score:.1f}%</span>", unsafe_allow_html=True)
st.write(f"🔴 Skor Negatif: <span class='negative-score'>{face_negative_score:.1f}%</span>", 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('</div>', unsafe_allow_html=True)
st.markdown('<h2 class="section-header">📝 Analisis Teks Jurnaling</h2>', 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: <span class='positive-score'>{text_positive_score:.1f}%</span>", unsafe_allow_html=True)
st.write(f"🔴 Skor Negatif: <span class='negative-score'>{text_negative_score:.1f}%</span>", unsafe_allow_html=True)
st.write("### Teks Jurnal")
st.write(text_result['text'])
st.markdown('</div>', unsafe_allow_html=True)
st.markdown('<h2 class="section-header">🔍 Kesimpulan Akhir</h2>', 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('<div class="custom-button-container">', 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('</div>', unsafe_allow_html=True)