Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import tensorflow as tf | |
| import numpy as np | |
| from PIL import Image | |
| import json | |
| from datetime import datetime | |
| import tempfile | |
| import os | |
| from gtts import gTTS | |
| from fpdf import FPDF | |
| from health_logic import generate_advice | |
| from tensorflow.keras.applications.efficientnet import preprocess_input as efficientnet_preprocess | |
| # Load model | |
| model = tf.keras.models.load_model("food_vision_model.keras") | |
| class_names = [ | |
| "akara", "banga_soup", "egusi_soup", "jollof_rice", "moi_moi", | |
| "nkwobi", "okpa", "suya", "tuwo", "yam_porridge" | |
| ] | |
| # Load food metadata | |
| with open("food_info.json", "r") as f: | |
| food_info = json.load(f) | |
| # Language map for TTS | |
| LANG_CODE = { | |
| "English": "en", | |
| "Hausa": "en", # gTTS doesn't support Hausa properly | |
| "Yoruba": "en", | |
| "Igbo": "en" | |
| } | |
| # Risk level helper | |
| def get_risk_level(score): | |
| if score <= 30: | |
| return "π’ Low Risk" | |
| elif score <= 70: | |
| return "π‘ Medium Risk" | |
| else: | |
| return "π΄ High Risk" | |
| # PDF class with Unicode support | |
| class UnicodePDF(FPDF): | |
| def header(self): | |
| self.set_font("DejaVu", "B", 16) | |
| self.cell(0, 10, "HoodHealth Pro+ Report", ln=True) | |
| def footer(self): | |
| self.set_y(-15) | |
| self.set_font("DejaVu", "I", 8) | |
| self.cell(0, 10, f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}", align="C") | |
| def generate_pdf(info, advice, risk_score, risk_level, flags, conditions, lang, display_name, image_path): | |
| pdf = UnicodePDF() | |
| pdf.add_font("DejaVu", "", "DejaVuSans.ttf", uni=True) | |
| pdf.add_font("DejaVu", "B", "DejaVuSans.ttf", uni=True) | |
| pdf.add_font("DejaVu", "I", "DejaVuSans.ttf", uni=True) | |
| pdf.add_page() | |
| pdf.set_font("DejaVu", "", 12) | |
| pdf.image(image_path, x=10, y=40, w=60) | |
| pdf.set_xy(75, 40) | |
| pdf.multi_cell(0, 10, f"Food: {display_name}\nEthnicity: {info['ethnicity']}\nIngredients: {info['ingredients']}") | |
| pdf.ln(35) | |
| pdf.multi_cell(0, 10, f"Calories: {info['calories']} kcal\nCarbs: {info['carbs']}g\nProtein: {info['protein']}g\nFat: {info['fat']}g") | |
| pdf.ln(5) | |
| pdf.multi_cell(0, 10, f"Diet Type: {info['diet_type']}\nSubstitute: {info.get('substitute', 'None')}") | |
| pdf.ln(10) | |
| pdf.set_font("DejaVu", "B", 14) | |
| pdf.cell(0, 10, "Health Analysis", ln=True) | |
| pdf.set_font("DejaVu", "", 12) | |
| pdf.multi_cell(0, 10, f"Conditions: {', '.join(conditions)}\nRisk Score: {risk_score}% ({risk_level})\nRisk Factors: {', '.join(flags) if flags else 'None'}\nAdvice: {advice}") | |
| pdf.ln(5) | |
| pdf.set_font("DejaVu", "I", 10) | |
| pdf.multi_cell(0, 10, "*Note: This advice is not a medical diagnosis.*") | |
| path = os.path.join(tempfile.gettempdir(), "report.pdf") | |
| pdf.output(path) | |
| return path | |
| # Main function | |
| def classify_food(image, conditions, language): | |
| if not conditions: | |
| return "β οΈ Please select at least one health condition.", None, None, None | |
| # Preprocess image | |
| image = image.convert("RGB").resize((224, 224)) | |
| img = tf.keras.preprocessing.image.img_to_array(image) | |
| img = efficientnet_preprocess(img) | |
| img = np.expand_dims(img, axis=0) | |
| # Predict | |
| preds = model.predict(img)[0] | |
| idx = np.argmax(preds) | |
| predicted_class = class_names[idx] | |
| confidence = round(float(preds[idx]) * 100, 2) | |
| info = food_info[predicted_class] | |
| display_name = info.get("display_name", predicted_class.replace("_", " ").title()) | |
| # Advice logic | |
| advice, risk_score, flags = generate_advice(info, conditions) | |
| risk_level = get_risk_level(risk_score) | |
| # Save audio | |
| audio_path = os.path.join(tempfile.gettempdir(), "tts.mp3") | |
| tts = gTTS(text=advice, lang=LANG_CODE.get(language, "en")) | |
| tts.save(audio_path) | |
| # Save image | |
| image_path = os.path.join(tempfile.gettempdir(), "upload.jpg") | |
| image.save(image_path) | |
| # Generate PDF | |
| pdf_path = generate_pdf(info, advice, risk_score, risk_level, flags, conditions, language, display_name, image_path) | |
| # Output summary | |
| result = ( | |
| f"π½οΈ Food: {display_name}\n" | |
| f"π Ethnicity: {info['ethnicity']}\n" | |
| f"π₯¦ Ingredients: {info['ingredients']}\n" | |
| f"π₯ Calories: {info['calories']} kcal\n" | |
| f"π Carbs: {info['carbs']}g\n" | |
| f"π₯© Protein: {info['protein']}g\n" | |
| f"π§ Fat: {info['fat']}g\n" | |
| f"π± Diet Type: {info['diet_type']}\n" | |
| f"π Substitute: {info.get('substitute', 'None')}\n\n" | |
| f"π Confidence: {confidence}%\n" | |
| f"π Risk Score: {risk_score}% ({risk_level})\n" | |
| f"β οΈ Risk Factors: {', '.join(flags) if flags else 'None'}\n" | |
| f"β Advice: {advice}\n\n" | |
| f"π *Generated by HoodHealth Pro+.*" | |
| ) | |
| return result, audio_path, f"π£οΈ Language: {language}", pdf_path | |
| # Gradio interface | |
| interface = gr.Interface( | |
| fn=classify_food, | |
| inputs=[ | |
| gr.Image(type="pil", label="Upload Food Image"), | |
| gr.CheckboxGroup(label="Select Health Conditions", choices=[ | |
| "Normal", "Diabetic", "Hypertensive", "Weight Loss", "Malnourished", "Pregnant/Nursing", "Cholesterol Watch"]), | |
| gr.Dropdown(label="Language for TTS", choices=["English", "Hausa", "Yoruba", "Igbo"], value="English") | |
| ], | |
| outputs=[ | |
| gr.Textbox(label="Prediction and Advice"), | |
| gr.Audio(label="Hear Advice"), | |
| gr.Textbox(label="Language Info"), | |
| gr.File(label="Download PDF Report") | |
| ], | |
| title="π² FoodHealth Pro+", | |
| description="Upload a food image and get nutrition details, health risk, personalized advice, audio guidance, and a downloadable PDF report." | |
| ) | |
| if __name__ == "__main__": | |
| interface.launch() |