SURESHBEEKHANI commited on
Commit
edd0522
·
verified ·
1 Parent(s): 36f4514

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -146
app.py CHANGED
@@ -3,6 +3,8 @@ from PIL import Image
3
  import os
4
  import base64
5
  import io
 
 
6
  from dotenv import load_dotenv
7
  from groq import Groq
8
  from reportlab.lib.pagesizes import letter
@@ -10,7 +12,7 @@ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image as Re
10
  from reportlab.lib.styles import getSampleStyleSheet
11
 
12
  # ======================
13
- # CONFIGURATION SETTINGS
14
  # ======================
15
  st.set_page_config(
16
  page_title="Smart Diet Analyzer",
@@ -20,214 +22,200 @@ st.set_page_config(
20
  )
21
 
22
  ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
 
 
 
 
 
 
 
23
 
24
  # ======================
25
- # RERUN HELPER FUNCTION
26
  # ======================
27
- def rerun():
28
- """
29
- Helper function to rerun the app.
30
- Tries to use st.experimental_rerun if available; otherwise, does nothing.
31
- """
32
- if hasattr(st, "experimental_rerun"):
33
- st.experimental_rerun()
34
- else:
35
- # Fallback: you might consider raising an exception to force a rerun.
36
- # However, this is not recommended for production. Instead, you might simply
37
- # notify the user to manually refresh the page.
38
- st.warning("Please refresh the page to update the results.")
39
-
40
- # ======================
41
- # UTILITY FUNCTIONS
42
- # ======================
43
-
44
- def initialize_api_client():
45
- """Initialize Groq API client."""
46
- load_dotenv()
47
- api_key = os.getenv("GROQ_API_KEY")
48
- if not api_key:
49
- st.error("API key not found. Please verify .env configuration.")
50
- st.stop()
51
- return Groq(api_key=api_key)
52
-
53
-
54
- def encode_image(image_path):
55
- """Encode an image to base64."""
56
  try:
57
- with open(image_path, "rb") as img_file:
58
  return base64.b64encode(img_file.read()).decode("utf-8")
59
  except FileNotFoundError:
60
- st.error(f"Logo file not found at {image_path}")
61
- return ""
62
 
 
 
 
 
 
 
 
 
63
 
64
- def process_image(uploaded_file):
65
- """Convert image to base64 string."""
 
 
 
66
  try:
67
- image = Image.open(uploaded_file)
68
- buffer = io.BytesIO()
69
- fmt = image.format if image.format else "PNG"
70
- image.save(buffer, format=fmt)
71
- return base64.b64encode(buffer.getvalue()).decode('utf-8'), fmt
72
  except Exception as e:
73
- st.error(f"Image processing error: {e}")
74
- return None, None
75
-
76
 
77
- def generate_pdf(report_text, logo_b64):
78
- """Generate a PDF report with logo."""
79
  buffer = io.BytesIO()
80
  doc = SimpleDocTemplate(buffer, pagesize=letter)
81
  styles = getSampleStyleSheet()
82
-
83
  story = []
84
-
85
- # If a logo is provided, decode and add it
86
  if logo_b64:
87
  try:
88
  logo_data = base64.b64decode(logo_b64)
89
- logo_image = Image.open(io.BytesIO(logo_data))
90
- logo_width, logo_height = logo_image.size
91
- logo_aspect = logo_height / logo_width
92
- max_logo_width = 150
93
- logo_width = min(logo_width, max_logo_width)
94
- logo_height = int(logo_width * logo_aspect)
95
-
96
- logo = ReportLabImage(io.BytesIO(logo_data), width=logo_width, height=logo_height)
97
- story.append(logo)
98
  story.append(Spacer(1, 12))
99
  except Exception as e:
100
- st.error(f"Error adding logo to PDF: {e}")
101
-
 
102
  story.extend([
103
  Paragraph("<b>Nutrition Analysis Report</b>", styles['Title']),
104
  Spacer(1, 12),
105
  Paragraph(report_text.replace('\n', '<br/>'), styles['BodyText'])
106
  ])
107
-
108
  try:
109
  doc.build(story)
110
  except Exception as e:
111
- st.error(f"Error generating PDF: {e}")
112
 
113
  buffer.seek(0)
114
  return buffer
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
- def generate_analysis(uploaded_file, client):
118
- """Generate AI-powered food analysis."""
119
- base64_image, img_format = process_image(uploaded_file)
120
- if not base64_image:
121
- return None
122
-
123
- image_url = f"data:image/{img_format.lower()};base64,{base64_image}"
124
-
125
  try:
126
  response = client.chat.completions.create(
127
- model="llama-3.2-11b-vision-preview",
128
- messages=[
129
- {
130
- "role": "user",
131
- "content": [
132
- {"type": "text", "text": """
133
- You are an expert nutritionist with advanced image analysis capabilities.
134
- Your task is to analyze the provided image, identify all visible food items, and estimate their calorie content with high accuracy.
135
- **Instructions:**
136
- - Identify and list each food item visible in the image.
137
- - For each item, estimate the calorie content based on standard nutritional data, considering portion size, cooking method, and food density.
138
- - Clearly mark any calorie estimate as "approximate" if based on assumptions due to unclear details.
139
- - Calculate and provide the total estimated calories for the entire meal.
140
- **Output Format:**
141
- - Food Item 1: [Name] – Estimated Calories: [value] kcal
142
- - Food Item 2: [Name] – Estimated Calories: [value] kcal
143
- - ...
144
- - **Total Estimated Calories:** [value] kcal
145
-
146
- If the image lacks sufficient detail or is unclear, specify the limitations and include your confidence level in the estimate as a percentage.
147
- """},
148
- {"type": "image_url", "image_url": {"url": image_url}}
149
- ]
150
- }
151
- ],
152
- temperature=0.2,
153
- max_tokens=400,
154
- top_p=0.5
155
  )
156
  return response.choices[0].message.content
157
  except Exception as e:
158
- st.error(f"API communication error: {e}")
159
  return None
160
 
161
  # ======================
162
  # UI COMPONENTS
163
  # ======================
164
-
165
- def display_main_interface(logo_b64):
166
- """Render primary application interface."""
167
  st.markdown(f"""
168
  <div style="text-align: center;">
169
- <img src="data:image/png;base64,{logo_b64}" width="100">
170
  <h2 style="color: #4CAF50;">Smart Diet Analyzer</h2>
171
  <p style="color: #FF6347;">AI-Powered Food & Nutrition Analysis</p>
172
  </div>
173
  """, unsafe_allow_html=True)
174
 
175
  st.markdown("---")
176
-
177
- if st.session_state.get('analysis_result'):
178
  col1, col2 = st.columns(2)
179
-
180
  with col1:
181
- pdf_report = generate_pdf(st.session_state.analysis_result, logo_b64)
182
- st.download_button("📄 Download Nutrition Report",
183
- data=pdf_report,
184
- file_name="nutrition_report.pdf",
185
- mime="application/pdf")
186
-
 
187
  with col2:
188
  if st.button("Clear Analysis 🗑️"):
189
- st.session_state.pop('analysis_result')
190
- rerun()
191
-
192
- if st.session_state.get('analysis_result'):
193
  st.markdown("### 🎯 Nutrition Analysis Report")
194
- st.info(st.session_state.analysis_result)
195
 
196
-
197
- def render_sidebar(client, logo_b64):
198
- """Create sidebar UI elements."""
199
  with st.sidebar:
200
- st.subheader("Image Upload")
201
- uploaded_file = st.file_uploader("Upload Food Image", type=ALLOWED_FILE_TYPES)
202
-
203
- if uploaded_file:
204
- try:
205
- image = Image.open(uploaded_file)
206
- st.image(image, caption="Uploaded Food Image")
207
- except Exception as e:
208
- st.error(f"Error displaying image: {e}")
209
-
210
- if st.button("Analyze Meal 🍽️"):
211
- with st.spinner("Analyzing image..."):
212
- report = generate_analysis(uploaded_file, client)
213
- if report:
214
- st.session_state.analysis_result = report
215
- rerun()
216
- else:
217
- st.error("Failed to generate analysis.")
218
 
 
 
 
 
 
 
 
219
 
220
  # ======================
221
  # APPLICATION ENTRYPOINT
222
  # ======================
223
-
224
  def main():
225
- """Primary application controller."""
226
- client = initialize_api_client()
227
- logo_b64 = encode_image("src/logo.png")
228
- display_main_interface(logo_b64)
229
- render_sidebar(client, logo_b64)
230
-
231
 
232
  if __name__ == "__main__":
233
- main()
 
3
  import os
4
  import base64
5
  import io
6
+ import textwrap
7
+ from typing import Optional, Tuple
8
  from dotenv import load_dotenv
9
  from groq import Groq
10
  from reportlab.lib.pagesizes import letter
 
12
  from reportlab.lib.styles import getSampleStyleSheet
13
 
14
  # ======================
15
+ # CONFIGURATION
16
  # ======================
17
  st.set_page_config(
18
  page_title="Smart Diet Analyzer",
 
22
  )
23
 
24
  ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
25
+ MODEL_NAME = "llama-3.2-11b-vision-preview"
26
+ MODEL_SETTINGS = {
27
+ 'temperature': 0.2,
28
+ 'max_tokens': 400,
29
+ 'top_p': 0.5
30
+ }
31
+ LOGO_PATH = "src/logo.png"
32
 
33
  # ======================
34
+ # CACHED RESOURCES
35
  # ======================
36
+ @st.cache_data
37
+ def get_logo_base64() -> Optional[str]:
38
+ """Load and cache logo as base64 string"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  try:
40
+ with open(LOGO_PATH, "rb") as img_file:
41
  return base64.b64encode(img_file.read()).decode("utf-8")
42
  except FileNotFoundError:
43
+ st.error(f"Logo file not found at {LOGO_PATH}")
44
+ return None
45
 
46
+ @st.cache_resource
47
+ def initialize_groq_client() -> Groq:
48
+ """Initialize and cache Groq API client"""
49
+ load_dotenv()
50
+ if api_key := os.getenv("GROQ_API_KEY"):
51
+ return Groq(api_key=api_key)
52
+ st.error("GROQ_API_KEY not found in environment")
53
+ st.stop()
54
 
55
+ # ======================
56
+ # CORE FUNCTIONALITY
57
+ # ======================
58
+ def process_image(uploaded_file: io.BytesIO) -> Optional[Tuple[str, str]]:
59
+ """Process uploaded image to base64 string with format detection"""
60
  try:
61
+ with Image.open(uploaded_file) as img:
62
+ fmt = img.format or 'PNG'
63
+ buffer = io.BytesIO()
64
+ img.save(buffer, format=fmt)
65
+ return base64.b64encode(buffer.getvalue()).decode('utf-8'), fmt
66
  except Exception as e:
67
+ st.error(f"Image processing error: {str(e)}")
68
+ return None
 
69
 
70
+ def generate_pdf_content(report_text: str, logo_b64: Optional[str]) -> io.BytesIO:
71
+ """Generate PDF report with logo and analysis content"""
72
  buffer = io.BytesIO()
73
  doc = SimpleDocTemplate(buffer, pagesize=letter)
74
  styles = getSampleStyleSheet()
 
75
  story = []
76
+
77
+ # Add logo if available
78
  if logo_b64:
79
  try:
80
  logo_data = base64.b64decode(logo_b64)
81
+ with Image.open(io.BytesIO(logo_data)) as logo_img:
82
+ aspect = logo_img.height / logo_img.width
83
+ max_width = 150
84
+ img_width = min(logo_img.width, max_width)
85
+ img_height = img_width * aspect
86
+
87
+ story.append(
88
+ ReportLabImage(io.BytesIO(logo_data), width=img_width, height=img_height)
89
+ )
90
  story.append(Spacer(1, 12))
91
  except Exception as e:
92
+ st.error(f"Logo processing error: {str(e)}")
93
+
94
+ # Add report content
95
  story.extend([
96
  Paragraph("<b>Nutrition Analysis Report</b>", styles['Title']),
97
  Spacer(1, 12),
98
  Paragraph(report_text.replace('\n', '<br/>'), styles['BodyText'])
99
  ])
100
+
101
  try:
102
  doc.build(story)
103
  except Exception as e:
104
+ st.error(f"PDF generation failed: {str(e)}")
105
 
106
  buffer.seek(0)
107
  return buffer
108
 
109
+ def generate_ai_analysis(client: Groq, image_b64: str, img_format: str) -> Optional[str]:
110
+ """Generate nutritional analysis using Groq's vision API"""
111
+ vision_prompt = textwrap.dedent("""
112
+ As an expert nutritionist with advanced image analysis capabilities, analyze the provided food image:
113
+
114
+ 1. Identify all visible food items
115
+ 2. Estimate calorie content considering:
116
+ - Portion size
117
+ - Cooking method
118
+ - Food density
119
+ 3. Mark estimates as "approximate" when assumptions are needed
120
+ 4. Calculate total meal calories
121
+
122
+ Output format:
123
+ - Food Item 1: [Name] – Estimated Calories: [value] kcal
124
+ - ...
125
+ - **Total Estimated Calories:** [value] kcal
126
+
127
+ Include confidence levels for unclear images and specify limitations.
128
+ """)
129
 
 
 
 
 
 
 
 
 
130
  try:
131
  response = client.chat.completions.create(
132
+ model=MODEL_NAME,
133
+ messages=[{
134
+ "role": "user",
135
+ "content": [
136
+ {"type": "text", "text": vision_prompt},
137
+ {"type": "image_url", "image_url": {
138
+ "url": f"data:image/{img_format.lower()};base64,{image_b64}"
139
+ }}
140
+ ]
141
+ }],
142
+ **MODEL_SETTINGS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  )
144
  return response.choices[0].message.content
145
  except Exception as e:
146
+ st.error(f"API Error: {str(e)}")
147
  return None
148
 
149
  # ======================
150
  # UI COMPONENTS
151
  # ======================
152
+ def render_main_content(logo_b64: Optional[str]):
153
+ """Main content layout and interactions"""
 
154
  st.markdown(f"""
155
  <div style="text-align: center;">
156
+ {f'<img src="data:image/png;base64,{logo_b64}" width="100">' if logo_b64 else ''}
157
  <h2 style="color: #4CAF50;">Smart Diet Analyzer</h2>
158
  <p style="color: #FF6347;">AI-Powered Food & Nutrition Analysis</p>
159
  </div>
160
  """, unsafe_allow_html=True)
161
 
162
  st.markdown("---")
163
+
164
+ if analysis := st.session_state.get('analysis_result'):
165
  col1, col2 = st.columns(2)
 
166
  with col1:
167
+ pdf_buffer = generate_pdf_content(analysis, logo_b64)
168
+ st.download_button(
169
+ "📄 Download Nutrition Report",
170
+ data=pdf_buffer,
171
+ file_name="nutrition_report.pdf",
172
+ mime="application/pdf"
173
+ )
174
  with col2:
175
  if st.button("Clear Analysis 🗑️"):
176
+ del st.session_state.analysis_result
177
+ st.rerun()
178
+
 
179
  st.markdown("### 🎯 Nutrition Analysis Report")
180
+ st.info(analysis)
181
 
182
+ def render_sidebar(client: Groq):
183
+ """Sidebar upload and processing functionality"""
 
184
  with st.sidebar:
185
+ st.subheader("Meal Image Analysis")
186
+ uploaded_file = st.file_uploader(
187
+ "Upload Food Image",
188
+ type=ALLOWED_FILE_TYPES,
189
+ help="Upload clear photo of your meal for analysis"
190
+ )
191
+
192
+ if not uploaded_file:
193
+ return
194
+
195
+ try:
196
+ st.image(Image.open(uploaded_file), caption="Uploaded Meal Image")
197
+ except Exception as e:
198
+ st.error(f"Invalid image file: {str(e)}")
199
+ return
 
 
 
200
 
201
+ if st.button("Analyze Meal 🍽️", use_container_width=True):
202
+ with st.spinner("Analyzing nutritional content..."):
203
+ if img_data := process_image(uploaded_file):
204
+ analysis = generate_ai_analysis(client, *img_data)
205
+ if analysis:
206
+ st.session_state.analysis_result = analysis
207
+ st.rerun()
208
 
209
  # ======================
210
  # APPLICATION ENTRYPOINT
211
  # ======================
 
212
  def main():
213
+ """Main application controller"""
214
+ client = initialize_groq_client()
215
+ logo_b64 = get_logo_base64()
216
+
217
+ render_main_content(logo_b64)
218
+ render_sidebar(client)
219
 
220
  if __name__ == "__main__":
221
+ main()