import cv2
import os
import numpy as np
import threading
import time
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from deepface import DeepFace
from queue import Queue, Empty
import datetime
import logging
# Configuration
DATABASE_FOLDER = "database"
MODEL_NAME = "ArcFace"
DETECTOR_BACKEND = "ssd"
FRAME_SKIP = 4
MAX_QUEUE_SIZE = 2
SIMILARITY_THRESHOLD = 0.65
HISTORY_FOLDER = "historique"
# Email Configuration
EMAIL_ENABLED = True
EMAIL_FROM = "[email protected]"
EMAIL_PASSWORD = "uvfbgvkicjtzzpvp"
EMAIL_TO = "[email protected] , [email protected]"
EMAIL_SMTP_SERVER = "smtp.gmail.com"
EMAIL_SMTP_PORT = 587
EMAIL_COOLDOWN = 60
# RFID Configuration
RFID_ENABLED = True
RFID_PORT = "COM3"
RFID_BAUDRATE = 9600
RFID_EMERGENCY_CODES = ["1234567890", "0987654321"]
# Logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='face_recognition.log'
)
logger = logging.getLogger(__name__)
# Global variables
face_database = []
processing_queue = Queue(maxsize=MAX_QUEUE_SIZE)
current_results = []
is_running = True
frame_count = 0
fps = 0
last_fps_update = time.time()
fps_counter = 0
last_email_time = 0
unknown_faces_detected = False
def create_directories():
directories = [DATABASE_FOLDER, "unknown_faces", HISTORY_FOLDER]
for directory in directories:
if not os.path.exists(directory):
os.makedirs(directory)
def init_database():
if not os.path.exists(DATABASE_FOLDER):
os.makedirs(DATABASE_FOLDER)
files = [f for f in os.listdir(DATABASE_FOLDER) if f.lower().endswith((".jpg",
".jpeg", ".png"))]
loaded = 0
for filename in files:
path = os.path.join(DATABASE_FOLDER, filename)
try:
img = cv2.imread(path)
if img is None:
continue
if max(img.shape[0], img.shape[1]) > 800:
scale = 800 / max(img.shape[0], img.shape[1])
img = cv2.resize(img, (0, 0), fx=scale, fy=scale)
result = DeepFace.represent(
img_path=img,
model_name=MODEL_NAME,
detector_backend=DETECTOR_BACKEND,
enforce_detection=True,
align=True
)
if result:
face_database.append({
"name": os.path.splitext(filename)[0],
"embedding": result[0]["embedding"]
})
loaded += 1
except Exception as e:
logger.error(f"Error loading {filename}: {str(e)}")
logger.info(f"Loaded {loaded} faces from database")
def save_recognized_face(frame, name, face_area):
try:
if not os.path.exists(HISTORY_FOLDER):
os.makedirs(HISTORY_FOLDER)
current_time = datetime.datetime.now()
timestamp = current_time.strftime("%Y-%m-%d_%H-%M-%S")
date_folder = os.path.join(HISTORY_FOLDER, current_time.strftime("%Y-%m-
%d"))
if not os.path.exists(date_folder):
os.makedirs(date_folder)
x, y, w, h = face_area
margin_x, margin_y = int(w * 0.2), int(h * 0.2)
x1 = max(0, x - margin_x)
y1 = max(0, y - margin_y)
x2 = min(frame.shape[1], x + w + margin_x)
y2 = min(frame.shape[0], y + h + margin_y)
face_img = frame[y1:y2, x1:x2]
label = f"{name} - {timestamp}"
cv2.putText(face_img, label, (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,
255, 0), 1)
filename = f"{name}_{timestamp}.jpg"
filepath = os.path.join(date_folder, filename)
cv2.imwrite(filepath, face_img)
return True
except Exception as e:
logger.error(f"Error saving recognized face: {str(e)}")
return False
def process_frames():
global current_results, unknown_faces_detected
while is_running:
try:
frame = processing_queue.get(timeout=0.5)
faces = DeepFace.extract_faces(
img_path=frame,
detector_backend=DETECTOR_BACKEND,
enforce_detection=False,
align=True
)
results = []
unknown_detected = False
for face in faces:
if face["confidence"] < 0.9:
continue
region = face["facial_area"]
x, y, w, h = region["x"], region["y"], region["w"], region["h"]
if not face_database:
results.append((x, y, w, h, "Unknown", 0, frame))
unknown_detected = True
continue
face_img = frame[max(0, y):min(frame.shape[0], y+h),
max(0, x):min(frame.shape[1], x+w)]
if face_img.shape[0] < 20 or face_img.shape[1] < 20:
continue
try:
embedding = DeepFace.represent(
img_path=face_img,
model_name=MODEL_NAME,
detector_backend="skip",
enforce_detection=False,
align=False
)[0]["embedding"]
db_embeddings = np.array([entry["embedding"] for entry in
face_database])
names = [entry["name"] for entry in face_database]
similarities = np.dot(db_embeddings, embedding) / (
np.linalg.norm(db_embeddings, axis=1) *
np.linalg.norm(embedding)
)
if similarities.size > 0:
best_idx = np.argmax(similarities)
score = (similarities[best_idx] + 1) / 2
if score > SIMILARITY_THRESHOLD:
label = names[best_idx]
results.append((x, y, w, h, label, score, None))
else:
results.append((x, y, w, h, "Unknown", score,
frame.copy()))
unknown_detected = True
except Exception as e:
results.append((x, y, w, h, "Unknown", 0, frame.copy()))
unknown_detected = True
current_results = results
unknown_faces_detected = unknown_detected
if unknown_detected:
send_email_notification()
processing_queue.task_done()
except Empty:
pass
except Exception as e:
logger.error(f"Process frame error: {str(e)}")
def enroll_face(frame, name):
try:
faces = DeepFace.extract_faces(
img_path=frame,
detector_backend=DETECTOR_BACKEND,
enforce_detection=True,
align=True
)
if not faces or faces[0]["confidence"] < 0.9:
return False
filename = f"{name}_{int(time.time())}.jpg"
path = os.path.join(DATABASE_FOLDER, filename)
region = faces[0]["facial_area"]
x, y, w, h = region["x"], region["y"], region["w"], region["h"]
margin_x, margin_y = int(w * 0.2), int(h * 0.2)
x1 = max(0, x - margin_x)
y1 = max(0, y - margin_y)
x2 = min(frame.shape[1], x + w + margin_x)
y2 = min(frame.shape[0], y + h + margin_y)
face_img = frame[y1:y2, x1:x2]
cv2.imwrite(path, face_img)
embedding = DeepFace.represent(
img_path=face_img,
model_name=MODEL_NAME,
detector_backend=DETECTOR_BACKEND,
enforce_detection=True
)[0]["embedding"]
face_database.append({"name": name, "embedding": embedding})
return True
except Exception as e:
logger.error(f"Enrollment failed: {str(e)}")
return False
def send_email_notification():
global last_email_time
if not EMAIL_ENABLED:
return
current_time = time.time()
if current_time - last_email_time < EMAIL_COOLDOWN:
return
last_email_time = current_time
threading.Thread(target=_send_email_worker).start()
def _send_email_worker():
try:
unknown_face = None
for face in current_results:
if face[4] == "Unknown" and face[6] is not None:
unknown_face = face
break
if unknown_face is None:
return
x, y, w, h, _, _, frame = unknown_face
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = MIMEMultipart()
msg['From'] = EMAIL_FROM
msg['To'] = EMAIL_TO
msg['Subject'] = f"⚠️ SECURITY ALERT: Unknown Person Detected -
{timestamp}"
body = f"""
<html><body>
<h2>Security Alert: Unknown Person Detected</h2>
<p><b>Time:</b> {timestamp}</p>
<p>An unknown person has been detected by the security system.</p>
</body></html>
"""
msg.attach(MIMEText(body, 'html'))
alert_img = frame.copy()
cv2.rectangle(alert_img, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.putText(alert_img, "Unknown", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
(0, 0, 255), 2)
_, img_encoded = cv2.imencode('.jpg', alert_img)
img_attach = MIMEImage(img_encoded.tobytes())
img_attach.add_header('Content-Disposition', 'attachment',
filename=f'unknown_person_{timestamp}.jpg')
msg.attach(img_attach)
server = smtplib.SMTP(EMAIL_SMTP_SERVER, EMAIL_SMTP_PORT)
server.starttls()
server.login(EMAIL_FROM, EMAIL_PASSWORD)
server.send_message(msg)
server.quit()
except Exception as e:
logger.error(f"Email sending failed: {str(e)}")
def main():
global is_running, frame_count, fps, last_fps_update, fps_counter,
SIMILARITY_THRESHOLD
create_directories()
init_database()
process_thread = threading.Thread(target=process_frames, daemon=True)
process_thread.start()
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
known_persons = set()
last_recognized_person = None
try:
while True:
ret, frame = cap.read()
if not ret:
break
fps_counter += 1
now = time.time()
if now - last_fps_update >= 1.0:
fps = fps_counter
fps_counter = 0
last_fps_update = now
if frame_count % FRAME_SKIP == 0 and not processing_queue.full():
processing_queue.put(frame.copy())
current_frame_persons = set()
face_coords = {}
for result in current_results:
x, y, w, h, label, conf, _ = result
color = (0, 255, 0) if label != "Unknown" else (0, 0, 255)
cv2.rectangle(frame, (x, y), (x+w, y+h), color, 2)
text = f"{label} ({conf*100:.1f}%)" if label != "Unknown" else
"Unknown"
cv2.putText(frame, text, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
color, 2)
if label != "Unknown":
current_frame_persons.add(label)
face_coords[label] = (x, y, w, h)
# Capture logic
for person in current_frame_persons:
if person not in known_persons:
save_recognized_face(frame.copy(), person, face_coords[person])
known_persons.add(person)
last_recognized_person = person
logger.info(f"New person detected: {person}")
# Reset tracking when no faces detected
if not current_frame_persons:
known_persons.clear()
last_recognized_person = None
# UI elements
cv2.putText(frame, f"FPS: {fps}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0, 255, 0), 2)
cv2.putText(frame, f"Thresh: {SIMILARITY_THRESHOLD:.2f}", (10, 60),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
if last_recognized_person:
cv2.putText(frame, f"Current: {last_recognized_person}", (10, 120),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
if unknown_faces_detected:
cv2.putText(frame, "ALERT: Unknown face detected", (frame.shape[1]
- 300, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
cv2.imshow('Face Recognition', frame)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('e'):
name = input("Enter name: ").strip()
if name:
threading.Thread(target=enroll_face, args=(frame.copy(),
name)).start()
elif key == ord('+'):
SIMILARITY_THRESHOLD = min(0.95, SIMILARITY_THRESHOLD + 0.05)
elif key == ord('-'):
SIMILARITY_THRESHOLD = max(0.3, SIMILARITY_THRESHOLD - 0.05)
finally:
is_running = False
process_thread.join(timeout=1.0)
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
main()