Face_Detection / app.py
roseyshi's picture
Update app.py
a06423a verified
Raw
History Blame Contribute Delete
18.2 kB
# frontend/app.py (Streamlit version)
# app.py
import gradio as gr
import cv2
import numpy as np
from datetime import datetime
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import random
import time
# Global variables for demo
employees = [
{"id": 1, "name": "Alice Johnson", "department": "Engineering", "status": "present"},
{"id": 2, "name": "Bob Smith", "department": "Sales", "status": "present"},
{"id": 3, "name": "Carol White", "department": "HR", "status": "absent"},
{"id": 4, "name": "David Brown", "department": "Engineering", "status": "present"},
{"id": 5, "name": "Eve Davis", "department": "Marketing", "status": "late"},
{"id": 6, "name": "Frank Wilson", "department": "Sales", "status": "present"},
{"id": 7, "name": "Grace Lee", "department": "Engineering", "status": "absent"},
{"id": 8, "name": "Henry Kim", "department": "HR", "status": "present"},
]
# Demo detector
class DemoFaceDetector:
def __init__(self):
self.confidence = 0.95
self.faces_detected = 0
self.last_names = []
def detect(self, image):
"""Simulate face detection for demo"""
if image is None:
return [], []
# Simulate processing
self.faces_detected += 1
# Mock detection - draw boxes on image
img_copy = image.copy()
h, w = img_copy.shape[:2]
# Draw some fake face boxes
import random
faces = []
names = []
num_faces = random.randint(2, 4)
for i in range(num_faces):
# Random positions
x = random.randint(50, w-150)
y = random.randint(50, h-150)
face_w = random.randint(80, 120)
face_h = random.randint(80, 120)
# Draw rectangle
cv2.rectangle(img_copy, (x, y), (x+face_w, y+face_h), (0, 255, 0), 2)
# Get random employee
emp = random.choice(employees)
name = emp['name']
names.append(name)
# Add name label
cv2.putText(img_copy, name, (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
# Add status
status_emoji = "βœ…" if emp['status'] == 'present' else "⏰" if emp['status'] == 'late' else "❌"
cv2.putText(img_copy, status_emoji, (x+face_w-30, y+face_h-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
faces.append({'bbox': (x, y, x+face_w, y+face_h), 'name': name})
self.last_names = names
return img_copy, names
# Initialize detector
detector = DemoFaceDetector()
def get_attendance_df():
"""Get attendance as DataFrame"""
return pd.DataFrame(employees)
def process_frame(frame):
"""Process video frame for live feed"""
if frame is None:
return None
# Convert to RGB if needed
if len(frame.shape) == 2:
frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB)
# Detect faces
processed_frame, names = detector.detect(frame)
# Update status simulation
for emp in employees:
if emp['name'] in names:
emp['status'] = 'present' if random.random() > 0.1 else 'late'
return processed_frame
def create_dashboard():
"""Create analytics dashboard with Plotly"""
df = get_attendance_df()
# Create subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=(
'Attendance by Department',
'Status Distribution',
'Daily Trend',
'Department Coverage'
),
specs=[[{'type': 'bar'}, {'type': 'pie'}],
[{'type': 'scatter'}, {'type': 'bar'}]]
)
# 1. Department breakdown
dept_counts = df.groupby('department')['status'].value_counts().unstack().fillna(0)
for status in dept_counts.columns:
fig.add_trace(
go.Bar(name=status, x=dept_counts.index, y=dept_counts[status]),
row=1, col=1
)
# 2. Status distribution
status_counts = df['status'].value_counts()
fig.add_trace(
go.Pie(labels=status_counts.index, values=status_counts.values),
row=1, col=2
)
# 3. Daily trend (simulated)
dates = pd.date_range(end=datetime.now(), periods=7)
trend_data = {
'date': dates,
'present': [random.randint(5, 8) for _ in range(7)],
'absent': [random.randint(0, 3) for _ in range(7)],
'late': [random.randint(0, 2) for _ in range(7)]
}
for key in ['present', 'absent', 'late']:
fig.add_trace(
go.Scatter(x=dates, y=trend_data[key], name=key, mode='lines+markers'),
row=2, col=1
)
# 4. Department coverage
total_dept = df.groupby('department').size().reset_index(name='total')
present_dept = df[df['status'] == 'present'].groupby('department').size().reset_index(name='present')
coverage = total_dept.merge(present_dept, on='department', how='left').fillna(0)
coverage['coverage_pct'] = (coverage['present'] / coverage['total'] * 100).round(1)
fig.add_trace(
go.Bar(x=coverage['department'], y=coverage['coverage_pct'],
name='Coverage %', text=coverage['coverage_pct'], textposition='auto'),
row=2, col=2
)
fig.update_layout(height=800, showlegend=True, title_text="πŸ“Š Personnel Analytics Dashboard")
fig.update_xaxes(title_text="Department", row=2, col=2)
fig.update_yaxes(title_text="Coverage %", row=2, col=2)
return fig
def get_metrics():
"""Get dashboard metrics"""
df = get_attendance_df()
total = len(df)
present = len(df[df['status'] == 'present'])
late = len(df[df['status'] == 'late'])
absent = len(df[df['status'] == 'absent'])
return {
'total': total,
'present': present,
'late': late,
'absent': absent,
'coverage': f"{(present/total*100):.1f}%"
}
def get_missing_personnel():
"""Get list of missing employees"""
df = get_attendance_df()
absent_emps = df[df['status'] == 'absent']['name'].tolist()
late_emps = df[df['status'] == 'late']['name'].tolist()
return absent_emps, late_emps
def live_feed():
"""Gradio interface for live feed"""
return gr.Interface(
fn=process_frame,
inputs=gr.Image(source="webcam", streaming=True, label="Live Feed"),
outputs=gr.Image(label="Detection Results", type="numpy"),
title="πŸ“Ή Live Personnel Monitoring",
description="Real-time face detection and recognition",
live=True
)
def attendance_table():
"""Show attendance table"""
df = get_attendance_df()
return df
def dashboard_view():
"""Dashboard view"""
metrics = get_metrics()
# Create HTML for metrics
html = f"""
<div style="display: flex; gap: 20px; margin: 20px 0; flex-wrap: wrap;">
<div style="background: #f0f8ff; padding: 20px; border-radius: 10px; flex: 1; min-width: 150px; border-left: 4px solid #00cc66;">
<h3 style="margin: 0; color: #666;">πŸ‘₯ Present</h3>
<p style="font-size: 2em; margin: 10px 0; font-weight: bold;">{metrics['present']}</p>
<small style="color: #666;">{metrics['coverage']} coverage</small>
</div>
<div style="background: #fff8f0; padding: 20px; border-radius: 10px; flex: 1; min-width: 150px; border-left: 4px solid #ffa500;">
<h3 style="margin: 0; color: #666;">⏰ Late</h3>
<p style="font-size: 2em; margin: 10px 0; font-weight: bold;">{metrics['late']}</p>
</div>
<div style="background: #fff0f0; padding: 20px; border-radius: 10px; flex: 1; min-width: 150px; border-left: 4px solid #ff4444;">
<h3 style="margin: 0; color: #666;">❌ Absent</h3>
<p style="font-size: 2em; margin: 10px 0; font-weight: bold;">{metrics['absent']}</p>
</div>
<div style="background: #f0fff0; padding: 20px; border-radius: 10px; flex: 1; min-width: 150px; border-left: 4px solid #4488ff;">
<h3 style="margin: 0; color: #666;">πŸ“Š Coverage</h3>
<p style="font-size: 2em; margin: 10px 0; font-weight: bold;">{metrics['coverage']}</p>
</div>
</div>
"""
return html
def alerts_view():
"""Alerts and notifications"""
absent_emps, late_emps = get_missing_personnel()
html = "<div style='padding: 20px;'>"
html += "<h2>⚠️ Alerts & Notifications</h2>"
if absent_emps:
html += f"""
<div style="background: #fff0f0; padding: 15px; border-radius: 8px; margin: 10px 0; border-left: 4px solid #ff4444;">
<h3 style="margin: 0; color: #cc0000;">🚨 {len(absent_emps)} Employee(s) Absent Today</h3>
<ul>
"""
for emp in absent_emps:
html += f"<li>{emp}</li>"
html += "</ul></div>"
if late_emps:
html += f"""
<div style="background: #fff8f0; padding: 15px; border-radius: 8px; margin: 10px 0; border-left: 4px solid #ffa500;">
<h3 style="margin: 0; color: #cc8800;">⏰ {len(late_emps)} Employee(s) Arrived Late</h3>
<ul>
"""
for emp in late_emps:
html += f"<li>{emp}</li>"
html += "</ul></div>"
if not absent_emps and not late_emps:
html += """
<div style="background: #f0fff0; padding: 15px; border-radius: 8px; margin: 10px 0; border-left: 4px solid #00cc66;">
<h3 style="margin: 0; color: #00aa00;">βœ… All employees present and on time!</h3>
</div>
"""
html += f"""
<div style="margin-top: 20px; display: flex; gap: 10px;">
<button onclick="alert('πŸ“§ Notifications sent!')" style="background: #4488ff; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer;">
πŸ”” Send Notifications
</button>
<button onclick="alert('πŸ“Š Report generated!')" style="background: #44cc88; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer;">
πŸ“Š Generate Report
</button>
<span style="background: #f0f0f0; padding: 10px 20px; border-radius: 5px;">
πŸ• {datetime.now().strftime("%H:%M:%S")}
</span>
</div>
"""
html += "</div>"
return html
# Build Gradio Interface
with gr.Blocks(title="AI Personnel Monitoring Dashboard", theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# πŸ‘₯ AI-Powered Personnel Monitoring & Attendance Dashboard
Real-time face detection, attendance tracking, and workforce analytics using AI.
""")
# Tabs
with gr.Tabs():
with gr.TabItem("πŸ“Ή Live Feed"):
gr.Markdown("### πŸ“Ή Real-time Face Detection")
gr.Markdown("*Note: This demo simulates face detection. In production, this would use YOLO + Face Recognition.*")
# Live feed interface
with gr.Row():
with gr.Column(scale=2):
live_output = gr.Image(label="Detection Results", type="numpy")
with gr.Column(scale=1):
live_status = gr.JSON(label="Status Updates")
# Controls
with gr.Row():
start_btn = gr.Button("▢️ Start Camera", variant="primary")
stop_btn = gr.Button("⏹️ Stop Camera", variant="stop")
refresh_btn = gr.Button("πŸ”„ Refresh")
# Process video function
def process_video():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
# Use simulated frames
for i in range(30):
# Create simulated frame
frame = np.zeros((480, 640, 3), dtype=np.uint8)
frame[:] = (30, 30, 30)
# Add some face boxes
for j, emp in enumerate(employees[:3]):
x = 100 + j * 180
y = 150
cv2.rectangle(frame, (x, y), (x+120, y+160), (0, 255, 0), 2)
cv2.putText(frame, emp['name'].split()[0], (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
yield frame, {"frame": i, "status": "simulating"}
time.sleep(0.1)
else:
while True:
ret, frame = cap.read()
if not ret:
break
processed = process_frame(frame)
yield processed, {"timestamp": datetime.now().isoformat()}
time.sleep(0.05)
cap.release()
start_btn.click(
process_video,
outputs=[live_output, live_status]
)
with gr.TabItem("πŸ“Š Dashboard"):
gr.Markdown("### πŸ“Š Real-time Analytics Dashboard")
# Metrics row
metrics_html = gr.HTML()
# Dashboard chart
with gr.Row():
with gr.Column(scale=2):
dashboard_plot = gr.Plot(label="Analytics Dashboard")
with gr.Column(scale=1):
metrics_html_2 = gr.HTML()
# Refresh button
refresh_dashboard = gr.Button("πŸ”„ Update Dashboard")
def update_dashboard():
return create_dashboard(), dashboard_view()
refresh_dashboard.click(
update_dashboard,
outputs=[dashboard_plot, metrics_html]
)
# Initial load
gr.load(fn=create_dashboard, outputs=dashboard_plot)
gr.load(fn=dashboard_view, outputs=metrics_html)
with gr.TabItem("πŸ“‹ Attendance"):
gr.Markdown("### πŸ“‹ Today's Attendance Log")
attendance_table = gr.Dataframe(
headers=["ID", "Name", "Department", "Status"],
interactive=False
)
with gr.Row():
export_btn = gr.Button("πŸ“₯ Export CSV", variant="primary")
filter_btn = gr.Button("πŸ” Filter")
def update_attendance():
df = get_attendance_df()
return df
gr.load(fn=update_attendance, outputs=attendance_table)
with gr.TabItem("⚠️ Alerts"):
gr.Markdown("### ⚠️ Missing Personnel Alerts")
alerts_html = gr.HTML()
refresh_alerts = gr.Button("πŸ”„ Check Alerts")
refresh_alerts.click(
alerts_view,
outputs=alerts_html
)
gr.load(fn=alerts_view, outputs=alerts_html)
with gr.TabItem("πŸ“ˆ Analytics"):
gr.Markdown("### πŸ“ˆ Staffing Trends & Analytics")
# Additional analytics
with gr.Row():
with gr.Column():
dept_chart = gr.Plot(label="Department Performance")
with gr.Column():
trend_chart = gr.Plot(label="Attendance Trend")
def create_analytics():
df = get_attendance_df()
# Department performance
dept_fig = go.Figure()
dept_data = df.groupby('department').size().reset_index(name='total')
dept_present = df[df['status'] == 'present'].groupby('department').size().reset_index(name='present')
dept_merged = dept_data.merge(dept_present, on='department', how='left').fillna(0)
dept_merged['coverage'] = (dept_merged['present'] / dept_merged['total'] * 100).round(1)
dept_fig.add_trace(go.Bar(x=dept_merged['department'], y=dept_merged['coverage'],
text=dept_merged['coverage'], textposition='auto',
marker_color=dept_merged['coverage'].apply(
lambda x: '#00cc66' if x >= 80 else '#ffa500' if x >= 50 else '#ff4444'
)))
dept_fig.update_layout(title="Department Coverage %", yaxis_title="Coverage %")
# Trend chart
trend_fig = go.Figure()
dates = pd.date_range(end=datetime.now(), periods=7)
for status in ['present', 'late', 'absent']:
trend_fig.add_trace(go.Scatter(
x=dates,
y=[random.randint(2, 8) for _ in range(7)],
name=status.capitalize(),
mode='lines+markers'
))
trend_fig.update_layout(title="7-Day Attendance Trend", yaxis_title="Employees")
return dept_fig, trend_fig
gr.load(fn=create_analytics, outputs=[dept_chart, trend_chart])
# Footer
gr.Markdown("""
---
### 🎯 About This Demo
This is a **simulated demonstration** of an AI-powered attendance system.
The live feed uses simulated face detection to showcase the dashboard interface.
**Real implementation features:**
- βœ… YOLOv8 for face detection
- βœ… FaceNet/ArcFace for recognition
- βœ… PostgreSQL attendance database
- βœ… Real-time analytics and alerts
- βœ… Shift coverage and staffing trends
Built with ❀️ using Gradio, OpenCV, and Plotly
""")
# Launch the app
if __name__ == "__main__":
demo.launch()