import streamlit as st
from PIL import Image
import os
import base64
import io
from dotenv import load_dotenv
from groq import Groq
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as ReportLabImage
from reportlab.lib.styles import getSampleStyleSheet
# ======================
# CONFIGURATION SETTINGS
# ======================
st.set_page_config(
page_title="Smart Diet Analyzer",
page_icon="🍎",
layout="wide",
initial_sidebar_state="expanded"
)
ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
# ======================
# RERUN HELPER FUNCTION
# ======================
def rerun():
"""
Helper function to rerun the app.
Tries to use st.experimental_rerun if available; otherwise, does nothing.
"""
if hasattr(st, "experimental_rerun"):
st.experimental_rerun()
else:
# Fallback: you might consider raising an exception to force a rerun.
# However, this is not recommended for production. Instead, you might simply
# notify the user to manually refresh the page.
st.warning("Please refresh the page to update the results.")
# ======================
# UTILITY FUNCTIONS
# ======================
def initialize_api_client():
"""Initialize Groq API client."""
load_dotenv()
api_key = os.getenv("GROQ_API_KEY")
if not api_key:
st.error("API key not found. Please verify .env configuration.")
st.stop()
return Groq(api_key=api_key)
def encode_image(image_path):
"""Encode an image to base64."""
try:
with open(image_path, "rb") as img_file:
return base64.b64encode(img_file.read()).decode("utf-8")
except FileNotFoundError:
st.error(f"Logo file not found at {image_path}")
return ""
def process_image(uploaded_file):
"""Convert image to base64 string."""
try:
image = Image.open(uploaded_file)
buffer = io.BytesIO()
fmt = image.format if image.format else "PNG"
image.save(buffer, format=fmt)
return base64.b64encode(buffer.getvalue()).decode('utf-8'), fmt
except Exception as e:
st.error(f"Image processing error: {e}")
return None, None
def generate_pdf(report_text, logo_b64):
"""Generate a PDF report with logo."""
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter)
styles = getSampleStyleSheet()
story = []
# If a logo is provided, decode and add it
if logo_b64:
try:
logo_data = base64.b64decode(logo_b64)
logo_image = Image.open(io.BytesIO(logo_data))
logo_width, logo_height = logo_image.size
logo_aspect = logo_height / logo_width
max_logo_width = 150
logo_width = min(logo_width, max_logo_width)
logo_height = int(logo_width * logo_aspect)
logo = ReportLabImage(io.BytesIO(logo_data), width=logo_width, height=logo_height)
story.append(logo)
story.append(Spacer(1, 12))
except Exception as e:
st.error(f"Error adding logo to PDF: {e}")
story.extend([
Paragraph("Nutrition Analysis Report", styles['Title']),
Spacer(1, 12),
Paragraph(report_text.replace('\n', '
'), styles['BodyText'])
])
try:
doc.build(story)
except Exception as e:
st.error(f"Error generating PDF: {e}")
buffer.seek(0)
return buffer
def generate_analysis(uploaded_file, client):
"""Generate AI-powered food analysis."""
base64_image, img_format = process_image(uploaded_file)
if not base64_image:
return None
image_url = f"data:image/{img_format.lower()};base64,{base64_image}"
try:
response = client.chat.completions.create(
model="llama-3.2-11b-vision-preview",
messages=[
{
"role": "user",
"content": [
{"type": "text", "text": """
You are an expert nutritionist with advanced image analysis capabilities.
Your task is to analyze the provided image, identify all visible food items, and estimate their calorie content with high accuracy.
**Instructions:**
- Identify and list each food item visible in the image.
- For each item, estimate the calorie content based on standard nutritional data, considering portion size, cooking method, and food density.
- Clearly mark any calorie estimate as "approximate" if based on assumptions due to unclear details.
- Calculate and provide the total estimated calories for the entire meal.
**Output Format:**
- Food Item 1: [Name] – Estimated Calories: [value] kcal
- Food Item 2: [Name] – Estimated Calories: [value] kcal
- ...
- **Total Estimated Calories:** [value] kcal
If the image lacks sufficient detail or is unclear, specify the limitations and include your confidence level in the estimate as a percentage.
"""},
{"type": "image_url", "image_url": {"url": image_url}}
]
}
],
temperature=0.2,
max_tokens=400,
top_p=0.5
)
return response.choices[0].message.content
except Exception as e:
st.error(f"API communication error: {e}")
return None
# ======================
# UI COMPONENTS
# ======================
def display_main_interface(logo_b64):
"""Render primary application interface."""
st.markdown(f"""
AI-Powered Food & Nutrition Analysis