| """ |
| Indian/Pakistani Food Classifier |
| A deep learning model to identify 80+ Indian and Pakistani dishes |
| """ |
|
|
| import streamlit as st |
| import tensorflow as tf |
| import numpy as np |
| from PIL import Image |
| import json |
| import os |
| import plotly.graph_objects as go |
| import pandas as pd |
| from datetime import datetime |
| import random |
|
|
| |
| st.set_page_config( |
| page_title="Pakistani & Indian Food Classifier", |
| page_icon="๐", |
| layout="wide", |
| initial_sidebar_state="expanded" |
| ) |
|
|
| |
| st.markdown(""" |
| <style> |
| /* Main container */ |
| .main { |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| } |
| |
| /* Header styling */ |
| .header-container { |
| background: linear-gradient(135deg, #006400 0%, #008000 50%, #ffffff 100%); |
| padding: 2rem; |
| border-radius: 20px; |
| margin-bottom: 2rem; |
| text-align: center; |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); |
| } |
| |
| .header-title { |
| font-size: 3rem; |
| font-weight: bold; |
| color: white; |
| margin-bottom: 0.5rem; |
| } |
| |
| .header-subtitle { |
| font-size: 1.2rem; |
| color: rgba(255,255,255,0.9); |
| } |
| |
| .pakistan-flag { |
| font-size: 2rem; |
| margin-bottom: 1rem; |
| } |
| |
| /* Card styling */ |
| .prediction-card { |
| background: white; |
| border-radius: 15px; |
| padding: 1.5rem; |
| margin: 1rem 0; |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); |
| transition: transform 0.3s; |
| } |
| |
| .prediction-card:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 6px 12px rgba(0,0,0,0.15); |
| } |
| |
| /* Top prediction styling */ |
| .top-prediction { |
| background: linear-gradient(135deg, #006400 0%, #008000 100%); |
| color: white; |
| border-radius: 15px; |
| padding: 1.5rem; |
| margin: 1rem 0; |
| text-align: center; |
| } |
| |
| .top-prediction h2 { |
| font-size: 2.5rem; |
| margin: 0; |
| } |
| |
| .confidence-score { |
| font-size: 1.2rem; |
| margin-top: 0.5rem; |
| } |
| |
| /* Other predictions */ |
| .other-prediction { |
| background: #f8f9fa; |
| border-left: 5px solid #006400; |
| border-radius: 10px; |
| padding: 1rem; |
| margin: 0.8rem 0; |
| } |
| |
| /* Sidebar styling */ |
| .sidebar-content { |
| background: #f0f2f6; |
| border-radius: 10px; |
| padding: 1rem; |
| } |
| |
| /* Button styling */ |
| .stButton > button { |
| background: linear-gradient(135deg, #006400 0%, #008000 100%); |
| color: white; |
| border: none; |
| padding: 0.5rem 2rem; |
| border-radius: 25px; |
| font-weight: bold; |
| transition: all 0.3s; |
| } |
| |
| .stButton > button:hover { |
| transform: scale(1.05); |
| box-shadow: 0 4px 8px rgba(0,0,0,0.2); |
| } |
| |
| /* Footer */ |
| .footer { |
| text-align: center; |
| padding: 2rem; |
| color: #666; |
| font-size: 0.8rem; |
| margin-top: 3rem; |
| } |
| |
| /* Success/Error messages */ |
| .success-message { |
| background: #d4edda; |
| color: #155724; |
| padding: 1rem; |
| border-radius: 10px; |
| margin: 1rem 0; |
| } |
| |
| .info-message { |
| background: #d1ecf1; |
| color: #0c5460; |
| padding: 1rem; |
| border-radius: 10px; |
| margin: 1rem 0; |
| } |
| </style> |
| """, unsafe_allow_html=True) |
|
|
| |
| |
| |
| @st.cache_resource |
| def load_model(): |
| """Load the trained model""" |
| try: |
| model = tf.keras.models.load_model('indian_food_classifier.keras') |
| return model |
| except: |
| try: |
| model = tf.keras.models.load_model('/kaggle/working/indian_food_classifier.keras') |
| return model |
| except: |
| st.error("โ ๏ธ Model file not found. Please upload 'indian_food_classifier.keras'") |
| return None |
|
|
| @st.cache_data |
| def load_class_names(): |
| """Load class names""" |
| try: |
| with open('class_names.json', 'r') as f: |
| class_names = json.load(f) |
| return class_names |
| except: |
| try: |
| with open('/kaggle/working/class_names.json', 'r') as f: |
| class_names = json.load(f) |
| return class_names |
| except: |
| st.error("โ ๏ธ class_names.json not found. Please upload the file.") |
| return None |
|
|
| def preprocess_image(image, target_size=(224, 224)): |
| """Preprocess image for model prediction""" |
| if image.mode != 'RGB': |
| image = image.convert('RGB') |
| image = image.resize(target_size) |
| img_array = np.array(image) / 255.0 |
| img_array = np.expand_dims(img_array, axis=0) |
| return img_array |
|
|
| def format_food_name(name): |
| """Format food name for display""" |
| return name.replace('_', ' ').title() |
|
|
| def create_confidence_chart(confidences, labels, top_n=5): |
| """Create an interactive confidence chart""" |
| fig = go.Figure(data=[ |
| go.Bar( |
| x=confidences[:top_n], |
| y=[format_food_name(l) for l in labels[:top_n]], |
| orientation='h', |
| marker=dict( |
| color=confidences[:top_n], |
| colorscale='Greens', |
| showscale=True, |
| colorbar=dict(title="Confidence (%)") |
| ), |
| text=[f"{c:.1f}%" for c in confidences[:top_n]], |
| textposition='outside' |
| ) |
| ]) |
| |
| fig.update_layout( |
| title="Top Predictions Confidence Score", |
| xaxis_title="Confidence (%)", |
| yaxis_title="Food Item", |
| height=400, |
| margin=dict(l=0, r=0, t=40, b=0), |
| paper_bgcolor='rgba(0,0,0,0)', |
| plot_bgcolor='rgba(0,0,0,0)' |
| ) |
| |
| return fig |
|
|
| |
| |
| |
| def main(): |
| |
| st.markdown(""" |
| <div class="header-container"> |
| <div class="pakistan-flag"> |
| ๐ต๐ฐ ๐ฎ๐ณ ๐ต๐ฐ |
| </div> |
| <div class="header-title"> |
| ๐ Pakistani & Indian Food Classifier |
| </div> |
| <div class="header-subtitle"> |
| AI-powered dish recognition for 80+ South Asian delicacies |
| </div> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| |
| with st.sidebar: |
| st.markdown("### ๐ Model Information") |
| st.info(""" |
| - **Architecture:** EfficientNetV2S |
| - **Classes:** 80 Indian/Pakistani Dishes |
| - **Accuracy:** 59.25% |
| - **Input Size:** 224x224 pixels |
| """) |
| |
| st.markdown("---") |
| st.markdown("### ๐ฝ๏ธ Popular Dishes") |
| |
| |
| popular_dishes = [ |
| "Biryani", "Nihari", "Butter Chicken", "Aloo Gobi", |
| "Samosa", "Gulab Jamun", "Naan", "Haleem", |
| "Karahi", "Seekh Kebab", "Dal Makhani", "Ras Malai" |
| ] |
| |
| for dish in random.sample(popular_dishes, min(6, len(popular_dishes))): |
| st.markdown(f"โข {dish}") |
| |
| st.markdown("---") |
| st.markdown("### ๐ How It Works") |
| st.markdown(""" |
| 1. ๐ธ Upload a clear photo of food |
| 2. ๐ค AI analyzes the image |
| 3. ๐ฏ Get top 5 predictions with confidence scores |
| 4. ๐ View detailed confidence chart |
| """) |
| |
| st.markdown("---") |
| st.markdown("### ๐ก Tips for Best Results") |
| st.markdown(""" |
| - Use well-lit photos |
| - Focus on the main dish |
| - Avoid cluttered backgrounds |
| - Single dish per photo works best |
| """) |
| |
| st.markdown("---") |
| st.markdown("Made with โค๏ธ for South Asian Cuisine") |
| |
| |
| col1, col2 = st.columns([1, 1]) |
| |
| with col1: |
| st.markdown("### ๐ค Upload Food Image") |
| uploaded_file = st.file_uploader( |
| "Choose an image...", |
| type=['jpg', 'jpeg', 'png', 'webp', 'gif'], |
| help="Upload a clear image of Pakistani or Indian food" |
| ) |
| |
| if uploaded_file is not None: |
| image = Image.open(uploaded_file) |
| |
| |
| st.markdown("#### Preview") |
| st.image(image, caption="Uploaded Image", use_container_width=True) |
| |
| |
| st.caption(f"๐ Image size: {image.size[0]} x {image.size[1]} pixels") |
| |
| with col2: |
| if uploaded_file is not None: |
| st.markdown("### ๐ Analysis Results") |
| |
| with st.spinner("๐ Analyzing your food image..."): |
| |
| model = load_model() |
| class_names = load_class_names() |
| |
| if model is not None and class_names is not None: |
| |
| processed_img = preprocess_image(image) |
| predictions = model.predict(processed_img, verbose=0)[0] |
| |
| |
| top_5_idx = np.argsort(predictions)[-5:][::-1] |
| top_5_names = [class_names[idx] for idx in top_5_idx] |
| top_5_confidences = [predictions[idx] * 100 for idx in top_5_idx] |
| |
| |
| st.markdown(f""" |
| <div class="top-prediction"> |
| <div style="font-size: 1.2rem;">๐ฅ Top Prediction</div> |
| <h2>{format_food_name(top_5_names[0])}</h2> |
| <div class="confidence-score">Confidence: {top_5_confidences[0]:.2f}%</div> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| |
| st.markdown("#### Other Possibilities") |
| |
| for i in range(1, min(5, len(top_5_names))): |
| confidence_percent = top_5_confidences[i] |
| |
| |
| if i == 1: |
| emoji = "๐ฅ" |
| elif i == 2: |
| emoji = "๐ฅ" |
| else: |
| emoji = f"{i+1}๏ธโฃ" |
| |
| st.markdown(f""" |
| <div class="other-prediction"> |
| <strong>{emoji} {format_food_name(top_5_names[i])}</strong><br> |
| <span style="color: #666;">Confidence: {confidence_percent:.2f}%</span> |
| </div> |
| """, unsafe_allow_html=True) |
| |
| |
| st.markdown("---") |
| st.markdown("### ๐ Confidence Analysis") |
| |
| fig = create_confidence_chart(top_5_confidences, top_5_names, top_n=5) |
| st.plotly_chart(fig, use_container_width=True) |
| |
| |
| st.markdown("#### Confidence Meter") |
| confidence_level = top_5_confidences[0] |
| |
| if confidence_level > 70: |
| st.success(f"๐ฏ High confidence! The AI is very sure this is {format_food_name(top_5_names[0])}") |
| elif confidence_level > 50: |
| st.warning(f"๐ค Medium confidence. The AI thinks it's {format_food_name(top_5_names[0])}") |
| else: |
| st.info(f"๐ก Low confidence. Try uploading a clearer photo for better results") |
| |
| |
| st.markdown("---") |
| |
| col1, col2, col3 = st.columns(3) |
| |
| with col1: |
| st.markdown(""" |
| ### ๐ฏ Supported Cuisines |
| - Punjabi |
| - Mughlai |
| - South Indian |
| - Sindhi |
| - Kashmiri |
| - Hyderabadi |
| """) |
| |
| with col2: |
| st.markdown(""" |
| ### ๐ Dish Categories |
| - Curries & Gravies |
| - Rice Dishes (Biryani) |
| - Breads (Naan, Roti) |
| - Desserts & Sweets |
| - Snacks & Appetizers |
| - Beverages |
| """) |
| |
| with col3: |
| st.markdown(""" |
| ### ๐ Model Performance |
| - 59.25% Top-1 Accuracy |
| - 80+ Food Classes |
| - 3,200 Training Images |
| - EfficientNetV2S Backbone |
| - Real-time Predictions |
| """) |
| |
| |
| st.markdown(""" |
| <div class="footer"> |
| <p>๐ต๐ฐ Celebrating the rich culinary heritage of Pakistan and India ๐ฎ๐ณ</p> |
| <p>โ ๏ธ Note: For best results, use clear, well-lit images of individual dishes. The model works best on traditional South Asian cuisine.</p> |
| <p>Made with Streamlit & TensorFlow | Model trained on 80+ dishes</p> |
| </div> |
| """, unsafe_allow_html=True) |
|
|
| if __name__ == "__main__": |
| main() |