Spaces:
Running
Running
| import gradio as gr | |
| import tensorflow as tf | |
| import numpy as np | |
| from PIL import Image | |
| import json | |
| from gtts import gTTS | |
| import os | |
| import tempfile | |
| from health_logic import generate_advice | |
| from deep_translator import GoogleTranslator | |
| from datetime import datetime | |
| from fpdf import FPDF | |
| # Load model | |
| model = tf.keras.models.load_model("food_vision_model.keras") | |
| # Load metadata | |
| with open("food_info.json", "r") as f: | |
| food_info = json.load(f) | |
| class_names = list(food_info.keys()) | |
| # Language code mapping for gTTS | |
| LANG_CODE = { | |
| "English": "en", | |
| "Hausa": "ha", | |
| "Yoruba": "yo", | |
| "Igbo": "ig" | |
| } | |
| # Risk color level | |
| def get_risk_level(score): | |
| if score <= 30: | |
| return "π’ Low Risk" | |
| elif score <= 70: | |
| return "π‘ Medium Risk" | |
| else: | |
| return "π΄ High Risk" | |
| # PDF Generator using Unicode-safe font | |
| class UnicodePDF(FPDF): | |
| def __init__(self): | |
| super().__init__() | |
| self.add_font("DejaVu", "", "DejaVuSans.ttf", uni=True) | |
| self.add_font("DejaVu", "B", "DejaVuSans.ttf", uni=True) | |
| self.set_font("DejaVu", size=12) | |
| def generate_pdf(info, advice, risk_score, risk_level, flags, conditions, lang, display_name, image_path): | |
| pdf = UnicodePDF() | |
| pdf.add_page() | |
| pdf.set_font("DejaVu", "B", 16) | |
| pdf.cell(0, 10, "HoodHealth Pro+ Report", ln=True) | |
| pdf.set_font("DejaVu", size=12) | |
| pdf.cell(0, 10, f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M')}", ln=True) | |
| pdf.ln(10) | |
| if os.path.exists(image_path): | |
| pdf.image(image_path, x=10, y=40, w=60) | |
| pdf.set_xy(75, 40) | |
| pdf.multi_cell(0, 10, | |
| f"π½οΈ Food: {display_name}\n" | |
| f"π Ethnicity: {info['ethnicity']}\n" | |
| f"π₯¦ Ingredients: {info['ingredients']}" | |
| ) | |
| pdf.ln(35) | |
| pdf.multi_cell(0, 10, | |
| f"π₯ Calories: {info['calories']} kcal\n" | |
| f"π Carbs: {info['carbs']}g\n" | |
| f"π₯© Protein: {info['protein']}g\n" | |
| f"π§ Fat: {info['fat']}g" | |
| ) | |
| pdf.ln(5) | |
| pdf.multi_cell(0, 10, | |
| f"π± Diet Type: {info['diet_type']}\n" | |
| f"π Substitute: {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", size=12) | |
| pdf.multi_cell(0, 10, | |
| f"π Selected Conditions: {', '.join(conditions)}\n" | |
| f"π Risk Score: {risk_score}% ({risk_level})\n" | |
| f"β οΈ Risk Factors: {', '.join(flags) if flags else 'None'}\n" | |
| f"β Advice: {advice}" | |
| ) | |
| pdf.ln(5) | |
| pdf.set_font("DejaVu", "I", 10) | |
| pdf.multi_cell(0, 10, "*Note: This advice is generated using simplified nutritional rules and is not a medical diagnosis.*") | |
| temp_pdf = os.path.join(tempfile.gettempdir(), "report.pdf") | |
| pdf.output(temp_pdf) | |
| return temp_pdf | |
| # Main logic | |
| def classify_food(image, conditions, language): | |
| if not conditions: | |
| return "β οΈ Please select at least one health condition.", None, None, None | |
| image = image.convert("RGB").resize((224, 224)) | |
| img_array = tf.keras.preprocessing.image.img_to_array(image) / 255.0 | |
| img_array = np.expand_dims(img_array, axis=0) | |
| predictions = model.predict(img_array)[0] | |
| predicted_index = np.argmax(predictions) | |
| predicted_class = class_names[predicted_index] | |
| confidence = round(float(predictions[predicted_index]) * 100, 2) | |
| info = food_info[predicted_class] | |
| display_name = info.get("display_name", predicted_class.title()) | |
| advice, risk_score, flags = generate_advice(info, conditions) | |
| risk_level = get_risk_level(risk_score) | |
| # Translate | |
| if language != "English": | |
| try: | |
| translated = GoogleTranslator(source="auto", target=LANG_CODE[language]).translate(advice) | |
| advice = translated | |
| except: | |
| advice += " (β οΈ Translation failed)" | |
| # TTS with fallback | |
| tts_audio = None | |
| try: | |
| tts_lang = LANG_CODE.get(language, "en") | |
| tts = gTTS(text=advice, lang=tts_lang) | |
| tts_audio = os.path.join(tempfile.gettempdir(), "tts.mp3") | |
| tts.save(tts_audio) | |
| except Exception as e: | |
| print(f"TTS error: {e}") | |
| advice += " (β οΈ Voice advice unavailable)" | |
| # Save image and PDF | |
| tmp_img = os.path.join(tempfile.gettempdir(), "upload.jpg") | |
| image.save(tmp_img) | |
| pdf_path = generate_pdf(info, advice, risk_score, risk_level, flags, conditions, language, display_name, tmp_img) | |
| # Output | |
| result = ( | |
| f"π§ *Predictions*:\n" | |
| f"π² {display_name} β {confidence}%\n\n" | |
| f"π *Top 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" | |
| f"π *Substitute*: {info.get('substitute', 'None')}\n\n" | |
| f"π This advice is generated using simple nutrition rules and is not a medical diagnosis." | |
| ) | |
| return result, tts_audio, f"π£οΈ Language: {language}", pdf_path | |
| # 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=list(LANG_CODE.keys()), value="English") | |
| ], | |
| outputs=[ | |
| gr.Textbox(label="Result", lines=10), | |
| gr.Audio(label="Hear Advice"), | |
| gr.Textbox(label="TTS Language"), | |
| gr.File(label="Download Health Report (PDF)") | |
| ], | |
| title="π₯ FoodHealth Pro+", | |
| description="Upload a food image. Get full nutrition info, risk score, health advice with voice support in your language and download a detailed PDF." | |
| ) | |
| if __name__ == "__main__": | |
| interface.launch() | |