Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import cv2 | |
| import numpy as np | |
| import sqlite3 | |
| import json | |
| import pickle | |
| import os | |
| from datetime import datetime | |
| from deepface import DeepFace | |
| from PIL import Image | |
| import io | |
| import tensorflow as tf | |
| # Suppress TensorFlow warnings | |
| tf.get_logger().setLevel('ERROR') | |
| os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' | |
| class CoffeeShopSystem: | |
| def __init__(self): | |
| self.init_database() | |
| def init_database(self): | |
| """Initialize SQLite database for customers and orders""" | |
| self.conn = sqlite3.connect('coffee_shop.db', check_same_thread=False) | |
| cursor = self.conn.cursor() | |
| # Create customers table | |
| cursor.execute(''' | |
| CREATE TABLE IF NOT EXISTS customers ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT NOT NULL, | |
| phone TEXT, | |
| face_encoding BLOB, | |
| created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| visit_count INTEGER DEFAULT 1 | |
| ) | |
| ''') | |
| # Create orders table | |
| cursor.execute(''' | |
| CREATE TABLE IF NOT EXISTS orders ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| customer_id INTEGER, | |
| order_items TEXT, | |
| total_amount REAL, | |
| order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | |
| FOREIGN KEY (customer_id) REFERENCES customers (id) | |
| ) | |
| ''') | |
| self.conn.commit() | |
| def encode_face(self, image): | |
| """Extract face embedding from image using DeepFace""" | |
| try: | |
| # Convert PIL Image to numpy array if needed | |
| if isinstance(image, Image.Image): | |
| image = np.array(image) | |
| # Save temporary image for DeepFace | |
| temp_path = "temp_face.jpg" | |
| cv2.imwrite(temp_path, cv2.cvtColor(image, cv2.COLOR_RGB2BGR)) | |
| try: | |
| # Extract face embedding using DeepFace | |
| embedding = DeepFace.represent( | |
| img_path=temp_path, | |
| model_name="Facenet", | |
| enforce_detection=True, | |
| detector_backend="mtcnn" | |
| ) | |
| # Clean up temp file | |
| if os.path.exists(temp_path): | |
| os.remove(temp_path) | |
| if embedding and len(embedding) > 0: | |
| return np.array(embedding[0]["embedding"]), None | |
| else: | |
| return None, "Could not extract face embedding" | |
| except Exception as e: | |
| # Clean up temp file | |
| if os.path.exists(temp_path): | |
| os.remove(temp_path) | |
| if "Face could not be detected" in str(e): | |
| return None, "No face detected in the image" | |
| else: | |
| return None, f"Error processing face: {str(e)}" | |
| except Exception as e: | |
| return None, f"Error processing image: {str(e)}" | |
| def find_matching_customer(self, face_embedding, threshold=0.7): | |
| """Find matching customer in database using cosine similarity""" | |
| cursor = self.conn.cursor() | |
| cursor.execute("SELECT id, name, phone, face_encoding, visit_count FROM customers") | |
| customers = cursor.fetchall() | |
| for customer in customers: | |
| stored_embedding = pickle.loads(customer[3]) | |
| # Calculate cosine similarity | |
| similarity = np.dot(face_embedding, stored_embedding) / ( | |
| np.linalg.norm(face_embedding) * np.linalg.norm(stored_embedding) | |
| ) | |
| if similarity > threshold: | |
| return { | |
| 'id': customer[0], | |
| 'name': customer[1], | |
| 'phone': customer[2], | |
| 'visit_count': customer[4], | |
| 'similarity': similarity | |
| } | |
| return None | |
| def add_new_customer(self, name, phone, face_encoding): | |
| """Add new customer to database""" | |
| cursor = self.conn.cursor() | |
| cursor.execute(''' | |
| INSERT INTO customers (name, phone, face_encoding) | |
| VALUES (?, ?, ?) | |
| ''', (name, phone, pickle.dumps(face_encoding))) | |
| customer_id = cursor.lastrowid | |
| self.conn.commit() | |
| return customer_id | |
| def update_visit_count(self, customer_id): | |
| """Update customer visit count""" | |
| cursor = self.conn.cursor() | |
| cursor.execute(''' | |
| UPDATE customers SET visit_count = visit_count + 1 | |
| WHERE id = ? | |
| ''', (customer_id,)) | |
| self.conn.commit() | |
| def get_customer_orders(self, customer_id, limit=5): | |
| """Get recent orders for a customer""" | |
| cursor = self.conn.cursor() | |
| cursor.execute(''' | |
| SELECT order_items, total_amount, order_date | |
| FROM orders | |
| WHERE customer_id = ? | |
| ORDER BY order_date DESC | |
| LIMIT ? | |
| ''', (customer_id, limit)) | |
| return cursor.fetchall() | |
| def add_order(self, customer_id, items, total): | |
| """Add new order""" | |
| cursor = self.conn.cursor() | |
| cursor.execute(''' | |
| INSERT INTO orders (customer_id, order_items, total_amount) | |
| VALUES (?, ?, ?) | |
| ''', (customer_id, json.dumps(items), total)) | |
| self.conn.commit() | |
| # Initialize the system | |
| coffee_system = CoffeeShopSystem() | |
| def process_customer_image(image, customer_name=None, customer_phone=None): | |
| """Process customer image for recognition or registration""" | |
| if image is None: | |
| return "Please capture or upload an image", "", "" | |
| # Encode the face | |
| face_encoding, error = coffee_system.encode_face(image) | |
| if error: | |
| return f"β {error}", "", "" | |
| # Try to find matching customer | |
| matching_customer = coffee_system.find_matching_customer(face_encoding) | |
| if matching_customer: | |
| # Existing customer found | |
| coffee_system.update_visit_count(matching_customer['id']) | |
| # Get recent orders | |
| recent_orders = coffee_system.get_customer_orders(matching_customer['id']) | |
| welcome_msg = f"π Welcome back, {matching_customer['name']}!\n" | |
| welcome_msg += f"π± Phone: {matching_customer['phone']}\n" | |
| welcome_msg += f"π Visit #{matching_customer['visit_count'] + 1}\n" | |
| welcome_msg += f"π― Match confidence: {matching_customer['similarity']*100:.1f}%" | |
| # Format recent orders | |
| orders_text = "π Recent Orders:\n" | |
| if recent_orders: | |
| for i, (items, total, date) in enumerate(recent_orders, 1): | |
| order_items = json.loads(items) | |
| orders_text += f"{i}. {', '.join(order_items)} - ${total:.2f} ({date[:10]})\n" | |
| else: | |
| orders_text += "No previous orders found." | |
| return welcome_msg, orders_text, "" | |
| else: | |
| # New customer - need registration | |
| if customer_name and customer_phone: | |
| # Register new customer | |
| customer_id = coffee_system.add_new_customer(customer_name, customer_phone, face_encoding) | |
| success_msg = f"β New customer registered!\n" | |
| success_msg += f"π€ Name: {customer_name}\n" | |
| success_msg += f"π± Phone: {customer_phone}\n" | |
| success_msg += f"π Customer ID: {customer_id}" | |
| return success_msg, "π Welcome to our coffee shop!", "" | |
| else: | |
| # Need customer details for registration | |
| return "π€ New customer detected!", "Please enter your name and phone number to register", "new_customer" | |
| def place_order(customer_image, items_text, total_amount): | |
| """Place an order for the recognized customer""" | |
| if not customer_image: | |
| return "Please capture customer image first" | |
| if not items_text or not total_amount: | |
| return "Please enter order items and total amount" | |
| # Encode face and find customer | |
| face_encoding, error = coffee_system.encode_face(customer_image) | |
| if error: | |
| return f"Error: {error}" | |
| matching_customer = coffee_system.find_matching_customer(face_encoding) | |
| if not matching_customer: | |
| return "Customer not found. Please register first." | |
| # Parse order items | |
| items = [item.strip() for item in items_text.split(',')] | |
| # Add order to database | |
| coffee_system.add_order(matching_customer['id'], items, float(total_amount)) | |
| order_msg = f"β Order placed for {matching_customer['name']}!\n" | |
| order_msg += f"π Items: {', '.join(items)}\n" | |
| order_msg += f"π° Total: ${total_amount}\n" | |
| order_msg += f"π Time: {datetime.now().strftime('%H:%M:%S')}" | |
| return order_msg | |
| # Create Gradio interface | |
| with gr.Blocks(title="β Coffee Shop Face Recognition System") as demo: | |
| gr.Markdown("# β Coffee Shop Face Recognition System") | |
| gr.Markdown("Take a customer photo to recognize returning customers or register new ones!") | |
| with gr.Tab("π€ Customer Recognition"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| customer_image = gr.Image( | |
| sources=["webcam", "upload"], | |
| type="pil", | |
| label="πΈ Customer Photo" | |
| ) | |
| with gr.Group(visible=True) as registration_group: | |
| gr.Markdown("### π New Customer Registration") | |
| customer_name = gr.Textbox(label="π€ Customer Name", placeholder="Enter full name") | |
| customer_phone = gr.Textbox(label="π± Phone Number", placeholder="Enter phone number") | |
| recognize_btn = gr.Button("π Recognize/Register Customer", variant="primary", size="lg") | |
| with gr.Column(): | |
| recognition_result = gr.Textbox( | |
| label="π― Recognition Result", | |
| lines=4, | |
| interactive=False | |
| ) | |
| order_history = gr.Textbox( | |
| label="π Order History", | |
| lines=6, | |
| interactive=False | |
| ) | |
| with gr.Tab("π Place Order"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| order_image = gr.Image( | |
| sources=["webcam", "upload"], | |
| type="pil", | |
| label="πΈ Customer Photo for Order" | |
| ) | |
| order_items = gr.Textbox( | |
| label="β Order Items", | |
| placeholder="Latte, Croissant, Americano", | |
| lines=2 | |
| ) | |
| order_total = gr.Number( | |
| label="π° Total Amount ($)", | |
| value=0.0, | |
| minimum=0 | |
| ) | |
| place_order_btn = gr.Button("π Place Order", variant="primary", size="lg") | |
| with gr.Column(): | |
| order_result = gr.Textbox( | |
| label="π Order Result", | |
| lines=6, | |
| interactive=False | |
| ) | |
| with gr.Tab("π System Info"): | |
| gr.Markdown(""" | |
| ### π Features: | |
| - **Face Recognition**: Automatically identify returning customers using FaceNet deep learning | |
| - **Customer Database**: Store customer info and preferences | |
| - **Order History**: Track previous purchases | |
| - **Real-time Processing**: Instant recognition and registration | |
| ### π Privacy: | |
| - Face data is stored as mathematical encodings (not actual photos) | |
| - All data stays local to your deployment | |
| - Customers can opt-out anytime | |
| ### π‘ How to Use: | |
| 1. **New Customer**: Take photo β Enter name/phone β Register | |
| 2. **Returning Customer**: Take photo β System recognizes automatically | |
| 3. **Place Order**: Take photo β Enter items β Confirm order | |
| ### π― Accuracy: | |
| - Face matching threshold: 70% similarity | |
| - Uses FaceNet deep learning model | |
| - Works in various lighting conditions | |
| - Handles glasses, hats, and minor appearance changes | |
| ### π§ Technical Details: | |
| - **Model**: FaceNet with MTCNN face detection | |
| - **Similarity**: Cosine similarity matching | |
| - **Database**: SQLite for customer and order storage | |
| - **Performance**: ~2-3 seconds per recognition | |
| """) | |
| # Event handlers | |
| recognize_btn.click( | |
| fn=process_customer_image, | |
| inputs=[customer_image, customer_name, customer_phone], | |
| outputs=[recognition_result, order_history] | |
| ) | |
| place_order_btn.click( | |
| fn=place_order, | |
| inputs=[order_image, order_items, order_total], | |
| outputs=[order_result] | |
| ) | |
| # Launch the app | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=True | |
| ) |