Spaces:
Running
Running
Added
Browse files- app.py +1865 -0
- haarcascade_frontalface_default.xml +0 -0
- requirements.txt +31 -0
app.py
ADDED
@@ -0,0 +1,1865 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import cv2
|
3 |
+
from deepface import DeepFace
|
4 |
+
import numpy as np
|
5 |
+
from retinaface import RetinaFace
|
6 |
+
from PIL import Image
|
7 |
+
import random
|
8 |
+
import time
|
9 |
+
import json
|
10 |
+
import os
|
11 |
+
from functools import lru_cache
|
12 |
+
import pandas as pd
|
13 |
+
import plotly.express as px
|
14 |
+
import threading
|
15 |
+
import concurrent.futures
|
16 |
+
|
17 |
+
import tensorflow as tf
|
18 |
+
try:
|
19 |
+
import torch
|
20 |
+
TORCH_AVAILABLE = True
|
21 |
+
except ImportError:
|
22 |
+
TORCH_AVAILABLE = False
|
23 |
+
|
24 |
+
def check_gpu_availability():
|
25 |
+
gpu_available = False
|
26 |
+
gpu_info = ""
|
27 |
+
|
28 |
+
tf_gpu = len(tf.config.list_physical_devices('GPU')) > 0
|
29 |
+
|
30 |
+
torch_gpu = False
|
31 |
+
if TORCH_AVAILABLE:
|
32 |
+
torch_gpu = torch.cuda.is_available()
|
33 |
+
|
34 |
+
gpu_available = tf_gpu or torch_gpu
|
35 |
+
|
36 |
+
if gpu_available:
|
37 |
+
if tf_gpu:
|
38 |
+
gpu_info += f"TensorFlow GPU: {tf.config.list_physical_devices('GPU')}\n"
|
39 |
+
if torch_gpu:
|
40 |
+
gpu_info += f"PyTorch GPU: {torch.cuda.get_device_name(0)}\n"
|
41 |
+
|
42 |
+
return gpu_available, gpu_info
|
43 |
+
|
44 |
+
def configure_gpu():
|
45 |
+
try:
|
46 |
+
gpus = tf.config.experimental.list_physical_devices('GPU')
|
47 |
+
if gpus:
|
48 |
+
for gpu in gpus:
|
49 |
+
tf.config.experimental.set_memory_growth(gpu, True)
|
50 |
+
st.sidebar.success("GPU configured for optimal performance")
|
51 |
+
except Exception as e:
|
52 |
+
st.sidebar.warning(f"Could not configure GPU: {e}")
|
53 |
+
|
54 |
+
class NumpyJSONEncoder(json.JSONEncoder):
|
55 |
+
def default(self, obj):
|
56 |
+
if isinstance(obj, np.integer):
|
57 |
+
return int(obj)
|
58 |
+
elif isinstance(obj, np.floating):
|
59 |
+
return float(obj)
|
60 |
+
elif isinstance(obj, np.ndarray):
|
61 |
+
return obj.tolist()
|
62 |
+
return super(NumpyJSONEncoder, self).default(obj)
|
63 |
+
|
64 |
+
def save_user_preferences(prefs):
|
65 |
+
try:
|
66 |
+
with open(USER_PREF_PATH, "w") as f:
|
67 |
+
json.dump(prefs, f, cls=NumpyJSONEncoder)
|
68 |
+
except Exception as e:
|
69 |
+
st.error(f"Failed to save preferences: {e}")
|
70 |
+
|
71 |
+
GPU_AVAILABLE, GPU_INFO = check_gpu_availability()
|
72 |
+
if GPU_AVAILABLE:
|
73 |
+
configure_gpu()
|
74 |
+
|
75 |
+
if 'model_cache' not in st.session_state:
|
76 |
+
st.session_state['model_cache'] = {}
|
77 |
+
|
78 |
+
@lru_cache(maxsize=4)
|
79 |
+
def load_face_model(model_name, use_gpu=False):
|
80 |
+
if model_name in st.session_state['model_cache']:
|
81 |
+
return st.session_state['model_cache'][model_name]
|
82 |
+
|
83 |
+
if use_gpu and GPU_AVAILABLE:
|
84 |
+
if model_name == "VGG-Face":
|
85 |
+
pass
|
86 |
+
|
87 |
+
st.session_state['model_cache'][model_name] = model_name
|
88 |
+
return model_name
|
89 |
+
|
90 |
+
def preprocess_image(img_array):
|
91 |
+
height, width = img_array.shape[:2]
|
92 |
+
max_dimension = 640
|
93 |
+
|
94 |
+
if max(height, width) > max_dimension:
|
95 |
+
scale = max_dimension / max(height, width)
|
96 |
+
new_height = int(height * scale)
|
97 |
+
new_width = int(width * scale)
|
98 |
+
img_array = cv2.resize(img_array, (new_width, new_height))
|
99 |
+
|
100 |
+
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
|
101 |
+
equalized = cv2.equalizeHist(gray)
|
102 |
+
enhanced = cv2.cvtColor(equalized, cv2.COLOR_GRAY2RGB)
|
103 |
+
|
104 |
+
brightened = cv2.convertScaleAbs(img_array, alpha=1.5, beta=30)
|
105 |
+
|
106 |
+
return {"original": img_array, "enhanced": enhanced, "brightened": brightened}
|
107 |
+
|
108 |
+
def detect_faces_with_fallback(img_array):
|
109 |
+
faces = None
|
110 |
+
detection_method = "none"
|
111 |
+
|
112 |
+
preprocessed = preprocess_image(img_array)
|
113 |
+
|
114 |
+
try:
|
115 |
+
faces = RetinaFace.detect_faces(preprocessed["original"])
|
116 |
+
if faces:
|
117 |
+
detection_method = "retinaface_original"
|
118 |
+
return faces, detection_method
|
119 |
+
except Exception:
|
120 |
+
pass
|
121 |
+
|
122 |
+
try:
|
123 |
+
faces = RetinaFace.detect_faces(preprocessed["enhanced"])
|
124 |
+
if faces:
|
125 |
+
detection_method = "retinaface_enhanced"
|
126 |
+
return faces, detection_method
|
127 |
+
except Exception:
|
128 |
+
pass
|
129 |
+
|
130 |
+
try:
|
131 |
+
faces = RetinaFace.detect_faces(preprocessed["brightened"])
|
132 |
+
if faces:
|
133 |
+
detection_method = "retinaface_brightened"
|
134 |
+
return faces, detection_method
|
135 |
+
except Exception:
|
136 |
+
pass
|
137 |
+
|
138 |
+
try:
|
139 |
+
face_cascade = cv2.CascadeClassifier(
|
140 |
+
cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
|
141 |
+
)
|
142 |
+
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
|
143 |
+
opencv_faces = face_cascade.detectMultiScale(gray, 1.1, 4)
|
144 |
+
|
145 |
+
if len(opencv_faces) > 0:
|
146 |
+
faces = {}
|
147 |
+
for i, (x, y, w, h) in enumerate(opencv_faces):
|
148 |
+
faces[f"face_{i+1}"] = {"facial_area": (x, y, w, h), "score": 0.9}
|
149 |
+
detection_method = "opencv_haar"
|
150 |
+
return faces, detection_method
|
151 |
+
except Exception:
|
152 |
+
pass
|
153 |
+
|
154 |
+
try:
|
155 |
+
result = DeepFace.extract_faces(
|
156 |
+
img_path=img_array,
|
157 |
+
enforce_detection=False,
|
158 |
+
detector_backend="opencv",
|
159 |
+
align=True,
|
160 |
+
)
|
161 |
+
|
162 |
+
if result and len(result) > 0:
|
163 |
+
faces = {}
|
164 |
+
for i, face_data in enumerate(result):
|
165 |
+
facial_area = face_data.get("facial_area", {})
|
166 |
+
x = facial_area.get("x", 0)
|
167 |
+
y = facial_area.get("y", 0)
|
168 |
+
w = facial_area.get("w", 0)
|
169 |
+
h = facial_area.get("h", 0)
|
170 |
+
faces[f"face_{i+1}"] = {"facial_area": (x, y, w, h), "score": 0.8}
|
171 |
+
detection_method = "deepface_detector"
|
172 |
+
return faces, detection_method
|
173 |
+
except Exception:
|
174 |
+
pass
|
175 |
+
|
176 |
+
if not faces:
|
177 |
+
try:
|
178 |
+
from deepface.detectors import MTCNN
|
179 |
+
|
180 |
+
detector = MTCNN()
|
181 |
+
dets = detector.detect_faces(img_array)
|
182 |
+
if len(dets) > 0:
|
183 |
+
faces = {}
|
184 |
+
detection_method = "mtcnn_fallback"
|
185 |
+
for i, d in enumerate(dets):
|
186 |
+
x, y, w, h = d["box"]
|
187 |
+
faces[f"face_{i+1}"] = {
|
188 |
+
"facial_area": (x, y, w, h),
|
189 |
+
"score": d.get("confidence", 0.8),
|
190 |
+
}
|
191 |
+
return faces, detection_method
|
192 |
+
except Exception:
|
193 |
+
pass
|
194 |
+
|
195 |
+
return None, detection_method
|
196 |
+
|
197 |
+
def analyze_with_models(img_array):
|
198 |
+
results = []
|
199 |
+
|
200 |
+
models_to_try = ["VGG-Face", "Facenet", "DeepID"] if GPU_AVAILABLE else ["VGG-Face", "Facenet"]
|
201 |
+
|
202 |
+
analyze_kwargs = {
|
203 |
+
"actions": ["emotion"],
|
204 |
+
"enforce_detection": False,
|
205 |
+
"detector_backend": "skip",
|
206 |
+
"prog_bar": False
|
207 |
+
}
|
208 |
+
|
209 |
+
if GPU_AVAILABLE:
|
210 |
+
analyze_kwargs["use_gpu"] = True
|
211 |
+
|
212 |
+
for model in models_to_try:
|
213 |
+
try:
|
214 |
+
model_obj = load_face_model(model, use_gpu=GPU_AVAILABLE)
|
215 |
+
|
216 |
+
result = DeepFace.analyze(
|
217 |
+
img_path=img_array,
|
218 |
+
**analyze_kwargs
|
219 |
+
)
|
220 |
+
|
221 |
+
if isinstance(result, list):
|
222 |
+
result = result[0]
|
223 |
+
|
224 |
+
if "emotion" in result:
|
225 |
+
for key in result["emotion"]:
|
226 |
+
if isinstance(result["emotion"][key], (np.floating, np.integer)):
|
227 |
+
result["emotion"][key] = float(result["emotion"][key])
|
228 |
+
|
229 |
+
results.append(result)
|
230 |
+
|
231 |
+
if len(results) >= 2:
|
232 |
+
break
|
233 |
+
|
234 |
+
except Exception as e:
|
235 |
+
pass
|
236 |
+
|
237 |
+
return results
|
238 |
+
|
239 |
+
def weighted_average_results(results):
|
240 |
+
|
241 |
+
if not results:
|
242 |
+
return None
|
243 |
+
|
244 |
+
avg_result = results[0].copy()
|
245 |
+
weights = [1.0] * len(results)
|
246 |
+
|
247 |
+
for i, res in enumerate(results[1:], start=1):
|
248 |
+
if "emotion" in res and "emotion" in avg_result:
|
249 |
+
for key in avg_result["emotion"]:
|
250 |
+
if key in res["emotion"]:
|
251 |
+
avg_result["emotion"][key] += float(res["emotion"][key]) * weights[i]
|
252 |
+
|
253 |
+
total_weight = sum(weights)
|
254 |
+
if "emotion" in avg_result:
|
255 |
+
for key in avg_result["emotion"]:
|
256 |
+
avg_result["emotion"][key] = float(avg_result["emotion"][key]) / total_weight
|
257 |
+
|
258 |
+
dominant_key = max(avg_result["emotion"], key=avg_result["emotion"].get)
|
259 |
+
avg_result["dominant_emotion"] = dominant_key
|
260 |
+
|
261 |
+
return avg_result
|
262 |
+
|
263 |
+
def analyze_emotion_with_models(img_array):
|
264 |
+
max_dimension = 320
|
265 |
+
height, width = img_array.shape[:2]
|
266 |
+
if max(height, width) > max_dimension:
|
267 |
+
scale = max_dimension / max(height, width)
|
268 |
+
small_img = cv2.resize(img_array, (int(width * scale), int(height * scale)))
|
269 |
+
else:
|
270 |
+
small_img = img_array.copy()
|
271 |
+
|
272 |
+
faces, detection_method = detect_faces_with_fallback(small_img)
|
273 |
+
|
274 |
+
if not faces:
|
275 |
+
return (
|
276 |
+
None,
|
277 |
+
0,
|
278 |
+
"No face was detected. Please use better lighting or adjust your face position.",
|
279 |
+
)
|
280 |
+
|
281 |
+
try:
|
282 |
+
if isinstance(faces, dict) and len(faces) > 0:
|
283 |
+
face_key = list(faces.keys())[0]
|
284 |
+
face = faces[face_key]
|
285 |
+
|
286 |
+
x, y, w, h = face["facial_area"]
|
287 |
+
|
288 |
+
if max(height, width) > max_dimension:
|
289 |
+
scale_factor = max(height, width) / max_dimension
|
290 |
+
x = int(x * scale_factor)
|
291 |
+
y = int(y * scale_factor)
|
292 |
+
w = int(w * scale_factor)
|
293 |
+
h = int(h * scale_factor)
|
294 |
+
|
295 |
+
x, y = max(0, x), max(0, y)
|
296 |
+
w = min(w, img_array.shape[1] - x)
|
297 |
+
h = min(h, img_array.shape[0] - y)
|
298 |
+
|
299 |
+
if w > 0 and h > 0:
|
300 |
+
face_img = img_array[y:y+h, x:x+w]
|
301 |
+
else:
|
302 |
+
face_img = img_array
|
303 |
+
else:
|
304 |
+
face_img = img_array
|
305 |
+
|
306 |
+
results = analyze_with_models(face_img)
|
307 |
+
|
308 |
+
if not results:
|
309 |
+
return (
|
310 |
+
None,
|
311 |
+
0,
|
312 |
+
"A face was detected, but we couldn't analyze the emotion. Please try a different angle or change your expression.",
|
313 |
+
)
|
314 |
+
|
315 |
+
result_dict = weighted_average_results(results)
|
316 |
+
if not result_dict or "emotion" not in result_dict:
|
317 |
+
return (
|
318 |
+
None,
|
319 |
+
0,
|
320 |
+
"We found your face, but couldn't process the emotion data. Please try again with better lighting.",
|
321 |
+
)
|
322 |
+
|
323 |
+
dominant_emotion = result_dict["dominant_emotion"]
|
324 |
+
confidence = result_dict["emotion"][dominant_emotion] * 100
|
325 |
+
|
326 |
+
confidence = float(confidence)
|
327 |
+
|
328 |
+
if confidence < 35:
|
329 |
+
return (
|
330 |
+
None,
|
331 |
+
confidence,
|
332 |
+
"We found your face, but the confidence was too low to determine your mood. Please try a different angle or select your mood manually."
|
333 |
+
)
|
334 |
+
|
335 |
+
emotion_results = {
|
336 |
+
"emotions": {k: float(v) for k, v in result_dict["emotion"].items()},
|
337 |
+
"dominant_emotion": dominant_emotion
|
338 |
+
}
|
339 |
+
|
340 |
+
return emotion_results, confidence, detection_method
|
341 |
+
|
342 |
+
except Exception as e:
|
343 |
+
st.error(f"Error in emotion analysis: {str(e)}")
|
344 |
+
return (
|
345 |
+
None,
|
346 |
+
0,
|
347 |
+
"An error occurred during emotion analysis. Please try again."
|
348 |
+
)
|
349 |
+
|
350 |
+
st.set_page_config(
|
351 |
+
page_title="Music Recommendation System",
|
352 |
+
page_icon="πΌ",
|
353 |
+
layout="wide",
|
354 |
+
initial_sidebar_state="expanded",
|
355 |
+
)
|
356 |
+
|
357 |
+
USER_PREF_PATH = os.path.join(os.path.dirname(__file__), "user_preferences.json")
|
358 |
+
|
359 |
+
hindi_music_recommendations = {
|
360 |
+
"happy": [
|
361 |
+
{
|
362 |
+
"title": "Badtameez Dil β Yeh Jawaani Hai Deewani",
|
363 |
+
"url": "https://www.youtube.com/watch?v=II2EO3Nw4m0",
|
364 |
+
},
|
365 |
+
{
|
366 |
+
"title": "Gallan Goodiyaan β Dil Dhadakne Do",
|
367 |
+
"url": "https://www.youtube.com/watch?v=jCEdTq3j-0U",
|
368 |
+
},
|
369 |
+
{
|
370 |
+
"title": "Nagada Sang Dhol β Goliyon Ki Raasleela Ram-Leela",
|
371 |
+
"url": "https://www.youtube.com/watch?v=3X7x4Ye-tqo",
|
372 |
+
},
|
373 |
+
{
|
374 |
+
"title": "London Thumakda β Queen",
|
375 |
+
"url": "https://www.youtube.com/watch?v=udra3Mfw2oo",
|
376 |
+
},
|
377 |
+
{
|
378 |
+
"title": "Balam Pichkari β Yeh Jawaani Hai Deewani",
|
379 |
+
"url": "https://www.youtube.com/watch?v=0WtRNGubWGA",
|
380 |
+
},
|
381 |
+
{
|
382 |
+
"title": "Tune Maari Entriyaan β Gunday",
|
383 |
+
"url": "https://www.youtube.com/watch?v=2I3NgxDAiqE",
|
384 |
+
},
|
385 |
+
{
|
386 |
+
"title": "Kar Gayi Chull β Kapoor & Sons",
|
387 |
+
"url": "https://www.youtube.com/watch?v=NTHz9ephYTw",
|
388 |
+
},
|
389 |
+
{
|
390 |
+
"title": "Ghungroo β War",
|
391 |
+
"url": "https://www.youtube.com/watch?v=qFkNATtc3mc",
|
392 |
+
},
|
393 |
+
{
|
394 |
+
"title": "Aankh Marey β Simmba",
|
395 |
+
"url": "https://www.youtube.com/watch?v=O6OI2-p-gC4",
|
396 |
+
},
|
397 |
+
{
|
398 |
+
"title": "Morni Banke β Badhaai Ho",
|
399 |
+
"url": "https://www.youtube.com/watch?v=h-v5tHtL_cA",
|
400 |
+
},
|
401 |
+
{
|
402 |
+
"title": "Dil Dhadakne Do β Title Track",
|
403 |
+
"url": "https://www.youtube.com/watch?v=R5jQ8VudZbA",
|
404 |
+
},
|
405 |
+
{
|
406 |
+
"title": "Nashe Si Chadh Gayi β Befikre",
|
407 |
+
"url": "https://www.youtube.com/watch?v=Wd2B8OAOc9c",
|
408 |
+
},
|
409 |
+
{
|
410 |
+
"title": "Tamma Tamma Again β Badrinath Ki Dulhania",
|
411 |
+
"url": "https://www.youtube.com/watch?v=EEX_XM6SxmY",
|
412 |
+
},
|
413 |
+
{
|
414 |
+
"title": "Proper Patola β Namaste England",
|
415 |
+
"url": "https://www.youtube.com/watch?v=Y1O_aIV1FNM",
|
416 |
+
},
|
417 |
+
{
|
418 |
+
"title": "Abhi Toh Party Shuru Hui Hai β Khoobsurat",
|
419 |
+
"url": "https://www.youtube.com/watch?v=8LZgzAZ2lpQ",
|
420 |
+
},
|
421 |
+
],
|
422 |
+
"sad": [
|
423 |
+
{
|
424 |
+
"title": "Channa Mereya β Ae Dil Hai Mushkil",
|
425 |
+
"url": "https://www.youtube.com/watch?v=284Ov7ysmfA",
|
426 |
+
},
|
427 |
+
{
|
428 |
+
"title": "Tum Hi Ho β Aashiqui 2",
|
429 |
+
"url": "https://www.youtube.com/watch?v=Umqb9KENgmk",
|
430 |
+
},
|
431 |
+
{
|
432 |
+
"title": "Luka Chuppi β Rang De Basanti",
|
433 |
+
"url": "https://www.youtube.com/watch?v=_ikZtcgAMxo",
|
434 |
+
},
|
435 |
+
{
|
436 |
+
"title": "Agar Tum Saath Ho β Tamasha",
|
437 |
+
"url": "https://www.youtube.com/watch?v=sK7riqg2mr4",
|
438 |
+
},
|
439 |
+
{
|
440 |
+
"title": "Judaai β Badlapur",
|
441 |
+
"url": "https://www.youtube.com/watch?v=zPpNZFgSzDo",
|
442 |
+
},
|
443 |
+
{
|
444 |
+
"title": "Kabhi Alvida Naa Kehna β KANK",
|
445 |
+
"url": "https://www.youtube.com/watch?v=O8fIwHfZz2E",
|
446 |
+
},
|
447 |
+
{
|
448 |
+
"title": "Main Dhoondne Ko Zamaane Mein β Heartless",
|
449 |
+
"url": "https://www.youtube.com/watch?v=5wqoxs9zG3w",
|
450 |
+
},
|
451 |
+
{
|
452 |
+
"title": "Tujhe Bhula Diya β Anjaana Anjaani",
|
453 |
+
"url": "https://www.youtube.com/watch?v=_SK9K58Olqo",
|
454 |
+
},
|
455 |
+
{
|
456 |
+
"title": "Phir Le Aaya Dil β Barfi!",
|
457 |
+
"url": "https://www.youtube.com/watch?v=ntC3sO-VeJY",
|
458 |
+
},
|
459 |
+
{
|
460 |
+
"title": "Bhula Dena β Aashiqui 2",
|
461 |
+
"url": "https://www.youtube.com/watch?v=j4OVQJ6-R1U",
|
462 |
+
},
|
463 |
+
{
|
464 |
+
"title": "Kabira (Encore) β Yeh Jawaani Hai Deewani",
|
465 |
+
"url": "https://www.youtube.com/watch?v=jHNNMj5bNQw",
|
466 |
+
},
|
467 |
+
{
|
468 |
+
"title": "Tera Ban Jaunga β Kabir Singh",
|
469 |
+
"url": "https://www.youtube.com/watch?v=mQiiw7uRngA",
|
470 |
+
},
|
471 |
+
{
|
472 |
+
"title": "Humsafar β Badrinath Ki Dulhania",
|
473 |
+
"url": "https://www.youtube.com/watch?v=8v-TWxPWIWc",
|
474 |
+
},
|
475 |
+
{
|
476 |
+
"title": "Agar Tum Mil Jao β Zeher",
|
477 |
+
"url": "https://www.youtube.com/watch?v=C8tMQzcPXOY",
|
478 |
+
},
|
479 |
+
{
|
480 |
+
"title": "Dil Ke Paas β Wajah Tum Ho",
|
481 |
+
"url": "https://www.youtube.com/watch?v=FOLUdLQBPuE",
|
482 |
+
},
|
483 |
+
],
|
484 |
+
"angry": [
|
485 |
+
{
|
486 |
+
"title": "Challa β Jab Tak Hai Jaan",
|
487 |
+
"url": "https://www.youtube.com/watch?v=9a4izd3Rvdw",
|
488 |
+
},
|
489 |
+
{
|
490 |
+
"title": "Brothers Anthem β Brothers",
|
491 |
+
"url": "https://www.youtube.com/watch?v=IjBAgWKW12Y",
|
492 |
+
},
|
493 |
+
{
|
494 |
+
"title": "Sultan β Sultan",
|
495 |
+
"url": "https://www.youtube.com/watch?v=RYvUMglNznM",
|
496 |
+
},
|
497 |
+
{
|
498 |
+
"title": "Bulleya β Ae Dil Hai Mushkil",
|
499 |
+
"url": "https://www.youtube.com/watch?v=hXh35CtnSyU",
|
500 |
+
},
|
501 |
+
{
|
502 |
+
"title": "Sadda Haq β Rockstar",
|
503 |
+
"url": "https://www.youtube.com/watch?v=p9DQINKZxWE",
|
504 |
+
},
|
505 |
+
{
|
506 |
+
"title": "Jee Karda β Badlapur",
|
507 |
+
"url": "https://www.youtube.com/watch?v=BN45QQ7R92M",
|
508 |
+
},
|
509 |
+
{
|
510 |
+
"title": "Dhan Te Nan β Kaminey",
|
511 |
+
"url": "https://www.youtube.com/watch?v=m9RdKcnUvFU",
|
512 |
+
},
|
513 |
+
{
|
514 |
+
"title": "Bhaag DK Bose β Delhi Belly",
|
515 |
+
"url": "https://www.youtube.com/watch?v=IQEDu8SPHao",
|
516 |
+
},
|
517 |
+
{
|
518 |
+
"title": "Sher Aaya Sher β Gully Boy",
|
519 |
+
"url": "https://www.youtube.com/watch?v=hejXc_FSYb8",
|
520 |
+
},
|
521 |
+
{
|
522 |
+
"title": "Aala Re Aala β Simmba",
|
523 |
+
"url": "https://www.youtube.com/watch?v=2wbVxHlOepM",
|
524 |
+
},
|
525 |
+
{
|
526 |
+
"title": "Zinda β Bhaag Milkha Bhaag",
|
527 |
+
"url": "https://www.youtube.com/watch?v=RLzC55ai0eo",
|
528 |
+
},
|
529 |
+
{
|
530 |
+
"title": "Mardaani β Mardaani",
|
531 |
+
"url": "https://www.youtube.com/watch?v=C1QOVnH0bKY",
|
532 |
+
},
|
533 |
+
{
|
534 |
+
"title": "Jai Ho β Slumdog Millionaire",
|
535 |
+
"url": "https://www.youtube.com/watch?v=Yc5OyXmHD0w",
|
536 |
+
},
|
537 |
+
{
|
538 |
+
"title": "Malhari β Bajirao Mastani",
|
539 |
+
"url": "https://www.youtube.com/watch?v=l_MyUGq7pgs",
|
540 |
+
},
|
541 |
+
{
|
542 |
+
"title": "Apna Time Aayega β Gully Boy",
|
543 |
+
"url": "https://www.youtube.com/watch?v=SlHnlxDt2TQ",
|
544 |
+
},
|
545 |
+
],
|
546 |
+
"fear": [
|
547 |
+
{
|
548 |
+
"title": "Darr Ke Aage Jeet Hai β Mountain Dew",
|
549 |
+
"url": "https://www.youtube.com/watch?v=xT7E-n1t3vI",
|
550 |
+
},
|
551 |
+
{
|
552 |
+
"title": "Hai Ram β Sarkar Raj",
|
553 |
+
"url": "https://www.youtube.com/watch?v=zDQUylzavMQ",
|
554 |
+
},
|
555 |
+
{
|
556 |
+
"title": "Aaj Phir Jeene Ki Tamanna Hai β Guide",
|
557 |
+
"url": "https://www.youtube.com/watch?v=2LG8LwEVlJE",
|
558 |
+
},
|
559 |
+
{
|
560 |
+
"title": "Main Hoon Don β Don",
|
561 |
+
"url": "https://www.youtube.com/watch?v=xvNgjtgXOVo",
|
562 |
+
},
|
563 |
+
{
|
564 |
+
"title": "Khalbali β Rang De Basanti",
|
565 |
+
"url": "https://www.youtube.com/watch?v=Yd-ngSyDZss",
|
566 |
+
},
|
567 |
+
{
|
568 |
+
"title": "Bhoot Hoon Main β Bhoot",
|
569 |
+
"url": "https://www.youtube.com/watch?v=JNV4To5uzKA",
|
570 |
+
},
|
571 |
+
{
|
572 |
+
"title": "Bhool Bhulaiyaa β Title Track",
|
573 |
+
"url": "https://www.youtube.com/watch?v=eN6AYHAT8UM",
|
574 |
+
},
|
575 |
+
{
|
576 |
+
"title": "Darr β Title Track",
|
577 |
+
"url": "https://www.youtube.com/watch?v=BTAXAc1bJh8",
|
578 |
+
},
|
579 |
+
{
|
580 |
+
"title": "Pari β Title Track",
|
581 |
+
"url": "https://www.youtube.com/watch?v=ZwyKOXwJsC0",
|
582 |
+
},
|
583 |
+
{
|
584 |
+
"title": "Bol Na Halke Halke β Jhoom Barabar Jhoom",
|
585 |
+
"url": "https://www.youtube.com/watch?v=S9LZjGGeedw",
|
586 |
+
},
|
587 |
+
{
|
588 |
+
"title": "Raat Ka Nasha β Asoka",
|
589 |
+
"url": "https://www.youtube.com/watch?v=OjaFNUA-UFE",
|
590 |
+
},
|
591 |
+
{
|
592 |
+
"title": "Phir Se Ud Chala β Rockstar",
|
593 |
+
"url": "https://www.youtube.com/watch?v=2mWaqsC3U7k",
|
594 |
+
},
|
595 |
+
{
|
596 |
+
"title": "Roobaroo β Rang De Basanti",
|
597 |
+
"url": "https://www.youtube.com/watch?v=BrfRB6aTZlM",
|
598 |
+
},
|
599 |
+
{
|
600 |
+
"title": "Khamoshiyan β Khamoshiyan",
|
601 |
+
"url": "https://www.youtube.com/watch?v=FXiaIH49oAU",
|
602 |
+
},
|
603 |
+
{
|
604 |
+
"title": "Tum Ho Toh β Rock On!!",
|
605 |
+
"url": "https://www.youtube.com/watch?v=hCsY8T0uBGA",
|
606 |
+
},
|
607 |
+
],
|
608 |
+
"neutral": [
|
609 |
+
{
|
610 |
+
"title": "Kabira β Yeh Jawaani Hai Deewani",
|
611 |
+
"url": "https://www.youtube.com/watch?v=jHNNMj5bNQw",
|
612 |
+
},
|
613 |
+
{
|
614 |
+
"title": "Mitwa β Kabhi Alvida Naa Kehna",
|
615 |
+
"url": "https://www.youtube.com/watch?v=Cv86br9MSBc",
|
616 |
+
},
|
617 |
+
{
|
618 |
+
"title": "Kun Faya Kun β Rockstar",
|
619 |
+
"url": "https://www.youtube.com/watch?v=T94PHkuydcw",
|
620 |
+
},
|
621 |
+
{
|
622 |
+
"title": "Tum Se Hi β Jab We Met",
|
623 |
+
"url": "https://www.youtube.com/watch?v=mt9xg0mmt28",
|
624 |
+
},
|
625 |
+
{
|
626 |
+
"title": "Iktara β Wake Up Sid",
|
627 |
+
"url": "https://www.youtube.com/watch?v=fSS_R91Nimw",
|
628 |
+
},
|
629 |
+
{
|
630 |
+
"title": "Nazm Nazm β Bareilly Ki Barfi",
|
631 |
+
"url": "https://www.youtube.com/watch?v=DK_UsATwoxI",
|
632 |
+
},
|
633 |
+
{
|
634 |
+
"title": "Ae Dil Hai Mushkil β ADHM",
|
635 |
+
"url": "https://www.youtube.com/watch?v=6FURuLYrR_Q",
|
636 |
+
},
|
637 |
+
{
|
638 |
+
"title": "Raabta β Agent Vinod",
|
639 |
+
"url": "https://www.youtube.com/watch?v=zAU_rsoS5ok",
|
640 |
+
},
|
641 |
+
{
|
642 |
+
"title": "Tera Hone Laga Hoon β Ajab Prem Ki Ghazab Kahani",
|
643 |
+
"url": "https://www.youtube.com/watch?v=K0IvuwrSFaI",
|
644 |
+
},
|
645 |
+
{
|
646 |
+
"title": "Safarnama β Tamasha",
|
647 |
+
"url": "https://www.youtube.com/watch?v=zLv0V_19L-A",
|
648 |
+
},
|
649 |
+
{
|
650 |
+
"title": "Pehli Nazar Mein β Race",
|
651 |
+
"url": "https://www.youtube.com/watch?v=BadBAMnPX0I",
|
652 |
+
},
|
653 |
+
{
|
654 |
+
"title": "Saibo β Shor in the City",
|
655 |
+
"url": "https://www.youtube.com/watch?v=zXLgYBSdv74",
|
656 |
+
},
|
657 |
+
{
|
658 |
+
"title": "O Re Piya β Aaja Nachle",
|
659 |
+
"url": "https://www.youtube.com/watch?v=iv7lcUkFVSc",
|
660 |
+
},
|
661 |
+
{
|
662 |
+
"title": "Khairiyat β Chhichhore",
|
663 |
+
"url": "https://www.youtube.com/watch?v=hoNb6HuNmU0",
|
664 |
+
},
|
665 |
+
{
|
666 |
+
"title": "Manwa Laage β Happy New Year",
|
667 |
+
"url": "https://www.youtube.com/watch?v=d8IT-16kA8M",
|
668 |
+
},
|
669 |
+
],
|
670 |
+
"disgust": [
|
671 |
+
{
|
672 |
+
"title": "Beedi β Omkara",
|
673 |
+
"url": "https://www.youtube.com/watch?v=XLJCtZKQGrY",
|
674 |
+
},
|
675 |
+
{
|
676 |
+
"title": "Emotional Atyachaar β Dev D",
|
677 |
+
"url": "https://www.youtube.com/watch?v=Vng5mg0iY0k",
|
678 |
+
},
|
679 |
+
{
|
680 |
+
"title": "Gandi Baat β R... Rajkumar",
|
681 |
+
"url": "https://www.youtube.com/watch?v=vvLBXO4MnKQ",
|
682 |
+
},
|
683 |
+
{
|
684 |
+
"title": "Bluffmaster β Bluffmaster",
|
685 |
+
"url": "https://www.youtube.com/watch?v=t5UzO4gOYKc",
|
686 |
+
},
|
687 |
+
{
|
688 |
+
"title": "Dhoom Machale β Dhoom",
|
689 |
+
"url": "https://www.youtube.com/watch?v=ymk2_5a2V8g",
|
690 |
+
},
|
691 |
+
{
|
692 |
+
"title": "Zor Ka Jhatka β Action Replayy",
|
693 |
+
"url": "https://www.youtube.com/watch?v=UZV8Yb4hVgU",
|
694 |
+
},
|
695 |
+
{
|
696 |
+
"title": "Genda Phool β Delhi-6",
|
697 |
+
"url": "https://www.youtube.com/watch?v=f0CvPQ3l-Xg",
|
698 |
+
},
|
699 |
+
{
|
700 |
+
"title": "Dum Maro Dum β Hare Rama Hare Krishna",
|
701 |
+
"url": "https://www.youtube.com/watch?v=BZNT_Y-mAkE",
|
702 |
+
},
|
703 |
+
{
|
704 |
+
"title": "Chaar Botal Vodka β Ragini MMS 2",
|
705 |
+
"url": "https://www.youtube.com/watch?v=x8F5dz8kv1w",
|
706 |
+
},
|
707 |
+
{
|
708 |
+
"title": "Kamli β Dhoom 3",
|
709 |
+
"url": "https://www.youtube.com/watch?v=C8kSrkz8Hz8",
|
710 |
+
},
|
711 |
+
{
|
712 |
+
"title": "Munni Badnaam Hui β Dabangg",
|
713 |
+
"url": "https://www.youtube.com/watch?v=Jn5hsfbhWx4",
|
714 |
+
},
|
715 |
+
{
|
716 |
+
"title": "Sheila Ki Jawani β Tees Maar Khan",
|
717 |
+
"url": "https://www.youtube.com/watch?v=ZTmF2v59CtI",
|
718 |
+
},
|
719 |
+
{
|
720 |
+
"title": "Baby Doll β Ragini MMS 2",
|
721 |
+
"url": "https://www.youtube.com/watch?v=yP9KiFTyBks",
|
722 |
+
},
|
723 |
+
{
|
724 |
+
"title": "Oo Antava β Pushpa",
|
725 |
+
"url": "https://www.youtube.com/watch?v=kyNdRJR_NRs",
|
726 |
+
},
|
727 |
+
{
|
728 |
+
"title": "Laila Main Laila β Raees",
|
729 |
+
"url": "https://www.youtube.com/watch?v=fMW7ze7-Gik",
|
730 |
+
},
|
731 |
+
],
|
732 |
+
"surprise": [
|
733 |
+
{
|
734 |
+
"title": "Kala Chashma β Baar Baar Dekho",
|
735 |
+
"url": "https://www.youtube.com/watch?v=k4yXQkG2s1E",
|
736 |
+
},
|
737 |
+
{
|
738 |
+
"title": "Matargashti β Tamasha",
|
739 |
+
"url": "https://www.youtube.com/watch?v=6vKucgAeF_Q",
|
740 |
+
},
|
741 |
+
{
|
742 |
+
"title": "Sooraj Dooba Hain β Roy",
|
743 |
+
"url": "https://www.youtube.com/watch?v=nJZcbidTutE",
|
744 |
+
},
|
745 |
+
{
|
746 |
+
"title": "Ghagra β Yeh Jawaani Hai Deewani",
|
747 |
+
"url": "https://www.youtube.com/watch?v=caoGNx1LF2Q",
|
748 |
+
},
|
749 |
+
{
|
750 |
+
"title": "Ishq Shava β Jab Tak Hai Jaan",
|
751 |
+
"url": "https://www.youtube.com/watch?v=2kH4fLrwc0A",
|
752 |
+
},
|
753 |
+
{
|
754 |
+
"title": "Naina β Dangal",
|
755 |
+
"url": "https://www.youtube.com/watch?v=BTtx6HMzunQ",
|
756 |
+
},
|
757 |
+
{
|
758 |
+
"title": "Haanikaarak Bapu β Dangal",
|
759 |
+
"url": "https://www.youtube.com/watch?v=KyZZrClK0rM",
|
760 |
+
},
|
761 |
+
{
|
762 |
+
"title": "Malhari β Bajirao Mastani",
|
763 |
+
"url": "https://www.youtube.com/watch?v=l_MyUGq7pgs",
|
764 |
+
},
|
765 |
+
{
|
766 |
+
"title": "Dilbar β Satyameva Jayate",
|
767 |
+
"url": "https://www.youtube.com/watch?v=JFcgOboQZ08",
|
768 |
+
},
|
769 |
+
{
|
770 |
+
"title": "Bom Diggy Diggy β Sonu Ke Titu Ki Sweety",
|
771 |
+
"url": "https://www.youtube.com/watch?v=yIIGQB6EMAM",
|
772 |
+
},
|
773 |
+
{
|
774 |
+
"title": "Cutiepie β Ae Dil Hai Mushkil",
|
775 |
+
"url": "https://www.youtube.com/watch?v=f6vY6tYvKGA",
|
776 |
+
},
|
777 |
+
{
|
778 |
+
"title": "Coca Cola β Luka Chuppi",
|
779 |
+
"url": "https://www.youtube.com/watch?v=_cPHiwPqbqo",
|
780 |
+
},
|
781 |
+
{
|
782 |
+
"title": "Sweety Tera Drama β Bareilly Ki Barfi",
|
783 |
+
"url": "https://www.youtube.com/watch?v=1MU4wjcxoR4",
|
784 |
+
},
|
785 |
+
{
|
786 |
+
"title": "Dil Chori β Sonu Ke Titu Ki Sweety",
|
787 |
+
"url": "https://www.youtube.com/watch?v=MU9oULiwmaU",
|
788 |
+
},
|
789 |
+
{
|
790 |
+
"title": "Koi Kahe Kehta Rahe β Dil Chahta Hai",
|
791 |
+
"url": "https://www.youtube.com/watch?v=4vEBkbkzwR8",
|
792 |
+
},
|
793 |
+
],
|
794 |
+
}
|
795 |
+
|
796 |
+
|
797 |
+
def load_css():
|
798 |
+
st.markdown(
|
799 |
+
"""
|
800 |
+
<style>
|
801 |
+
* {
|
802 |
+
font-family: 'Segoe UI', Arial, sans-serif !important;
|
803 |
+
}
|
804 |
+
|
805 |
+
.main-header {
|
806 |
+
font-size: 2.8rem;
|
807 |
+
color: #ff5722;
|
808 |
+
text-align: center;
|
809 |
+
margin-bottom: 1.5rem;
|
810 |
+
font-weight: bold;
|
811 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
812 |
+
background: linear-gradient(45deg, #ff5722, #FF9800);
|
813 |
+
-webkit-background-clip: text;
|
814 |
+
-webkit-text-fill-color: transparent;
|
815 |
+
// animation: pulse 2s infinite;
|
816 |
+
}
|
817 |
+
|
818 |
+
@keyframes pulse {
|
819 |
+
0% { transform: scale(1); }
|
820 |
+
50% { transform: scale(1.03); }
|
821 |
+
100% { transform: scale(1); }
|
822 |
+
}
|
823 |
+
|
824 |
+
.hindi-title {
|
825 |
+
font-size: 1.5rem;
|
826 |
+
color: #333;
|
827 |
+
font-weight: 600;
|
828 |
+
margin-bottom: 0.5rem;
|
829 |
+
}
|
830 |
+
|
831 |
+
.subheader {
|
832 |
+
font-size: 1.8rem;
|
833 |
+
color: #ff5722;
|
834 |
+
margin-bottom: 1rem;
|
835 |
+
text-transform: uppercase;
|
836 |
+
letter-spacing: 1px;
|
837 |
+
border-bottom: 2px solid #ff5722;
|
838 |
+
padding-bottom: 0.5rem;
|
839 |
+
}
|
840 |
+
|
841 |
+
.emotion-card {
|
842 |
+
padding: 1.5rem;
|
843 |
+
border-radius: 12px;
|
844 |
+
margin-bottom: 1.5rem;
|
845 |
+
background-color: #2596be;
|
846 |
+
border-left: 5px solid #ff5722;
|
847 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
848 |
+
transition: all 0.3s ease;
|
849 |
+
}
|
850 |
+
|
851 |
+
.emotion-card:hover {
|
852 |
+
box-shadow: 0 6px 12px rgba(0,0,0,0.1);
|
853 |
+
transform: translateY(-3px);
|
854 |
+
}
|
855 |
+
|
856 |
+
.recommendation-card {
|
857 |
+
padding: 1.5rem;
|
858 |
+
border-radius: 12px;
|
859 |
+
margin: 1.5rem 0;
|
860 |
+
background-color: #009933;
|
861 |
+
border-left: 6px solid #ff5722;
|
862 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.08);
|
863 |
+
transition: transform 0.3s;
|
864 |
+
}
|
865 |
+
|
866 |
+
.recommendation-card:hover {
|
867 |
+
transform: translateY(-5px);
|
868 |
+
box-shadow: 0 8px 20px rgba(0,0,0,0.12);
|
869 |
+
}
|
870 |
+
|
871 |
+
.stButton>button {
|
872 |
+
background-color: #ff5722;
|
873 |
+
color: white;
|
874 |
+
border-radius: 20px;
|
875 |
+
font-size: 1rem;
|
876 |
+
padding: 0.5rem 1.5rem;
|
877 |
+
border: none;
|
878 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
879 |
+
transition: all 0.3s;
|
880 |
+
}
|
881 |
+
|
882 |
+
.stButton>button:hover {
|
883 |
+
background-color: #ffffff;
|
884 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
885 |
+
transform: translateY(-2px);
|
886 |
+
}
|
887 |
+
|
888 |
+
.like-button {
|
889 |
+
display: flex;
|
890 |
+
justify-content: center;
|
891 |
+
align-items: center;
|
892 |
+
}
|
893 |
+
|
894 |
+
.mood-button {
|
895 |
+
text-align: center;
|
896 |
+
margin-bottom: 12px;
|
897 |
+
}
|
898 |
+
|
899 |
+
.mood-emoji {
|
900 |
+
font-size: 2.2rem;
|
901 |
+
margin-bottom: 8px;
|
902 |
+
display: block;
|
903 |
+
}
|
904 |
+
|
905 |
+
.mood-label {
|
906 |
+
display: block;
|
907 |
+
text-align: center;
|
908 |
+
}
|
909 |
+
|
910 |
+
.stSelectbox>div>div {
|
911 |
+
background-color: #06c91a;
|
912 |
+
border: 1px solid #ddd;
|
913 |
+
border-radius: 8px;
|
914 |
+
}
|
915 |
+
|
916 |
+
.info-box {
|
917 |
+
background-color: #009933;
|
918 |
+
border-left: 4px solid #2196F3;
|
919 |
+
padding: 1rem;
|
920 |
+
border-radius: 8px;
|
921 |
+
margin-bottom: 1rem;
|
922 |
+
}
|
923 |
+
|
924 |
+
.feature-card {
|
925 |
+
background-color: #3399ff;
|
926 |
+
border-radius: 10px;
|
927 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
928 |
+
padding: 1.5rem;
|
929 |
+
margin-bottom: 1rem;
|
930 |
+
transition: all 0.3s ease;
|
931 |
+
border-top: 4px solid #ff5722;
|
932 |
+
}
|
933 |
+
|
934 |
+
.feature-card:hover {
|
935 |
+
transform: translateY(-5px);
|
936 |
+
box-shadow: 0 8px 15px rgba(0,0,0,0.1);
|
937 |
+
}
|
938 |
+
|
939 |
+
.feature-icon {
|
940 |
+
font-size: 2rem;
|
941 |
+
margin-bottom: 0.5rem;
|
942 |
+
color: #ff5722;
|
943 |
+
}
|
944 |
+
|
945 |
+
.sidebar-info {
|
946 |
+
background-color: #ffffff;
|
947 |
+
padding: 1rem;
|
948 |
+
border-radius: 8px;
|
949 |
+
margin-top: 1rem;
|
950 |
+
}
|
951 |
+
|
952 |
+
.sidebar-header {
|
953 |
+
color: #ff5722;
|
954 |
+
font-size: 1.2rem;
|
955 |
+
font-weight: bold;
|
956 |
+
margin-bottom: 0.5rem;
|
957 |
+
}
|
958 |
+
|
959 |
+
.step-container {
|
960 |
+
display: flex;
|
961 |
+
margin-bottom: 1rem;
|
962 |
+
}
|
963 |
+
|
964 |
+
.step-number {
|
965 |
+
background-color: #ff5722;
|
966 |
+
color: white;
|
967 |
+
width: 30px;
|
968 |
+
height: 30px;
|
969 |
+
display: flex;
|
970 |
+
align-items: center;
|
971 |
+
justify-content: center;
|
972 |
+
border-radius: 50%;
|
973 |
+
margin-right: 10px;
|
974 |
+
flex-shrink: 0;
|
975 |
+
}
|
976 |
+
|
977 |
+
.step-content {
|
978 |
+
flex: 1;
|
979 |
+
}
|
980 |
+
|
981 |
+
.footer {
|
982 |
+
text-align: center;
|
983 |
+
margin-top: 3rem;
|
984 |
+
padding-top: 1rem;
|
985 |
+
border-top: 1px solid #eee;
|
986 |
+
font-size: 0.9rem;
|
987 |
+
color: #777;
|
988 |
+
}
|
989 |
+
|
990 |
+
@keyframes fadeIn {
|
991 |
+
from { opacity: 0; transform: translateY(20px); }
|
992 |
+
to { opacity: 1; transform: translateY(0); }
|
993 |
+
}
|
994 |
+
|
995 |
+
.animated-fade {
|
996 |
+
animation: fadeIn 0.5s ease-out;
|
997 |
+
}
|
998 |
+
|
999 |
+
.stVideo {
|
1000 |
+
border-radius: 10px;
|
1001 |
+
overflow: hidden;
|
1002 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
1003 |
+
}
|
1004 |
+
|
1005 |
+
.hide-on-secondary-pages {
|
1006 |
+
display: none;
|
1007 |
+
}
|
1008 |
+
|
1009 |
+
.show-on-home {
|
1010 |
+
display: block;
|
1011 |
+
}
|
1012 |
+
|
1013 |
+
.custom-mood-btn {
|
1014 |
+
width: 100%;
|
1015 |
+
border-radius: 12px;
|
1016 |
+
padding: 10px;
|
1017 |
+
background-color: #ffffff;
|
1018 |
+
color: #333;
|
1019 |
+
border: 1px solid #ddd;
|
1020 |
+
cursor: pointer;
|
1021 |
+
display: flex;
|
1022 |
+
flex-direction: column;
|
1023 |
+
align-items: center;
|
1024 |
+
justify-content: center;
|
1025 |
+
transition: all 0.3s ease;
|
1026 |
+
margin-bottom: 10px;
|
1027 |
+
}
|
1028 |
+
|
1029 |
+
.custom-mood-btn:hover {
|
1030 |
+
background-color: #ffffff;
|
1031 |
+
transform: translateY(-3px);
|
1032 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
1033 |
+
}
|
1034 |
+
</style>
|
1035 |
+
""",
|
1036 |
+
unsafe_allow_html=True,
|
1037 |
+
)
|
1038 |
+
|
1039 |
+
|
1040 |
+
def load_user_preferences():
|
1041 |
+
if not os.path.exists(USER_PREF_PATH):
|
1042 |
+
return {"liked_songs": [], "emotion_history": []}
|
1043 |
+
|
1044 |
+
try:
|
1045 |
+
with open(USER_PREF_PATH, "r") as f:
|
1046 |
+
return json.load(f)
|
1047 |
+
except:
|
1048 |
+
return {"liked_songs": [], "emotion_history": []}
|
1049 |
+
|
1050 |
+
|
1051 |
+
@lru_cache(maxsize=4)
|
1052 |
+
def load_face_model(model_name):
|
1053 |
+
return model_name
|
1054 |
+
|
1055 |
+
|
1056 |
+
def preprocess_image(img_array):
|
1057 |
+
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
|
1058 |
+
equalized = cv2.equalizeHist(gray)
|
1059 |
+
enhanced = cv2.cvtColor(equalized, cv2.COLOR_GRAY2RGB)
|
1060 |
+
|
1061 |
+
brightened = cv2.convertScaleAbs(img_array, alpha=1.5, beta=30)
|
1062 |
+
|
1063 |
+
return {"original": img_array, "enhanced": enhanced, "brightened": brightened}
|
1064 |
+
|
1065 |
+
|
1066 |
+
def detect_faces_with_fallback(img_array):
|
1067 |
+
faces = None
|
1068 |
+
detection_method = "none"
|
1069 |
+
|
1070 |
+
preprocessed = preprocess_image(img_array)
|
1071 |
+
|
1072 |
+
try:
|
1073 |
+
faces = RetinaFace.detect_faces(preprocessed["original"])
|
1074 |
+
if faces:
|
1075 |
+
detection_method = "retinaface_original"
|
1076 |
+
return faces, detection_method
|
1077 |
+
except Exception:
|
1078 |
+
pass
|
1079 |
+
|
1080 |
+
try:
|
1081 |
+
faces = RetinaFace.detect_faces(preprocessed["enhanced"])
|
1082 |
+
if faces:
|
1083 |
+
detection_method = "retinaface_enhanced"
|
1084 |
+
return faces, detection_method
|
1085 |
+
except Exception:
|
1086 |
+
pass
|
1087 |
+
|
1088 |
+
try:
|
1089 |
+
faces = RetinaFace.detect_faces(preprocessed["brightened"])
|
1090 |
+
if faces:
|
1091 |
+
detection_method = "retinaface_brightened"
|
1092 |
+
return faces, detection_method
|
1093 |
+
except Exception:
|
1094 |
+
pass
|
1095 |
+
|
1096 |
+
try:
|
1097 |
+
face_cascade = cv2.CascadeClassifier(
|
1098 |
+
cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
|
1099 |
+
)
|
1100 |
+
gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
|
1101 |
+
opencv_faces = face_cascade.detectMultiScale(gray, 1.1, 4)
|
1102 |
+
|
1103 |
+
if len(opencv_faces) > 0:
|
1104 |
+
faces = {}
|
1105 |
+
for i, (x, y, w, h) in enumerate(opencv_faces):
|
1106 |
+
faces[f"face_{i+1}"] = {"facial_area": (x, y, w, h), "score": 0.9}
|
1107 |
+
detection_method = "opencv_haar"
|
1108 |
+
return faces, detection_method
|
1109 |
+
except Exception:
|
1110 |
+
pass
|
1111 |
+
|
1112 |
+
try:
|
1113 |
+
result = DeepFace.extract_faces(
|
1114 |
+
img_path=img_array,
|
1115 |
+
enforce_detection=False,
|
1116 |
+
detector_backend="opencv",
|
1117 |
+
align=True,
|
1118 |
+
)
|
1119 |
+
|
1120 |
+
if result and len(result) > 0:
|
1121 |
+
faces = {}
|
1122 |
+
for i, face_data in enumerate(result):
|
1123 |
+
facial_area = face_data.get("facial_area", {})
|
1124 |
+
x = facial_area.get("x", 0)
|
1125 |
+
y = facial_area.get("y", 0)
|
1126 |
+
w = facial_area.get("w", 0)
|
1127 |
+
h = facial_area.get("h", 0)
|
1128 |
+
faces[f"face_{i+1}"] = {"facial_area": (x, y, w, h), "score": 0.8}
|
1129 |
+
detection_method = "deepface_detector"
|
1130 |
+
return faces, detection_method
|
1131 |
+
except Exception:
|
1132 |
+
pass
|
1133 |
+
|
1134 |
+
if not faces:
|
1135 |
+
try:
|
1136 |
+
from deepface.detectors import MTCNN
|
1137 |
+
|
1138 |
+
detector = MTCNN()
|
1139 |
+
dets = detector.detect_faces(img_array)
|
1140 |
+
if len(dets) > 0:
|
1141 |
+
faces = {}
|
1142 |
+
detection_method = "mtcnn_fallback"
|
1143 |
+
for i, d in enumerate(dets):
|
1144 |
+
x, y, w, h = d["box"]
|
1145 |
+
faces[f"face_{i+1}"] = {
|
1146 |
+
"facial_area": (x, y, w, h),
|
1147 |
+
"score": d.get("confidence", 0.8),
|
1148 |
+
}
|
1149 |
+
return faces, detection_method
|
1150 |
+
except Exception:
|
1151 |
+
pass
|
1152 |
+
|
1153 |
+
return None, detection_method
|
1154 |
+
|
1155 |
+
|
1156 |
+
def analyze_with_models(img_array):
|
1157 |
+
results = []
|
1158 |
+
models = ["VGG-Face", "Facenet", "OpenFace", "DeepID", "ArcFace", "Dlib"]
|
1159 |
+
for model in models:
|
1160 |
+
try:
|
1161 |
+
result = DeepFace.analyze(
|
1162 |
+
img_path=img_array,
|
1163 |
+
actions=("age", "gender", "race", "emotion"),
|
1164 |
+
enforce_detection=False,
|
1165 |
+
detector_backend="retinaface",
|
1166 |
+
)
|
1167 |
+
if isinstance(result, list):
|
1168 |
+
result = result[0]
|
1169 |
+
results.append(result)
|
1170 |
+
except Exception as e:
|
1171 |
+
st.error(f"Error Analyzing With Model {model}: {str(e)}")
|
1172 |
+
return results
|
1173 |
+
|
1174 |
+
|
1175 |
+
def weighted_average_results(results):
|
1176 |
+
if not results:
|
1177 |
+
return None
|
1178 |
+
|
1179 |
+
avg_result = results[0].copy()
|
1180 |
+
weights = [1.0] * len(results)
|
1181 |
+
|
1182 |
+
for i, res in enumerate(results[1:], start=1):
|
1183 |
+
if "age" in res and "age" in avg_result:
|
1184 |
+
avg_result["age"] += res["age"] * weights[i]
|
1185 |
+
if "gender" in res and "gender" in avg_result:
|
1186 |
+
if res["gender"] == "Woman":
|
1187 |
+
avg_result["gender"] = "Woman"
|
1188 |
+
if "race" in res and "race" in avg_result:
|
1189 |
+
for key in avg_result["race"]:
|
1190 |
+
if key in res["race"]:
|
1191 |
+
avg_result["race"][key] += res["race"][key] * weights[i]
|
1192 |
+
if "emotion" in res and "emotion" in avg_result:
|
1193 |
+
for key in avg_result["emotion"]:
|
1194 |
+
if key in res["emotion"]:
|
1195 |
+
avg_result["emotion"][key] += res["emotion"][key] * weights[i]
|
1196 |
+
|
1197 |
+
total_weight = sum(weights)
|
1198 |
+
if "age" in avg_result:
|
1199 |
+
avg_result["age"] /= total_weight
|
1200 |
+
if "race" in avg_result:
|
1201 |
+
for key in avg_result["race"]:
|
1202 |
+
avg_result["race"][key] /= total_weight
|
1203 |
+
if "emotion" in avg_result:
|
1204 |
+
for key in avg_result["emotion"]:
|
1205 |
+
avg_result["emotion"][key] /= total_weight
|
1206 |
+
avg_result["dominant_emotion"] = max(avg_result["emotion"], key=avg_result["emotion"].get)
|
1207 |
+
|
1208 |
+
return avg_result
|
1209 |
+
|
1210 |
+
|
1211 |
+
def analyze_emotion_with_models(img_array):
|
1212 |
+
faces, detection_method = detect_faces_with_fallback(img_array)
|
1213 |
+
|
1214 |
+
if not faces:
|
1215 |
+
return (
|
1216 |
+
None,
|
1217 |
+
0,
|
1218 |
+
"No face was detected. Please use better lighting or adjust your face position.",
|
1219 |
+
)
|
1220 |
+
|
1221 |
+
try:
|
1222 |
+
if isinstance(faces, dict) and len(faces) > 0:
|
1223 |
+
face_key = list(faces.keys())[0]
|
1224 |
+
face = faces[face_key]
|
1225 |
+
x, y, w, h = face["facial_area"]
|
1226 |
+
|
1227 |
+
x, y = max(0, x), max(0, y)
|
1228 |
+
w = min(w, img_array.shape[1] - x)
|
1229 |
+
h = min(h, img_array.shape[0] - y)
|
1230 |
+
|
1231 |
+
if w > 0 and h > 0:
|
1232 |
+
face_img = img_array[y : y + h, x : x + w]
|
1233 |
+
else:
|
1234 |
+
face_img = img_array
|
1235 |
+
else:
|
1236 |
+
face_img = img_array
|
1237 |
+
|
1238 |
+
results = analyze_with_models(face_img)
|
1239 |
+
|
1240 |
+
if not results:
|
1241 |
+
return (
|
1242 |
+
None,
|
1243 |
+
0,
|
1244 |
+
"A face was detected, but we couldn't analyze the emotion. Please try a different angle or change your expression.",
|
1245 |
+
)
|
1246 |
+
|
1247 |
+
result_dict = weighted_average_results(results)
|
1248 |
+
if not result_dict or "emotion" not in result_dict:
|
1249 |
+
return (
|
1250 |
+
None,
|
1251 |
+
0,
|
1252 |
+
"We found your face, but couldn't process the emotion data. Please try again with better lighting.",
|
1253 |
+
)
|
1254 |
+
|
1255 |
+
dominant_emotion = result_dict["dominant_emotion"]
|
1256 |
+
confidence = result_dict["emotion"][dominant_emotion] * 100
|
1257 |
+
|
1258 |
+
if confidence < 35:
|
1259 |
+
return (
|
1260 |
+
None,
|
1261 |
+
confidence,
|
1262 |
+
"We found your face, but the confidence was too low to determine your mood. Please try a different angle or select your mood manually."
|
1263 |
+
)
|
1264 |
+
|
1265 |
+
emotion_results = {"emotions": result_dict["emotion"], "dominant_emotion": dominant_emotion}
|
1266 |
+
return emotion_results, confidence, detection_method
|
1267 |
+
|
1268 |
+
except Exception as e:
|
1269 |
+
st.error(f"Error in emotion analysis: {str(e)}")
|
1270 |
+
return (
|
1271 |
+
None,
|
1272 |
+
0,
|
1273 |
+
"An error occurred during emotion analysis. Please try again."
|
1274 |
+
)
|
1275 |
+
|
1276 |
+
|
1277 |
+
def music_recommendation():
|
1278 |
+
st.markdown(
|
1279 |
+
'<p class="subheader">Music that matches your mood</p>', unsafe_allow_html=True
|
1280 |
+
)
|
1281 |
+
st.markdown(
|
1282 |
+
'<div class="info-box">π <b>How it works:</b> Choose your mood below or take a photo so we can see how you feel. We will suggest songs that match your mood.</div>',
|
1283 |
+
unsafe_allow_html=True,
|
1284 |
+
)
|
1285 |
+
|
1286 |
+
user_prefs = load_user_preferences()
|
1287 |
+
|
1288 |
+
st.markdown("### Select Your Mood:")
|
1289 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1290 |
+
st.write("Click on how you feel right now:")
|
1291 |
+
|
1292 |
+
all_moods = {
|
1293 |
+
"happy": {"emoji": "π", "label": "Happy"},
|
1294 |
+
"sad": {"emoji": "π’", "label": "Sad"},
|
1295 |
+
"angry": {"emoji": "π ", "label": "Angry"},
|
1296 |
+
"fear": {"emoji": "π¨", "label": "Fear"},
|
1297 |
+
"neutral": {"emoji": "π", "label": "Neutral"},
|
1298 |
+
"disgust": {"emoji": "π€’", "label": "Disgust"},
|
1299 |
+
"surprise": {"emoji": "π²", "label": "Surprise"},
|
1300 |
+
}
|
1301 |
+
|
1302 |
+
mood_selected = None
|
1303 |
+
|
1304 |
+
cols_row1 = st.columns(4)
|
1305 |
+
mood_keys = list(all_moods.keys())
|
1306 |
+
|
1307 |
+
for i in range(4):
|
1308 |
+
mood = mood_keys[i]
|
1309 |
+
mood_info = all_moods[mood]
|
1310 |
+
with cols_row1[i]:
|
1311 |
+
st.markdown('<div class="mood-button">', unsafe_allow_html=True)
|
1312 |
+
if st.button(
|
1313 |
+
f"{mood_info['emoji']} {mood_info['label']}", key=f"mood_{mood}"
|
1314 |
+
):
|
1315 |
+
mood_selected = mood
|
1316 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1317 |
+
|
1318 |
+
cols_row2 = st.columns(3)
|
1319 |
+
for i in range(4, len(mood_keys)):
|
1320 |
+
col_idx = i - 4
|
1321 |
+
mood = mood_keys[i]
|
1322 |
+
mood_info = all_moods[mood]
|
1323 |
+
with cols_row2[col_idx]:
|
1324 |
+
st.markdown('<div class="mood-button">', unsafe_allow_html=True)
|
1325 |
+
if st.button(
|
1326 |
+
f"{mood_info['emoji']} {mood_info['label']}", key=f"mood_{mood}"
|
1327 |
+
):
|
1328 |
+
mood_selected = mood
|
1329 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1330 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1331 |
+
|
1332 |
+
st.markdown("<hr style='margin: 2rem 0; opacity: 0.3;'>", unsafe_allow_html=True)
|
1333 |
+
|
1334 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1335 |
+
st.markdown("### Or Take or Upload a Photo")
|
1336 |
+
st.write("Let our AI check your face to see how you're feeling.")
|
1337 |
+
|
1338 |
+
col1, col2 = st.columns([1, 1])
|
1339 |
+
|
1340 |
+
with col1:
|
1341 |
+
use_camera = st.checkbox("Use Camera", value=False)
|
1342 |
+
img_file = None
|
1343 |
+
if use_camera:
|
1344 |
+
img_file = st.camera_input("Take a photo", key="camera_input")
|
1345 |
+
|
1346 |
+
st.write("OR")
|
1347 |
+
uploaded_file = st.file_uploader("Upload a photo", type=["jpg", "jpeg", "png"])
|
1348 |
+
|
1349 |
+
with st.expander("π· Tips to help detect faces better:"):
|
1350 |
+
st.markdown(
|
1351 |
+
"""
|
1352 |
+
- Make sure your face is well-lit
|
1353 |
+
- Look directly at the camera
|
1354 |
+
- Remove sunglasses or hats
|
1355 |
+
- Keep your face in the center of the frame
|
1356 |
+
- If your face isn't detected, try a different angle
|
1357 |
+
"""
|
1358 |
+
)
|
1359 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1360 |
+
|
1361 |
+
if mood_selected:
|
1362 |
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
1363 |
+
user_prefs["emotion_history"].append(
|
1364 |
+
{
|
1365 |
+
"emotion": mood_selected,
|
1366 |
+
"confidence": 100.0,
|
1367 |
+
"timestamp": timestamp,
|
1368 |
+
"selection": "manual",
|
1369 |
+
}
|
1370 |
+
)
|
1371 |
+
|
1372 |
+
save_user_preferences(user_prefs)
|
1373 |
+
|
1374 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1375 |
+
st.markdown(
|
1376 |
+
f'<div class="emotion-card"><h3>{all_moods[mood_selected]["emoji"]} Your Selected Mood</h3>'
|
1377 |
+
f"<p>You're feeling: <strong>{mood_selected.capitalize()}</strong></p></div>",
|
1378 |
+
unsafe_allow_html=True,
|
1379 |
+
)
|
1380 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1381 |
+
|
1382 |
+
show_hindi_recommendations(mood_selected, user_prefs)
|
1383 |
+
|
1384 |
+
image_source = img_file or uploaded_file
|
1385 |
+
|
1386 |
+
if image_source is not None:
|
1387 |
+
with st.spinner("Our AI is checking your mood..."):
|
1388 |
+
img = Image.open(image_source)
|
1389 |
+
img_array = np.array(img)
|
1390 |
+
|
1391 |
+
progress_bar = st.progress(0)
|
1392 |
+
progress_bar.progress(25)
|
1393 |
+
|
1394 |
+
emotion_results, confidence, detection_method = analyze_emotion_with_models(
|
1395 |
+
img_array
|
1396 |
+
)
|
1397 |
+
progress_bar.progress(75)
|
1398 |
+
|
1399 |
+
with col2:
|
1400 |
+
st.image(img, caption="Your photo", use_container_width=True)
|
1401 |
+
|
1402 |
+
if emotion_results:
|
1403 |
+
if confidence > 25:
|
1404 |
+
dominant_emotion = emotion_results["dominant_emotion"]
|
1405 |
+
|
1406 |
+
emotion_emojis = {
|
1407 |
+
"happy": "π",
|
1408 |
+
"sad": "π’",
|
1409 |
+
"angry": "π ",
|
1410 |
+
"fear": "π¨",
|
1411 |
+
"neutral": "π",
|
1412 |
+
"disgust": "π€’",
|
1413 |
+
"surprise": "π²",
|
1414 |
+
}
|
1415 |
+
emoji = emotion_emojis.get(dominant_emotion, "")
|
1416 |
+
|
1417 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1418 |
+
st.markdown(
|
1419 |
+
f'<div class="emotion-card"><h3>{emoji} Your mood check</h3>'
|
1420 |
+
f"<p>We detected that you're feeling: <strong>{dominant_emotion.capitalize()}</strong> "
|
1421 |
+
f"(Confidence: {confidence:.1f}%)</p></div>",
|
1422 |
+
unsafe_allow_html=True,
|
1423 |
+
)
|
1424 |
+
|
1425 |
+
emotions_df = pd.DataFrame(
|
1426 |
+
{
|
1427 |
+
"Emotion": list(emotion_results["emotions"].keys()),
|
1428 |
+
"Confidence": list(emotion_results["emotions"].values()),
|
1429 |
+
}
|
1430 |
+
)
|
1431 |
+
|
1432 |
+
fig = px.bar(
|
1433 |
+
emotions_df,
|
1434 |
+
x="Emotion",
|
1435 |
+
y="Confidence",
|
1436 |
+
color="Emotion",
|
1437 |
+
title="Your emotion results.",
|
1438 |
+
labels={
|
1439 |
+
"Confidence": "Confidence %",
|
1440 |
+
"Emotion": "Emotions Detected",
|
1441 |
+
},
|
1442 |
+
)
|
1443 |
+
fig.update_layout(
|
1444 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
1445 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
1446 |
+
font=dict(family="Segoe UI, Arial, sans-serif"),
|
1447 |
+
)
|
1448 |
+
st.plotly_chart(fig, use_container_width=True)
|
1449 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1450 |
+
|
1451 |
+
progress_bar.progress(100)
|
1452 |
+
|
1453 |
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
1454 |
+
user_prefs["emotion_history"].append(
|
1455 |
+
{
|
1456 |
+
"emotion": dominant_emotion,
|
1457 |
+
"confidence": confidence,
|
1458 |
+
"timestamp": timestamp,
|
1459 |
+
"selection": "ai",
|
1460 |
+
}
|
1461 |
+
)
|
1462 |
+
|
1463 |
+
if len(user_prefs["emotion_history"]) > 50:
|
1464 |
+
user_prefs["emotion_history"] = user_prefs["emotion_history"][
|
1465 |
+
-50:
|
1466 |
+
]
|
1467 |
+
|
1468 |
+
save_user_preferences(user_prefs)
|
1469 |
+
|
1470 |
+
show_hindi_recommendations(dominant_emotion, user_prefs)
|
1471 |
+
else:
|
1472 |
+
st.warning(
|
1473 |
+
f"We found your face but the confidence ({confidence:.1f}%) "
|
1474 |
+
"is too low to determine your mood. Please try again or select manually."
|
1475 |
+
)
|
1476 |
+
else:
|
1477 |
+
if detection_method != "none":
|
1478 |
+
st.warning(
|
1479 |
+
"We found your face, but we couldn't clearly determine your mood."
|
1480 |
+
)
|
1481 |
+
else:
|
1482 |
+
st.warning(
|
1483 |
+
"We didn't detect a face in your picture. Please try again."
|
1484 |
+
)
|
1485 |
+
|
1486 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1487 |
+
st.markdown("### Let's choose your mood manually instead.")
|
1488 |
+
st.write(
|
1489 |
+
"Don't worry! You can still get great song recommendations by telling us how you feel:"
|
1490 |
+
)
|
1491 |
+
|
1492 |
+
available_emotions = list(hindi_music_recommendations.keys())
|
1493 |
+
selected_emotion = st.selectbox(
|
1494 |
+
"How are you feeling right now?",
|
1495 |
+
available_emotions,
|
1496 |
+
format_func=lambda x: x.capitalize(),
|
1497 |
+
)
|
1498 |
+
|
1499 |
+
if st.button("Get Music Recommendations", key="manual_mood"):
|
1500 |
+
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
|
1501 |
+
user_prefs["emotion_history"].append(
|
1502 |
+
{
|
1503 |
+
"emotion": selected_emotion,
|
1504 |
+
"confidence": 100.0,
|
1505 |
+
"timestamp": timestamp,
|
1506 |
+
"selection": "manual",
|
1507 |
+
}
|
1508 |
+
)
|
1509 |
+
|
1510 |
+
if len(user_prefs["emotion_history"]) > 50:
|
1511 |
+
user_prefs["emotion_history"] = user_prefs["emotion_history"][
|
1512 |
+
-50:
|
1513 |
+
]
|
1514 |
+
|
1515 |
+
save_user_preferences(user_prefs)
|
1516 |
+
|
1517 |
+
show_hindi_recommendations(selected_emotion, user_prefs)
|
1518 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1519 |
+
|
1520 |
+
|
1521 |
+
def show_hindi_recommendations(emotion, user_prefs):
|
1522 |
+
emotion_emojis = {
|
1523 |
+
"happy": "π",
|
1524 |
+
"sad": "π’",
|
1525 |
+
"angry": "π ",
|
1526 |
+
"fear": "π¨",
|
1527 |
+
"neutral": "π",
|
1528 |
+
"disgust": "π€’",
|
1529 |
+
"surprise": "π²",
|
1530 |
+
}
|
1531 |
+
emoji = emotion_emojis.get(emotion, "")
|
1532 |
+
|
1533 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1534 |
+
st.markdown(
|
1535 |
+
f"<h3>{emoji} Songs for your {emotion.capitalize()} Mood</h3>",
|
1536 |
+
unsafe_allow_html=True,
|
1537 |
+
)
|
1538 |
+
st.write(
|
1539 |
+
"Here are songs that match your current mood. Click the heart to save your favorites."
|
1540 |
+
)
|
1541 |
+
|
1542 |
+
recommendations = get_personalized_hindi_recommendations(emotion, user_prefs)
|
1543 |
+
|
1544 |
+
if not recommendations:
|
1545 |
+
st.warning("No songs were found for this mood. Please choose another one.")
|
1546 |
+
return
|
1547 |
+
|
1548 |
+
for i, song in enumerate(recommendations[:3]):
|
1549 |
+
with st.container():
|
1550 |
+
st.markdown(
|
1551 |
+
f"""
|
1552 |
+
<div class="recommendation-card">
|
1553 |
+
<h4 class="hindi-title">{song['title']}</h4>
|
1554 |
+
</div>
|
1555 |
+
""",
|
1556 |
+
unsafe_allow_html=True,
|
1557 |
+
)
|
1558 |
+
|
1559 |
+
try:
|
1560 |
+
st.video(song["url"])
|
1561 |
+
except Exception:
|
1562 |
+
st.error(f"Sorry, we couldn't load this video: {song['title']}")
|
1563 |
+
continue
|
1564 |
+
|
1565 |
+
col1, col2 = st.columns([1, 6])
|
1566 |
+
with col1:
|
1567 |
+
st.markdown('<div class="like-button">', unsafe_allow_html=True)
|
1568 |
+
if st.button("π", key=f"like_{emotion}_{i}"):
|
1569 |
+
if song not in user_prefs["liked_songs"]:
|
1570 |
+
user_prefs["liked_songs"].append(song)
|
1571 |
+
st.success("Added to your favorites!")
|
1572 |
+
save_user_preferences(user_prefs)
|
1573 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1574 |
+
|
1575 |
+
if st.button("π Find Different Songs", key=f"refresh_{emotion}"):
|
1576 |
+
st.rerun()
|
1577 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1578 |
+
|
1579 |
+
st.markdown(
|
1580 |
+
"<div class='footer'>Songs chosen based on your mood and what you like.</div>",
|
1581 |
+
unsafe_allow_html=True,
|
1582 |
+
)
|
1583 |
+
|
1584 |
+
|
1585 |
+
def get_personalized_hindi_recommendations(emotion, user_prefs):
|
1586 |
+
if emotion not in hindi_music_recommendations:
|
1587 |
+
return []
|
1588 |
+
|
1589 |
+
all_recommendations = hindi_music_recommendations[emotion].copy()
|
1590 |
+
|
1591 |
+
if not all_recommendations:
|
1592 |
+
return []
|
1593 |
+
|
1594 |
+
liked_urls = [song["url"] for song in user_prefs["liked_songs"]]
|
1595 |
+
new_recommendations = [
|
1596 |
+
song for song in all_recommendations if song["url"] not in liked_urls
|
1597 |
+
]
|
1598 |
+
previously_liked = [
|
1599 |
+
song for song in all_recommendations if song["url"] in liked_urls
|
1600 |
+
]
|
1601 |
+
|
1602 |
+
personalized = new_recommendations + previously_liked
|
1603 |
+
|
1604 |
+
if len(personalized) < 3:
|
1605 |
+
other_emotions = [e for e in hindi_music_recommendations.keys() if e != emotion]
|
1606 |
+
for other_emotion in other_emotions:
|
1607 |
+
other_songs = [
|
1608 |
+
song
|
1609 |
+
for song in hindi_music_recommendations[other_emotion]
|
1610 |
+
if song["url"] not in liked_urls
|
1611 |
+
]
|
1612 |
+
personalized.extend(other_songs)
|
1613 |
+
if len(personalized) >= 5:
|
1614 |
+
break
|
1615 |
+
|
1616 |
+
if len(new_recommendations) > 3:
|
1617 |
+
random.shuffle(new_recommendations)
|
1618 |
+
result = new_recommendations[:3]
|
1619 |
+
else:
|
1620 |
+
result = new_recommendations[:]
|
1621 |
+
if previously_liked and len(result) < 3:
|
1622 |
+
random.shuffle(previously_liked)
|
1623 |
+
result.extend(previously_liked[: 3 - len(result)])
|
1624 |
+
|
1625 |
+
return result
|
1626 |
+
|
1627 |
+
|
1628 |
+
def show_mood_history():
|
1629 |
+
st.markdown('<p class="subheader">Your Mood History</p>', unsafe_allow_html=True)
|
1630 |
+
st.markdown(
|
1631 |
+
'<div class="info-box">π <b>Your Personal Mood Tracker:</b> Watch how your feelings change over time. This helps us pick better songs for you.</div>',
|
1632 |
+
unsafe_allow_html=True,
|
1633 |
+
)
|
1634 |
+
|
1635 |
+
user_prefs = load_user_preferences()
|
1636 |
+
history = user_prefs.get("emotion_history", [])
|
1637 |
+
|
1638 |
+
if not history:
|
1639 |
+
st.info(
|
1640 |
+
"You don't have any mood history yet. Try the Music Recommendation feature to start building your profile."
|
1641 |
+
)
|
1642 |
+
return
|
1643 |
+
|
1644 |
+
df = pd.DataFrame(history)
|
1645 |
+
|
1646 |
+
df["timestamp"] = pd.to_datetime(df["timestamp"])
|
1647 |
+
|
1648 |
+
df["source"] = df.apply(
|
1649 |
+
lambda row: "Manual Selection"
|
1650 |
+
if row.get("selection") == "manual"
|
1651 |
+
else "AI Detection",
|
1652 |
+
axis=1,
|
1653 |
+
)
|
1654 |
+
|
1655 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1656 |
+
st.subheader("Your Mood Over Time")
|
1657 |
+
|
1658 |
+
fig = px.line(
|
1659 |
+
df,
|
1660 |
+
x="timestamp",
|
1661 |
+
y="confidence",
|
1662 |
+
color="emotion",
|
1663 |
+
symbol="source",
|
1664 |
+
title="Your Mood History",
|
1665 |
+
labels={
|
1666 |
+
"timestamp": "Time",
|
1667 |
+
"confidence": "Confidence %",
|
1668 |
+
"emotion": "Emotion",
|
1669 |
+
},
|
1670 |
+
)
|
1671 |
+
fig.update_layout(
|
1672 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
1673 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
1674 |
+
font=dict(family="Segoe UI, Arial, sans-serif"),
|
1675 |
+
)
|
1676 |
+
st.plotly_chart(fig, use_container_width=True)
|
1677 |
+
|
1678 |
+
emotion_counts = df["emotion"].value_counts().reset_index()
|
1679 |
+
emotion_counts.columns = ["Emotion", "Count"]
|
1680 |
+
|
1681 |
+
fig2 = px.pie(
|
1682 |
+
emotion_counts, values="Count", names="Emotion", title="Your Most Common Moods"
|
1683 |
+
)
|
1684 |
+
fig2.update_layout(
|
1685 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
1686 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
1687 |
+
font=dict(family="Segoe UI, Arial, sans-serif"),
|
1688 |
+
)
|
1689 |
+
st.plotly_chart(fig2, use_container_width=True)
|
1690 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1691 |
+
|
1692 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1693 |
+
st.subheader("Your Recent Mood Detections")
|
1694 |
+
recent_df = df.sort_values("timestamp", ascending=False).head(10)
|
1695 |
+
recent_df["Date"] = recent_df["timestamp"].dt.strftime("%Y-%m-%d")
|
1696 |
+
recent_df["Time"] = recent_df["timestamp"].dt.strftime("%H:%M:%S")
|
1697 |
+
|
1698 |
+
recent_df = recent_df.rename(
|
1699 |
+
columns={
|
1700 |
+
"emotion": "Mood",
|
1701 |
+
"confidence": "Confidence %",
|
1702 |
+
"source": "Detection Method",
|
1703 |
+
}
|
1704 |
+
)
|
1705 |
+
|
1706 |
+
st.dataframe(
|
1707 |
+
recent_df[["Date", "Time", "Mood", "Confidence %", "Detection Method"]],
|
1708 |
+
use_container_width=True,
|
1709 |
+
hide_index=True,
|
1710 |
+
)
|
1711 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1712 |
+
|
1713 |
+
|
1714 |
+
def main():
|
1715 |
+
load_css()
|
1716 |
+
|
1717 |
+
if GPU_AVAILABLE:
|
1718 |
+
st.sidebar.markdown("---")
|
1719 |
+
st.sidebar.markdown('<div class="sidebar-header">π₯ GPU Acceleration Active</div>', unsafe_allow_html=True)
|
1720 |
+
st.sidebar.info(f"Using GPU for faster processing:\n{GPU_INFO}")
|
1721 |
+
|
1722 |
+
menu = ["Home", "Music Recommendation", "Mood History"]
|
1723 |
+
|
1724 |
+
with st.sidebar:
|
1725 |
+
st.markdown(
|
1726 |
+
'<div class="sidebar-header" style="text-align: center;">Menu</div>', unsafe_allow_html=True
|
1727 |
+
)
|
1728 |
+
choice = st.selectbox("Choose a Page", menu)
|
1729 |
+
|
1730 |
+
st.markdown("---")
|
1731 |
+
st.markdown(
|
1732 |
+
'<div class="sidebar-header">How It Works</div>', unsafe_allow_html=True
|
1733 |
+
)
|
1734 |
+
|
1735 |
+
st.markdown(
|
1736 |
+
"""
|
1737 |
+
<div class="step-container">
|
1738 |
+
<div class="step-number">1</div>
|
1739 |
+
<div class="step-content">Take a photo or select your mood</div>
|
1740 |
+
</div>
|
1741 |
+
<div class="step-container">
|
1742 |
+
<div class="step-number">2</div>
|
1743 |
+
<div class="step-content">Our AI analyzes your feelings</div>
|
1744 |
+
</div>
|
1745 |
+
<div class="step-container">
|
1746 |
+
<div class="step-number">3</div>
|
1747 |
+
<div class="step-content">Get songs that match your mood</div>
|
1748 |
+
</div>
|
1749 |
+
""",
|
1750 |
+
unsafe_allow_html=True,
|
1751 |
+
)
|
1752 |
+
|
1753 |
+
st.markdown("---")
|
1754 |
+
st.markdown('<div class="sidebar-header">About</div>', unsafe_allow_html=True)
|
1755 |
+
st.write(
|
1756 |
+
"We use AI to understand your emotions and suggest music that fits your mood."
|
1757 |
+
)
|
1758 |
+
|
1759 |
+
if choice == "Home":
|
1760 |
+
st.markdown(
|
1761 |
+
'<h1 class="main-header">Music Recommendation System</h1>',
|
1762 |
+
unsafe_allow_html=True,
|
1763 |
+
)
|
1764 |
+
|
1765 |
+
st.markdown(
|
1766 |
+
"""
|
1767 |
+
<div class="info-box">
|
1768 |
+
Welcome to your personal music recommendation system! This app uses AI to detect your mood and suggest songs that match how you feel right now.
|
1769 |
+
</div>
|
1770 |
+
""",
|
1771 |
+
unsafe_allow_html=True,
|
1772 |
+
)
|
1773 |
+
|
1774 |
+
st.markdown('<div class="animated-fade">', unsafe_allow_html=True)
|
1775 |
+
st.markdown('<p class="subheader">Features</p>', unsafe_allow_html=True)
|
1776 |
+
|
1777 |
+
col1, col2 = st.columns(2)
|
1778 |
+
|
1779 |
+
with col1:
|
1780 |
+
st.markdown(
|
1781 |
+
"""
|
1782 |
+
<div class="feature-card">
|
1783 |
+
<div class="feature-icon">π</div>
|
1784 |
+
<h3>Mood Detection</h3>
|
1785 |
+
<p>Our AI detects your emotions from a single photo.</p>
|
1786 |
+
</div>
|
1787 |
+
""",
|
1788 |
+
unsafe_allow_html=True,
|
1789 |
+
)
|
1790 |
+
|
1791 |
+
st.markdown(
|
1792 |
+
"""
|
1793 |
+
<div class="feature-card">
|
1794 |
+
<div class="feature-icon">πΌ</div>
|
1795 |
+
<h3>Music Recommendations</h3>
|
1796 |
+
<p>Get song recommendations based on your mood.</p>
|
1797 |
+
</div>
|
1798 |
+
""",
|
1799 |
+
unsafe_allow_html=True,
|
1800 |
+
)
|
1801 |
+
|
1802 |
+
with col2:
|
1803 |
+
st.markdown(
|
1804 |
+
"""
|
1805 |
+
<div class="feature-card">
|
1806 |
+
<div class="feature-icon">π</div>
|
1807 |
+
<h3>Save Favorites</h3>
|
1808 |
+
<p>Choose songs you love to create your collection.</p>
|
1809 |
+
</div>
|
1810 |
+
""",
|
1811 |
+
unsafe_allow_html=True,
|
1812 |
+
)
|
1813 |
+
|
1814 |
+
st.markdown(
|
1815 |
+
"""
|
1816 |
+
<div class="feature-card">
|
1817 |
+
<div class="feature-icon">π</div>
|
1818 |
+
<h3>Track Your Moods</h3>
|
1819 |
+
<p>Watch your feelings change over time with charts.</p>
|
1820 |
+
</div>
|
1821 |
+
""",
|
1822 |
+
unsafe_allow_html=True,
|
1823 |
+
)
|
1824 |
+
|
1825 |
+
st.markdown("### Available Moods")
|
1826 |
+
mood_col1, mood_col2 = st.columns(2)
|
1827 |
+
|
1828 |
+
with mood_col1:
|
1829 |
+
st.markdown(
|
1830 |
+
"""
|
1831 |
+
- π **Happy** - Cheerful songs to boost your mood.
|
1832 |
+
- π’ **Sad** - Emotional songs for your deep moments.
|
1833 |
+
- π **Angry** - Strong tracks to match your energy.
|
1834 |
+
"""
|
1835 |
+
)
|
1836 |
+
|
1837 |
+
with mood_col2:
|
1838 |
+
st.markdown(
|
1839 |
+
"""
|
1840 |
+
- π **Neutral** - Calming songs for everyday listening.
|
1841 |
+
- π² **Surprise** - Tracks to spark your curiosity.
|
1842 |
+
- π€’ **Disgust** - Cheerful songs to lift your mood.
|
1843 |
+
"""
|
1844 |
+
)
|
1845 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1846 |
+
|
1847 |
+
col1, col2 = st.columns([1, 1])
|
1848 |
+
with col1:
|
1849 |
+
if st.button("πΌ Get Music Recommendations"):
|
1850 |
+
st.session_state.choice = "Music Recommendation"
|
1851 |
+
st.rerun()
|
1852 |
+
with col2:
|
1853 |
+
if st.button("π View Your Mood History"):
|
1854 |
+
st.session_state.choice = "Mood History"
|
1855 |
+
st.rerun()
|
1856 |
+
|
1857 |
+
elif choice == "Music Recommendation":
|
1858 |
+
music_recommendation()
|
1859 |
+
elif choice == "Mood History":
|
1860 |
+
show_mood_history()
|
1861 |
+
|
1862 |
+
|
1863 |
+
if __name__ == "__main__":
|
1864 |
+
main()
|
1865 |
+
|
haarcascade_frontalface_default.xml
ADDED
The diff for this file is too large to render.
See raw diff
|
|
requirements.txt
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# python.exe -m pip install --upgrade pip
|
2 |
+
# pip install streamlit
|
3 |
+
# pip install opencv-python
|
4 |
+
# pip install deepface
|
5 |
+
# pip install retina-face
|
6 |
+
# pip install numpy
|
7 |
+
# pip install Pillow
|
8 |
+
# pip install pandas
|
9 |
+
# pip install plotly
|
10 |
+
# pip install mtcnn
|
11 |
+
# pip install tensorflow
|
12 |
+
# pip install tf-keras
|
13 |
+
# pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
|
14 |
+
|
15 |
+
|
16 |
+
# THIS IS FOR THE CLOUD VERSION. YOU DO NOT NEED TO INSTALL THIS VERSION ON YOUR LOCAL MACHINE. IF YOU WANT TO RUN THIS MODEL ON YOUR LOCAL MACHINE, YOU CAN INSTALL THE COMMENTED VERSION ABOVE.
|
17 |
+
streamlit
|
18 |
+
opencv-python
|
19 |
+
deepface
|
20 |
+
retina-face
|
21 |
+
numpy
|
22 |
+
Pillow
|
23 |
+
pandas
|
24 |
+
plotly
|
25 |
+
mtcnn
|
26 |
+
tensorflow
|
27 |
+
tf-keras
|
28 |
+
--index-url https://download.pytorch.org/whl/cu126
|
29 |
+
torch
|
30 |
+
torchvision
|
31 |
+
torchaudio
|