Spaces:
Sleeping
Sleeping
File size: 7,196 Bytes
8e64a15 e1f3336 edd0522 e1f3336 74c93d7 e1f3336 2513d15 edd0522 2513d15 e1f3336 edd0522 e1f3336 36f4514 edd0522 36f4514 edd0522 e1f3336 edd0522 e1f3336 edd0522 e1f3336 edd0522 19ee958 edd0522 e1f3336 edd0522 e1f3336 edd0522 19ee958 edd0522 19ee958 74c93d7 edd0522 74c93d7 edd0522 74c93d7 edd0522 74c93d7 54b8e46 74c93d7 edd0522 74c93d7 edd0522 54b8e46 19ee958 edd0522 e1f3336 edd0522 e1f3336 2513d15 e1f3336 edd0522 e1f3336 2513d15 e1f3336 2513d15 edd0522 e1f3336 edd0522 e1f3336 edd0522 74c93d7 e1f3336 edd0522 e1f3336 edd0522 e1f3336 edd0522 e1f3336 edd0522 e1f3336 edd0522 74c93d7 edd0522 e1f3336 2513d15 e1f3336 2513d15 e1f3336 edd0522 e1f3336 edd0522 |
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 |
import streamlit as st
from PIL import Image
import os
import base64
import io
import textwrap
from typing import Optional, Tuple
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
# ======================
st.set_page_config(
page_title="Smart Diet Analyzer",
page_icon="π",
layout="wide",
initial_sidebar_state="expanded"
)
ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
MODEL_NAME = "llama-3.2-11b-vision-preview"
MODEL_SETTINGS = {
'temperature': 0.2,
'max_tokens': 400,
'top_p': 0.5
}
LOGO_PATH = "src/logo.png"
# ======================
# CACHED RESOURCES
# ======================
@st.cache_data
def get_logo_base64() -> Optional[str]:
"""Load and cache logo as base64 string"""
try:
with open(LOGO_PATH, "rb") as img_file:
return base64.b64encode(img_file.read()).decode("utf-8")
except FileNotFoundError:
st.error(f"Logo file not found at {LOGO_PATH}")
return None
@st.cache_resource
def initialize_groq_client() -> Groq:
"""Initialize and cache Groq API client"""
load_dotenv()
if api_key := os.getenv("GROQ_API_KEY"):
return Groq(api_key=api_key)
st.error("GROQ_API_KEY not found in environment")
st.stop()
# ======================
# CORE FUNCTIONALITY
# ======================
def process_image(uploaded_file: io.BytesIO) -> Optional[Tuple[str, str]]:
"""Process uploaded image to base64 string with format detection"""
try:
with Image.open(uploaded_file) as img:
fmt = img.format or 'PNG'
buffer = io.BytesIO()
img.save(buffer, format=fmt)
return base64.b64encode(buffer.getvalue()).decode('utf-8'), fmt
except Exception as e:
st.error(f"Image processing error: {str(e)}")
return None
def generate_pdf_content(report_text: str, logo_b64: Optional[str]) -> io.BytesIO:
"""Generate PDF report with logo and analysis content"""
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter)
styles = getSampleStyleSheet()
story = []
# Add logo if available
if logo_b64:
try:
logo_data = base64.b64decode(logo_b64)
with Image.open(io.BytesIO(logo_data)) as logo_img:
aspect = logo_img.height / logo_img.width
max_width = 150
img_width = min(logo_img.width, max_width)
img_height = img_width * aspect
story.append(
ReportLabImage(io.BytesIO(logo_data), width=img_width, height=img_height)
)
story.append(Spacer(1, 12))
except Exception as e:
st.error(f"Logo processing error: {str(e)}")
# Add report content
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"PDF generation failed: {str(e)}")
buffer.seek(0)
return buffer
def generate_ai_analysis(client: Groq, image_b64: str, img_format: str) -> Optional[str]:
"""Generate nutritional analysis using Groq's vision API"""
vision_prompt = textwrap.dedent("""
As an expert nutritionist with advanced image analysis capabilities, analyze the provided food image:
1. Identify all visible food items
2. Estimate calorie content considering:
- Portion size
- Cooking method
- Food density
3. Mark estimates as "approximate" when assumptions are needed
4. Calculate total meal calories
Output format:
- Food Item 1: [Name] β Estimated Calories: [value] kcal
- ...
- **Total Estimated Calories:** [value] kcal
Include confidence levels for unclear images and specify limitations.
""")
try:
response = client.chat.completions.create(
model=MODEL_NAME,
messages=[{
"role": "user",
"content": [
{"type": "text", "text": vision_prompt},
{"type": "image_url", "image_url": {
"url": f"data:image/{img_format.lower()};base64,{image_b64}"
}}
]
}],
**MODEL_SETTINGS
)
return response.choices[0].message.content
except Exception as e:
st.error(f"API Error: {str(e)}")
return None
# ======================
# UI COMPONENTS
# ======================
def render_main_content(logo_b64: Optional[str]):
"""Main content layout and interactions"""
st.markdown(f"""
<div style="text-align: center;">
{f'<img src="data:image/png;base64,{logo_b64}" width="100">' if logo_b64 else ''}
<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 analysis := st.session_state.get('analysis_result'):
col1, col2 = st.columns(2)
with col1:
pdf_buffer = generate_pdf_content(analysis, logo_b64)
st.download_button(
"π Download Nutrition Report",
data=pdf_buffer,
file_name="nutrition_report.pdf",
mime="application/pdf"
)
with col2:
if st.button("Clear Analysis ποΈ"):
del st.session_state.analysis_result
st.rerun()
st.markdown("### π― Nutrition Analysis Report")
st.info(analysis)
def render_sidebar(client: Groq):
"""Sidebar upload and processing functionality"""
with st.sidebar:
st.subheader("Meal Image Analysis")
uploaded_file = st.file_uploader(
"Upload Food Image",
type=ALLOWED_FILE_TYPES,
help="Upload clear photo of your meal for analysis"
)
if not uploaded_file:
return
try:
st.image(Image.open(uploaded_file), caption="Uploaded Meal Image")
except Exception as e:
st.error(f"Invalid image file: {str(e)}")
return
if st.button("Analyze Meal π½οΈ", use_container_width=True):
with st.spinner("Analyzing nutritional content..."):
if img_data := process_image(uploaded_file):
analysis = generate_ai_analysis(client, *img_data)
if analysis:
st.session_state.analysis_result = analysis
st.rerun()
# ======================
# APPLICATION ENTRYPOINT
# ======================
def main():
"""Main application controller"""
client = initialize_groq_client()
logo_b64 = get_logo_base64()
render_main_content(logo_b64)
render_sidebar(client)
if __name__ == "__main__":
main() |