CoolFace
Apppublic

Gianone/smartplate

sourceHugging Facemitupdated 4mo agoView on Hugging Face
0likes
app.py219 linesDownload Raw Back to root
1"""2SmartPlate Gradio application β€” inference only, no training code here.3 4Run locally:5    python app.py6 7On Hugging Face Spaces, this file is loaded automatically.8"""9 10from __future__ import annotations11 12import logging13import os14from pathlib import Path15from typing import Optional, Tuple16 17from dotenv import load_dotenv18 19load_dotenv()20 21import gradio as gr22 23# Aggressive patch: bypass Gradio's API info schema parser entirely24# Fixes both "bool not iterable" AND "Cannot parse schema True"25import gradio_client.utils as _gcu26 27_original_json_to_type = _gcu._json_schema_to_python_type28 29def _safe_json_to_type(schema, defs=None):30    if not isinstance(schema, dict):31        return "Any"32    try:33        return _original_json_to_type(schema, defs)34    except Exception:35        return "Any"36 37_gcu._json_schema_to_python_type = _safe_json_to_type38 39def _safe_top_level(schema):40    if not isinstance(schema, dict):41        return "Any"42    try:43        return _safe_json_to_type(schema, schema.get("$defs"))44    except Exception:45        return "Any"46 47_gcu.json_schema_to_python_type = _safe_top_level48 49_original_get_type = _gcu.get_type50def _patched_get_type(schema):51    if not isinstance(schema, dict):52        return "Any"53    try:54        return _original_get_type(schema)55    except Exception:56        return "Any"57_gcu.get_type = _patched_get_type58 59from PIL import Image60 61from src.pipeline import SmartPlatePipeline62 63logging.basicConfig(level=logging.INFO)64logger = logging.getLogger(__name__)65 66_pipeline: Optional[SmartPlatePipeline] = None67 68 69def get_pipeline() -> SmartPlatePipeline:70    global _pipeline71    if _pipeline is None:72        _pipeline = SmartPlatePipeline()73    return _pipeline74 75 76def analyze_meal(77    image: Optional[Image.Image],78    user_question: str,79) -> Tuple[str, str, str]:80    """Gradio callback: run the full pipeline and return formatted outputs.81 82    Returns:83        Tuple of (cv_output, ml_output, nlp_output) as Markdown strings.84    """85    if image is None:86        return "Please upload a meal photo to get started.", "", ""87 88    question = user_question.strip() if user_question else None89 90    try:91        result = get_pipeline().process(image, user_question=question)92 93        cv = result["cv_result"]94        ml = result["ml_result"]95        nlp = result["nlp_result"]96 97        # --- CV output ---98        food_name = cv["class"].replace("_", " ").title()99        cv_text = f"**{food_name}**\n\nConfidence: {cv['confidence']:.0%}"100        if cv.get("top_5") and len(cv["top_5"]) > 1:101            top5_lines = "\n".join(102                f"- {r['class'].replace('_', ' ').title()}: {r['confidence']:.0%}"103                for r in cv["top_5"]104            )105            cv_text += f"\n\n**Top 5 predictions:**\n{top5_lines}"106 107        # --- ML output ---108        n = ml["nutrition"]109        health = ml["health_label"].upper()110        health_emoji = {"HEALTHY": "🟒", "MEDIUM": "🟑", "UNHEALTHY": "πŸ”΄"}.get(111            health, "βšͺ"112        )113        proba = ml.get("probabilities", {})114        proba_str = "  ".join(115            f"{k}: {v:.0%}" for k, v in proba.items()116        )117 118        ml_text = (119            f"### Nutritional Values (per 100 g)\n\n"120            f"| Nutrient | Amount |\n"121            f"|---|---|\n"122            f"| Energy | {n['kcal']:.0f} kcal |\n"123            f"| Fat | {n['fat']:.1f} g |\n"124            f"| β€” Saturated fat | {n['sat_fat']:.1f} g |\n"125            f"| Carbohydrates | {n['carbs']:.1f} g |\n"126            f"| β€” Sugars | {n['sugar']:.1f} g |\n"127            f"| Fiber | {n['fiber']:.1f} g |\n"128            f"| Protein | {n['protein']:.1f} g |\n"129            f"| Salt | {n['salt']:.1f} g |\n\n"130            f"**Health Category:** {health_emoji} {health}\n\n"131            f"*Confidence: {proba_str}*"132        )133 134        # --- NLP output ---135        sources = nlp.get("sources", [])136        sources_str = " Β· ".join(dict.fromkeys(sources)) if sources else "WHO Β· DGE Β· Harvard"137        nlp_text = f"{nlp['answer']}\n\n*Sources: {sources_str}*"138 139        return cv_text, ml_text, nlp_text140 141    except EnvironmentError as exc:142        logger.error("Environment error: %s", exc)143        return (144            "⚠️ Configuration error.",145            str(exc),146            "Please set OPENAI_API_KEY in your .env file.",147        )148    except Exception as exc:149        logger.error("Pipeline error: %s", exc, exc_info=True)150        return f"⚠️ Error: {exc}", "", "Please try again or check the logs."151 152 153# ── Gradio layout ──────────────────────────────────────────────────────────────154 155_EXAMPLES_DIR = Path("assets/examples")156 157 158def _find_examples() -> list:159    """Return example image paths if the directory exists."""160    if not _EXAMPLES_DIR.exists():161        return []162    paths = sorted(163        list(_EXAMPLES_DIR.glob("*.jpg"))164        + list(_EXAMPLES_DIR.glob("*.jpeg"))165        + list(_EXAMPLES_DIR.glob("*.png"))166    )167    return [[str(p), ""] for p in paths[:5]]168 169 170with gr.Blocks(171    title="SmartPlate – AI Nutrition Assistant",172    theme=gr.themes.Soft(),173) as demo:174    gr.Markdown(175        "# SmartPlate – AI Nutrition Assistant 🍽️\n"176        "Photograph your meal and get instant nutritional analysis with "177        "evidence-based health advice."178    )179 180    with gr.Row():181        with gr.Column(scale=1):182            img_input = gr.Image(type="pil", label="Upload a meal photo")183            question_input = gr.Textbox(184                label="Ask a question (optional)",185                placeholder="e.g. Can I eat this on a diet?",186                lines=2,187            )188            submit_btn = gr.Button("Analyze πŸ”", variant="primary")189 190        with gr.Column(scale=2):191            cv_output = gr.Markdown(label="Dish Recognition")192            ml_output = gr.Markdown(label="Nutritional Analysis")193            nlp_output = gr.Markdown(label="Health Advice")194 195    examples = _find_examples()196    if examples:197        gr.Examples(198            examples=examples,199            inputs=[img_input, question_input],200            label="Try an example",201        )202 203    submit_btn.click(204        fn=analyze_meal,205        inputs=[img_input, question_input],206        outputs=[cv_output, ml_output, nlp_output],207    )208 209    gr.Markdown(210        "---\n"211        "**Sources:** WHO Β· DGE (Deutsche Gesellschaft fΓΌr ErnΓ€hrung) Β· Harvard Nutrition\n\n"212        "*For educational use only β€” not medical advice. "213        "ZHAW KI-Anwendungen FS 2026.*"214    )215 216 217if __name__ == "__main__":218    share = os.getenv("GRADIO_SHARE", "false").lower() == "true"219    demo.launch(show_api=False)