File size: 7,906 Bytes
8e64a15 e1f3336 74c93d7 e1f3336 2513d15 e1f3336 2513d15 e1f3336 36f4514 2513d15 e1f3336 2513d15 e1f3336 74c93d7 e1f3336 19ee958 8d64bf6 19ee958 e1f3336 74c93d7 e1f3336 74c93d7 e1f3336 19ee958 e1f3336 74c93d7 e1f3336 741f169 74c93d7 e1f3336 741f169 e1f3336 19ee958 54b8e46 74c93d7 19ee958 54b8e46 74c93d7 54b8e46 74c93d7 54b8e46 74c93d7 54b8e46 74c93d7 54b8e46 19ee958 e1f3336 74c93d7 e1f3336 741f169 2513d15 74c93d7 2513d15 8e64a15 741f169 e1f3336 2513d15 e1f3336 741f169 e1f3336 2513d15 e1f3336 2513d15 74c93d7 e1f3336 74c93d7 e1f3336 74c93d7 e1f3336 19ee958 36f4514 e1f3336 19ee958 74c93d7 e1f3336 74c93d7 e1f3336 74c93d7 36f4514 74c93d7 e1f3336 2513d15 e1f3336 2513d15 e1f3336 74c93d7 e1f3336 74c93d7 e1f3336 8e64a15 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
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("<b>Nutrition Analysis Report</b>", styles['Title']),
Spacer(1, 12),
Paragraph(report_text.replace('\n', '<br/>'), 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"""
<div style="text-align: center;">
<img src="data:image/png;base64,{logo_b64}" width="100">
<h2 style="color: #4CAF50;">Smart Diet Analyzer</h2>
<p style="color: #FF6347;">AI-Powered Food & Nutrition Analysis</p>
</div>
""", unsafe_allow_html=True)
st.markdown("---")
if st.session_state.get('analysis_result'):
col1, col2 = st.columns(2)
with col1:
pdf_report = generate_pdf(st.session_state.analysis_result, logo_b64)
st.download_button("π Download Nutrition Report",
data=pdf_report,
file_name="nutrition_report.pdf",
mime="application/pdf")
with col2:
if st.button("Clear Analysis ποΈ"):
st.session_state.pop('analysis_result')
rerun()
if st.session_state.get('analysis_result'):
st.markdown("### π― Nutrition Analysis Report")
st.info(st.session_state.analysis_result)
def render_sidebar(client, logo_b64):
"""Create sidebar UI elements."""
with st.sidebar:
st.subheader("Image Upload")
uploaded_file = st.file_uploader("Upload Food Image", type=ALLOWED_FILE_TYPES)
if uploaded_file:
try:
image = Image.open(uploaded_file)
st.image(image, caption="Uploaded Food Image")
except Exception as e:
st.error(f"Error displaying image: {e}")
if st.button("Analyze Meal π½οΈ"):
with st.spinner("Analyzing image..."):
report = generate_analysis(uploaded_file, client)
if report:
st.session_state.analysis_result = report
rerun()
else:
st.error("Failed to generate analysis.")
# ======================
# APPLICATION ENTRYPOINT
# ======================
def main():
"""Primary application controller."""
client = initialize_api_client()
logo_b64 = encode_image("src/logo.png")
display_main_interface(logo_b64)
render_sidebar(client, logo_b64)
if __name__ == "__main__":
main()
|