# app.py import streamlit as st from streamlit_webrtc import webrtc_streamer import cv2 import numpy as np from tensorflow.keras.models import load_model from tensorflow.keras.preprocessing.image import img_to_array from tensorflow.keras.utils import to_categorical from tensorflow.keras.optimizers import Adam from tensorflow.keras.callbacks import Callback from av import VideoFrame import os import importlib.util # Keep this for dynamic loading from sklearn.metrics import classification_report, confusion_matrix import matplotlib.pyplot as plt import seaborn as sns import pandas as pd # --- PATH ADJUSTMENTS START HERE --- # Get the directory of the current script (app.py) current_dir = os.path.dirname(__file__) # Dynamically load preprocess.py from utils folder # It's now located at 'app/utils/preprocess.py' relative to the root (where app.py is) preprocess_path = os.path.abspath(os.path.join(current_dir, "app", "utils", "preprocess.py")) spec = importlib.util.spec_from_file_location("preprocess", preprocess_path) preprocess = importlib.util.module_from_spec(spec) try: spec.loader.exec_module(preprocess) except FileNotFoundError as e: st.error(f"Error loading preprocess.py: {e}. Please ensure it's in the 'app/utils/' directory.") st.stop() # Constants EMOTIONS = ["Angry", "Disgust", "Fear", "Happy", "Sad", "Surprise", "Neutral"] IMG_SIZE = (48, 48) # Adjust model path: It's now at 'models/emotion_model.h5' relative to the root MODEL_PATH = os.path.abspath(os.path.join(current_dir, "models", "emotion_model.h5")) # Adjust data folder paths: They are now at 'data/train' and 'data/test' relative to the root TRAIN_FOLDER = os.path.abspath(os.path.join(current_dir, "data", "train")) TEST_FOLDER = os.path.abspath(os.path.join(current_dir, "data", "test")) # --- PATH ADJUSTMENTS END HERE --- # Load or build model try: model = load_model(MODEL_PATH) except Exception as e: st.error(f"Error loading model: {e}. Ensure the model file exists at: {MODEL_PATH}") st.stop() st.set_page_config(page_title="Facial Emotion Recognition", layout="wide") st.title("🎭 Facial Emotion Recognition") # Sidebar for navigation page = st.sidebar.selectbox("Select Page", ["Real-time Recognition", "Train Model", "Model Evaluation"]) # Make sure haarcascades are accessible. For Hugging Face, it's often better to download them or include in repo # If you run into issues, you might need to download this file and place it in your repo # For now, let's assume cv2.data.haarcascades works or that it's present in the environment. face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml") # Frame processing for real-time recognition def process_frame(frame): img = frame.to_ndarray(format="bgr24") gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5) for (x, y, w, h) in faces: roi_gray = gray[y:y+h, x:x+w] roi_gray = cv2.resize(roi_gray, IMG_SIZE) roi = roi_gray.astype("float") / 255.0 roi = img_to_array(roi) roi = np.expand_dims(roi, axis=0) preds = model.predict(roi, verbose=0)[0] label = EMOTIONS[np.argmax(preds)] cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.putText(img, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) return VideoFrame.from_ndarray(img, format="bgr24") # Pages if page == "Real-time Recognition": st.header("🎥 Real-Time Facial Emotion Recognition") st.write("Use your webcam to detect facial emotions live.") webrtc_streamer(key="emotion-recognition", video_frame_callback=process_frame) elif page == "Train Model": st.header("🚀 Train the Emotion Recognition Model") epochs = st.slider("Number of Epochs", 1, 50, 5) batch_size = st.selectbox("Batch Size", options=[8, 16, 32, 64, 128], index=2) class StreamlitCallback(Callback): def on_epoch_end(self, epoch, logs=None): st.text(f"Epoch {epoch+1} - Loss: {logs['loss']:.4f} - Accuracy: {logs['accuracy']:.4f}") if st.button("Start Training"): with st.spinner("Training the model... This might take a while ⏳"): try: x_train, y_train, _ = preprocess.load_images_from_folder(TRAIN_FOLDER, IMG_SIZE) y_train_cat = to_categorical(y_train, num_classes=len(EMOTIONS)) model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy']) history = model.fit( x_train, y_train_cat, epochs=epochs, batch_size=batch_size, callbacks=[StreamlitCallback()], verbose=0 ) model.save(MODEL_PATH) st.success("Training complete! Model saved.") # Show training loss and accuracy plots st.subheader("Training Loss and Accuracy") fig, ax = plt.subplots(1, 2, figsize=(12, 4)) ax[0].plot(history.history['loss'], label='Loss') ax[0].set_title('Loss') ax[0].set_xlabel('Epoch') ax[0].set_ylabel('Loss') ax[0].legend() ax[1].plot(history.history['accuracy'], label='Accuracy', color='green') ax[1].set_title('Accuracy') ax[1].set_xlabel('Epoch') ax[1].set_ylabel('Accuracy') ax[1].legend() st.pyplot(fig) except Exception as e: st.error(f"Error during training: {e}") elif page == "Model Evaluation": st.header("📊 Evaluate the Model") @st.cache_data def evaluate_model(_model, test_folder): x_test, y_test, emotions = preprocess.load_images_from_folder(test_folder, IMG_SIZE) x_test = x_test.astype("float32") / 255.0 y_pred_probs = _model.predict(x_test, verbose=0) y_pred = y_pred_probs.argmax(axis=1) report = classification_report(y_test, y_pred, target_names=emotions, output_dict=True) cm = confusion_matrix(y_test, y_pred) return report, cm, emotions if st.button("Evaluate Model on Test Dataset"): with st.spinner("Evaluating..."): try: report, cm, emotions = evaluate_model(model, TEST_FOLDER) st.subheader("Classification Report") data = [ [emotion, report[emotion]['precision'], report[emotion]['recall'], report[emotion]['f1-score'], report[emotion]['support']] for emotion in emotions ] data.append(['accuracy', report['accuracy'], None, None, None]) df_report = pd.DataFrame(data, columns=["Emotion", "Precision", "Recall", "F1-Score", "Support"]) st.dataframe(df_report.style.format({"Precision": "{:.2f}", "Recall": "{:.2f}", "F1-Score": "{:.2f}"})) st.subheader("Confusion Matrix") fig, ax = plt.subplots(figsize=(8, 6)) sns.heatmap(cm, annot=True, fmt="d", xticklabels=emotions, yticklabels=emotions, cmap="Blues", ax=ax) ax.set_xlabel("Predicted") ax.set_ylabel("True") ax.set_ylabel("True") st.pyplot(fig) except Exception as e: st.error(f"Error during evaluation: {e}") # Footer st.markdown("---") st.markdown("© 2025 Facial Emotion Recognition App. Developed by jephone.")