Update app.py
Browse files
app.py
CHANGED
@@ -1,115 +1,207 @@
|
|
1 |
-
import
|
2 |
import cv2
|
3 |
-
import sqlite3
|
4 |
import numpy as np
|
5 |
-
import streamlit as st
|
6 |
from datetime import datetime
|
7 |
-
|
|
|
|
|
8 |
from PIL import Image
|
|
|
|
|
|
|
9 |
|
10 |
-
#
|
11 |
-
|
12 |
-
conn = sqlite3.connect("face_records.db")
|
13 |
-
c = conn.cursor()
|
14 |
-
c.execute('''CREATE TABLE IF NOT EXISTS records (
|
15 |
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
16 |
-
name TEXT,
|
17 |
-
timestamp TEXT
|
18 |
-
)''')
|
19 |
-
conn.commit()
|
20 |
-
conn.close()
|
21 |
|
22 |
-
#
|
23 |
-
def
|
24 |
-
conn = sqlite3.connect(
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
conn.commit()
|
28 |
conn.close()
|
29 |
|
30 |
-
|
|
|
|
|
|
|
31 |
def load_emotion_model():
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
os.
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
# Show recent records
|
83 |
-
def show_recent_records():
|
84 |
-
st.header("Recent Records")
|
85 |
-
conn = sqlite3.connect("face_records.db")
|
86 |
-
c = conn.cursor()
|
87 |
-
c.execute("SELECT * FROM records ORDER BY timestamp DESC LIMIT 10")
|
88 |
-
records = c.fetchall()
|
89 |
-
conn.close()
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
else:
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
menu = ["Home", "Register Face", "View
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
st.write("
|
105 |
-
|
106 |
-
|
107 |
-
if
|
108 |
-
|
109 |
-
|
|
|
110 |
else:
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
import cv2
|
|
|
3 |
import numpy as np
|
|
|
4 |
from datetime import datetime
|
5 |
+
import torch
|
6 |
+
from facenet_pytorch import MTCNN, InceptionResnetV1
|
7 |
+
from keras.models import load_model
|
8 |
from PIL import Image
|
9 |
+
import sqlite3
|
10 |
+
import os
|
11 |
+
import tempfile
|
12 |
|
13 |
+
# SQLite Database
|
14 |
+
DB_NAME = "emotion_detection.db"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
+
# Initialize SQLite Database
|
17 |
+
def initialize_database():
|
18 |
+
conn = sqlite3.connect(DB_NAME)
|
19 |
+
cursor = conn.cursor()
|
20 |
+
cursor.execute("""
|
21 |
+
CREATE TABLE IF NOT EXISTS face_data (
|
22 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
23 |
+
name TEXT NOT NULL,
|
24 |
+
emotion TEXT NOT NULL,
|
25 |
+
timestamp TEXT NOT NULL
|
26 |
+
)
|
27 |
+
""")
|
28 |
conn.commit()
|
29 |
conn.close()
|
30 |
|
31 |
+
initialize_database()
|
32 |
+
|
33 |
+
# Load the emotion detection model
|
34 |
+
@st.cache_resource
|
35 |
def load_emotion_model():
|
36 |
+
model = load_model('CNN_Model_acc_75.h5') # Ensure the file exists
|
37 |
+
return model
|
38 |
+
|
39 |
+
emotion_model = load_emotion_model()
|
40 |
+
emotion_labels = ['angry', 'fear', 'happy', 'neutral', 'sad', 'surprise']
|
41 |
+
|
42 |
+
# Initialize FaceNet model and MTCNN
|
43 |
+
facenet = InceptionResnetV1(pretrained='vggface2').eval()
|
44 |
+
mtcnn = MTCNN()
|
45 |
+
|
46 |
+
# Directory for known faces
|
47 |
+
KNOWN_FACES_DIR = "known_faces"
|
48 |
+
if not os.path.exists(KNOWN_FACES_DIR):
|
49 |
+
os.makedirs(KNOWN_FACES_DIR)
|
50 |
+
|
51 |
+
# Load known faces and embeddings
|
52 |
+
known_faces = []
|
53 |
+
known_names = []
|
54 |
+
|
55 |
+
def load_known_faces():
|
56 |
+
for image_name in os.listdir(KNOWN_FACES_DIR):
|
57 |
+
if image_name.endswith(('.jpg', '.jpeg', '.png')):
|
58 |
+
image_path = os.path.join(KNOWN_FACES_DIR, image_name)
|
59 |
+
image = Image.open(image_path).convert("RGB")
|
60 |
+
face, _ = mtcnn.detect(image)
|
61 |
+
|
62 |
+
if face is not None:
|
63 |
+
face_box = face[0].astype(int)
|
64 |
+
cropped_face = image.crop((face_box[0], face_box[1], face_box[2], face_box[3]))
|
65 |
+
cropped_face = cropped_face.resize((160, 160))
|
66 |
+
face_tensor = np.array(cropped_face).transpose(2, 0, 1) / 255.0
|
67 |
+
face_tensor = torch.tensor(face_tensor, dtype=torch.float32).unsqueeze(0)
|
68 |
+
|
69 |
+
with torch.no_grad():
|
70 |
+
embedding = facenet(face_tensor).numpy()
|
71 |
+
|
72 |
+
known_faces.append(embedding)
|
73 |
+
known_names.append(image_name.split('.')[0])
|
74 |
+
|
75 |
+
load_known_faces()
|
76 |
+
|
77 |
+
def recognize_face(embedding):
|
78 |
+
min_distance = float('inf')
|
79 |
+
name = "Unknown"
|
80 |
+
for idx, known_embedding in enumerate(known_faces):
|
81 |
+
distance = np.linalg.norm(known_embedding - embedding)
|
82 |
+
if distance < min_distance and distance < 0.6: # Threshold
|
83 |
+
min_distance = distance
|
84 |
+
name = known_names[idx]
|
85 |
+
return name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
|
87 |
+
def process_frame(frame):
|
88 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
89 |
+
faces, _ = mtcnn.detect(frame_rgb)
|
90 |
+
result_text = ""
|
91 |
+
|
92 |
+
if faces is not None:
|
93 |
+
for face_box in faces:
|
94 |
+
x1, y1, x2, y2 = map(int, face_box)
|
95 |
+
cropped_face = frame_rgb[y1:y2, x1:x2]
|
96 |
+
resized_face = cv2.resize(cropped_face, (48, 48))
|
97 |
+
face_normalized = resized_face / 255.0
|
98 |
+
face_array = np.expand_dims(face_normalized, axis=0)
|
99 |
+
|
100 |
+
# Emotion prediction
|
101 |
+
predictions = emotion_model.predict(face_array)
|
102 |
+
emotion = emotion_labels[np.argmax(predictions[0])]
|
103 |
+
|
104 |
+
# Face recognition
|
105 |
+
cropped_face_for_recognition = cv2.resize(cropped_face, (160, 160))
|
106 |
+
face_tensor = np.array(cropped_face_for_recognition).transpose(2, 0, 1) / 255.0
|
107 |
+
face_tensor = torch.tensor(face_tensor, dtype=torch.float32).unsqueeze(0)
|
108 |
+
|
109 |
+
with torch.no_grad():
|
110 |
+
face_embedding = facenet(face_tensor).numpy()
|
111 |
+
|
112 |
+
name = recognize_face(face_embedding)
|
113 |
+
|
114 |
+
# Save record in SQLite
|
115 |
+
if name != "Unknown":
|
116 |
+
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
117 |
+
conn = sqlite3.connect(DB_NAME)
|
118 |
+
cursor = conn.cursor()
|
119 |
+
cursor.execute("""
|
120 |
+
INSERT INTO face_data (name, emotion, timestamp)
|
121 |
+
VALUES (?, ?, ?)
|
122 |
+
""", (name, emotion, timestamp))
|
123 |
+
conn.commit()
|
124 |
+
conn.close()
|
125 |
+
|
126 |
+
# Display result
|
127 |
+
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
128 |
+
result_text = f"{name} is feeling {emotion}"
|
129 |
+
cv2.putText(frame, result_text, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
|
130 |
else:
|
131 |
+
result_text = "No face detected!"
|
132 |
+
|
133 |
+
return frame, result_text
|
134 |
+
|
135 |
+
# Sidebar menu
|
136 |
+
menu = st.sidebar.selectbox("Menu", ["Home", "Register New Face", "View Records"])
|
137 |
+
|
138 |
+
if menu == "Home":
|
139 |
+
st.title("Emotion Detection")
|
140 |
+
st.write("Choose input source to start detection.")
|
141 |
+
upload_choice = st.radio("Choose Input Source", ["Camera", "Upload Image", "Upload Video"])
|
142 |
+
|
143 |
+
if upload_choice == "Camera":
|
144 |
+
cap = cv2.VideoCapture(0)
|
145 |
+
if not cap.isOpened():
|
146 |
+
st.error("Unable to access the camera.")
|
147 |
else:
|
148 |
+
while True:
|
149 |
+
ret, frame = cap.read()
|
150 |
+
if not ret:
|
151 |
+
break
|
152 |
+
frame, result_text = process_frame(frame)
|
153 |
+
st.image(frame, channels="BGR")
|
154 |
+
st.write(result_text)
|
155 |
+
cap.release()
|
156 |
+
|
157 |
+
elif upload_choice == "Upload Image":
|
158 |
+
uploaded_image = st.file_uploader("Upload Image", type=["png", "jpg", "jpeg"])
|
159 |
+
if uploaded_image:
|
160 |
+
image = Image.open(uploaded_image)
|
161 |
+
frame = np.array(image)
|
162 |
+
frame, result_text = process_frame(frame)
|
163 |
+
st.image(frame)
|
164 |
+
st.write(result_text)
|
165 |
+
|
166 |
+
elif upload_choice == "Upload Video":
|
167 |
+
uploaded_video = st.file_uploader("Upload Video", type=["mp4", "mov", "avi"])
|
168 |
+
if uploaded_video:
|
169 |
+
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
170 |
+
temp_file.write(uploaded_video.read())
|
171 |
+
video_source = cv2.VideoCapture(temp_file.name)
|
172 |
+
while video_source.isOpened():
|
173 |
+
ret, frame = video_source.read()
|
174 |
+
if not ret:
|
175 |
+
break
|
176 |
+
frame, result_text = process_frame(frame)
|
177 |
+
st.image(frame, channels="BGR")
|
178 |
+
st.write(result_text)
|
179 |
+
video_source.release()
|
180 |
+
|
181 |
+
elif menu == "Register New Face":
|
182 |
+
st.title("Register New Face")
|
183 |
+
name = st.text_input("Enter Name")
|
184 |
+
if st.button("Capture Image"):
|
185 |
+
cap = cv2.VideoCapture(0)
|
186 |
+
if not cap.isOpened():
|
187 |
+
st.error("Unable to access the camera.")
|
188 |
+
else:
|
189 |
+
ret, frame = cap.read()
|
190 |
+
if ret:
|
191 |
+
image_path = os.path.join(KNOWN_FACES_DIR, f"{name}.jpg")
|
192 |
+
cv2.imwrite(image_path, frame)
|
193 |
+
st.success(f"Face registered successfully for {name}")
|
194 |
+
load_known_faces() # Refresh known faces
|
195 |
+
cap.release()
|
196 |
+
|
197 |
+
elif menu == "View Records":
|
198 |
+
st.title("View Records")
|
199 |
+
st.subheader("Recent Records")
|
200 |
+
conn = sqlite3.connect(DB_NAME)
|
201 |
+
cursor = conn.cursor()
|
202 |
+
cursor.execute("SELECT name, emotion, timestamp FROM face_data ORDER BY timestamp DESC LIMIT 5")
|
203 |
+
records = cursor.fetchall()
|
204 |
+
conn.close()
|
205 |
+
|
206 |
+
for record in records:
|
207 |
+
st.write(f"**Name**: {record[0]}, **Emotion**: {record[1]}, **Timestamp**: {record[2]}")
|