import gradio as gr import numpy as np import pandas as pd import matplotlib.pyplot as plt import torch from transformers import AutoImageProcessor, AutoModelForImageClassification from PIL import Image from difflib import get_close_matches from typing import Optional, Dict, Any # ------------------------------------------------- # Configuration # ------------------------------------------------- # ------------------------------------------------- # Load Food Data from CSV # ------------------------------------------------- def load_food_data(): """Load and prepare food data from CSV""" csv_filepath = '/content/food_carb_data1 - food_carb_data1 (1) (1).csv' # Explicit filepath try: food_data = pd.read_csv(csv_filepath) # Normalize column names to lowercase and remove spaces food_data.columns = [col.lower().replace(' ', '') for col in food_data.columns] # Normalize food_name column to lowercase: Crucial for matching if 'food_name' in food_data.columns: #Safety check if food_name exists food_data['food_name'] = food_data['food_name'].str.lower() else: print("Warning: 'food_name' column not found in CSV.") return pd.DataFrame({ 'food_category': ['starch'], 'food_subcategory': ['bread'], 'food_name': ['white bread'], # lowercase default 'serving_description': ['servingsize'], 'serving_amount': [29], 'serving_unit': ['g'], 'carbohydrate_grams': [15], 'notes': ['default'] }) #Print first 5 rows to check columns and values print("First 5 rows of loaded data:") print(food_data.head()) return food_data except FileNotFoundError: print(f"Error: CSV file not found at {csv_filepath}") return pd.DataFrame({ 'food_category': ['starch'], 'food_subcategory': ['bread'], 'food_name': ['white bread'], # lowercase default 'serving_description': ['servingsize'], 'serving_amount': [29], 'serving_unit': ['g'], 'carbohydrate_grams': [15], 'notes': ['default'] }) except Exception as e: print(f"Error loading CSV: {e}") # Provide minimal default data in case of error return pd.DataFrame({ 'food_category': ['starch'], 'food_subcategory': ['bread'], 'food_name': ['white bread'], # lowercase default 'serving_description': ['servingsize'], 'serving_amount': [29], 'serving_unit': ['g'], 'carbohydrate_grams': [15], 'notes': ['default'] }) food_data = load_food_data() # ------------------------------------------------- # Load Food Classification Model # ------------------------------------------------- processor = AutoImageProcessor.from_pretrained("therealcyberlord/vit-indian-food") model = AutoModelForImageClassification.from_pretrained("therealcyberlord/vit-indian-food") def classify_food(image): """Classify food image using the pre-trained model""" if isinstance(image, np.ndarray): image = Image.fromarray(image) image = processor(images=image, return_tensors="pt") with torch.no_grad(): outputs = model(**image) predicted_idx = torch.argmax(outputs.logits, dim=-1).item() food_name = model.config.id2label.get(predicted_idx, "Unknown Food") return food_name.lower() #Convert classification to lowercase # ------------------------------------------------- # USDA API Integration - REMOVED for local HF Spaces deployment # ------------------------------------------------- def get_food_nutrition(food_name: str, portion_size: float = 1.0) -> Optional[Dict[str, Any]]: """Get carbohydrate content for the given food""" #No USDA anymore try: # First try the local CSV database food_name_lower = food_name.lower() # Ensure input is also lowercase food_names = food_data['food_name'].str.lower().tolist() #Already lowercased during load print(f"Searching for: {food_name_lower}") # Debugging: What are we searching for? matches = get_close_matches(food_name_lower, food_names, n=1, cutoff=0.5) if matches: # Use local database match matched_row = food_data[food_data['food_name'].str.lower() == matches[0]] if not matched_row.empty: row = matched_row.iloc[0] # Debugging: Print the entire row print(f"Matched row from CSV: {row}") # Explicitly check for column existence and valid data carb_col = 'carbohydrate_grams' amount_col = 'serving_amount' unit_col = 'serving_unit' if carb_col not in row or pd.isna(row[carb_col]): print(f"Warning: '{carb_col}' is missing or NaN in CSV") base_carbs = 0.0 else: base_carbs = row[carb_col] try: base_carbs = float(base_carbs) # Ensure it's a float except ValueError: print(f"Warning: '{carb_col}' is not a valid number in CSV") base_carbs = 0.0 if amount_col not in row or unit_col not in row or pd.isna(row[amount_col]) or pd.isna(row[unit_col]): serving_size = "Unknown" print(f"Warning: '{amount_col}' or '{unit_col}' is missing in CSV") else: serving_size = f"{row[amount_col]} {row[unit_col]}" adjusted_carbs = base_carbs * portion_size return { 'matched_food': row['food_name'], 'category': row['food_category'] if 'food_category' in row and not pd.isna(row['food_category']) else 'Unknown', 'subcategory': row['food_subcategory'] if 'food_subcategory' in row and not pd.isna(row['food_subcategory']) else 'Unknown', 'base_carbs': base_carbs, 'adjusted_carbs': adjusted_carbs, 'serving_size': serving_size, 'portion_multiplier': portion_size, 'notes': row['notes'] if 'notes' in row and not pd.isna(row['notes']) else '' } # If no match found in local database print(f"No match found in CSV for {food_name}") # Debugging line print(f"No nutrition information found for {food_name} in the local database.") # Debugging line return None except Exception as e: print(f"Error in get_food_nutrition: {e}") return None # ------------------------------------------------- # Insulin and Glucose Calculations # ------------------------------------------------- def calculate_insulin_needs(carbs, glucose_current, glucose_target, tdd, weight): """Calculate insulin needs for Type 1 diabetes""" if tdd <= 0: return { 'error': 'Total Daily Dose (TDD) must be greater than 0' } # Calculate ratios icr = 500 / tdd # Insulin to Carb Ratio isf = 1800 / tdd # Insulin Sensitivity Factor # Calculate correction dose glucose_difference = glucose_current - glucose_target correction_dose = glucose_difference / isf # Calculate carb dose carb_dose = carbs / icr # Calculate total bolus total_bolus = max(0, carb_dose + correction_dose) # Calculate basal basal_dose = weight * 0.5 return { 'icr': round(icr, 2), 'isf': round(isf, 2), 'correction_dose': round(correction_dose, 2), 'carb_dose': round(carb_dose, 2), 'total_bolus': round(total_bolus, 2), 'basal_dose': round(basal_dose, 2) } def create_detailed_report(nutrition_info, insulin_info): """Create a detailed report of carbs and insulin calculations""" carb_details = f""" FOOD DETAILS: ------------- Detected Food: {nutrition_info['matched_food']} Category: {nutrition_info['category']} Subcategory: {nutrition_info['subcategory']} CARBOHYDRATE INFORMATION: ------------------------ Standard Serving Size: {nutrition_info['serving_size']} Carbs per Serving: {nutrition_info['base_carbs']}g Portion Multiplier: {nutrition_info['portion_multiplier']}x Total Carbs: {nutrition_info['adjusted_carbs']}g Notes: {nutrition_info['notes']} """ insulin_details = f""" INSULIN CALCULATIONS: -------------------- ICR (Insulin to Carb Ratio): 1:{insulin_info['icr']} ISF (Insulin Sensitivity Factor): 1:{insulin_info['isf']} RECOMMENDED DOSES: ----------------- Correction Dose: {insulin_info['correction_dose']} units Carb Dose: {insulin_info['carb_dose']} units Total Bolus: {insulin_info['total_bolus']} units Daily Basal: {insulin_info['basal_dose']} units """ return carb_details, insulin_details # ------------------------------------------------- # Main Dashboard Function # ------------------------------------------------- def diabetes_dashboard(initial_glucose, food_image, stress_level, sleep_hours, time_hours, weight, tdd, target_glucose, exercise_duration, exercise_intensity, portion_size): """Main dashboard function""" try: # 1. Food Classification and Carb Calculation food_name = classify_food(food_image) # This line is now inside the function print(f"Classified food name: {food_name}") # Debugging: What is classified as? # Corrected indentation nutrition_info = get_food_nutrition(food_name, portion_size) if not nutrition_info: # Try with generic categories if specific food not found generic_terms = food_name.split() for term in generic_terms: nutrition_info = get_food_nutrition(term, portion_size) if nutrition_info: break if not nutrition_info: return ( f"Could not find nutrition information for: {food_name} in the local database", # Removed USDA ref "No insulin calculations available", None, None, None ) # 2. Insulin Calculations insulin_info = calculate_insulin_needs( nutrition_info['adjusted_carbs'], initial_glucose, target_glucose, tdd, weight ) if 'error' in insulin_info: return insulin_info['error'], None, None, None, None # 3. Create detailed reports carb_details, insulin_details = create_detailed_report(nutrition_info, insulin_info) # 4. Glucose Prediction hours = list(range(time_hours)) glucose_levels = [] current_glucose = initial_glucose for t in hours: # Factor in carbs effect (peaks at 1-2 hours) carb_effect = nutrition_info['adjusted_carbs'] * 0.1 * np.exp(-(t-1.5)**2/2) # Factor in insulin effect (peaks at 2-3 hours) insulin_effect = insulin_info['total_bolus'] * 2 * np.exp(-(t-2.5)**2/2) # Add stress effect stress_effect = stress_level * 2 # Add sleep effect sleep_effect = abs(8 - sleep_hours) * 5 # Add exercise effect exercise_effect = (exercise_duration/60) * exercise_intensity * 2 # Calculate glucose with all factors glucose = (current_glucose + carb_effect - insulin_effect + stress_effect + sleep_effect - exercise_effect) glucose_levels.append(max(70, min(400, glucose))) current_glucose = glucose_levels[-1] # 5. Create visualization fig, ax = plt.subplots(figsize=(12, 6)) ax.plot(hours, glucose_levels, 'b-', label='Predicted Glucose') ax.axhline(y=target_glucose, color='g', linestyle='--', label='Target') ax.fill_between(hours, [70]*len(hours), [180]*len(hours), alpha=0.1, color='g', label='Target Range') ax.set_ylabel('Glucose (mg/dL)') ax.set_xlabel('Hours') ax.set_title('Predicted Blood Glucose Over Time') ax.legend() ax.grid(True) return ( carb_details, insulin_details, insulin_info['basal_dose'], insulin_info['total_bolus'], fig ) except Exception as e: return f"Error: {str(e)}", None, None, None, None # ------------------------------------------------- # Gradio Interface Setup # ------------------------------------------------- app = gr.Interface( fn=diabetes_dashboard, inputs=[ gr.Number(label="Current Blood Glucose (mg/dL)", value=120), gr.Image(label="Food Image"), gr.Slider(1, 10, step=1, label="Stress Level (1-10)", value=1), gr.Number(label="Sleep Hours", value=7), gr.Slider(1, 24, step=1, label="Prediction Time (hours)", value=6), gr.Number(label="Weight (kg)", value=70), gr.Number(label="Total Daily Dose (TDD) of insulin", value=40), gr.Number(label="Target Blood Glucose (mg/dL)", value=100), gr.Number(label="Exercise Duration (minutes)", value=0), gr.Slider(1, 10, step=1, label="Exercise Intensity (1-10)", value=1), gr.Slider(0.1, 3, step=0.1, label="Portion Size Multiplier", value=1.0) ], outputs=[ gr.Textbox(label="Carbohydrate Details", lines=10), gr.Textbox(label="Insulin Calculation Details", lines=10), gr.Number(label="Basal Insulin Dose (units/day)"), gr.Number(label="Bolus Insulin Dose (units)"), gr.Plot(label="Glucose Prediction") ], title="Type 1 Diabetes Management Dashboard", description=""" Upload a food image to calculate carbohydrates and insulin doses. The dashboard will provide detailed information about: - Food classification and carbohydrate content - Recommended insulin doses (basal and bolus) - Predicted blood glucose levels over time Note: This tool uses a local database for food recognition. Make sure 'food_carb_data1 - food_carb_data1 (1).csv' is in the /content/ directory. """ ) if __name__ == "__main__": app.launch()