dev2607 commited on
Commit
1a5e0f6
·
verified ·
1 Parent(s): 18c8f7e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +221 -109
app.py CHANGED
@@ -1,6 +1,13 @@
1
  import os
2
  import subprocess
3
  import sys
 
 
 
 
 
 
 
4
 
5
  # Attempt to install pytesseract if not found
6
  try:
@@ -9,68 +16,134 @@ except ImportError:
9
  subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pytesseract'])
10
  import pytesseract
11
 
12
- # Set Tesseract path
13
- pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
14
-
15
- def extract_text_from_image(image):
16
- try:
17
- if image is None:
18
- return "No image captured. Please try again."
19
-
20
- # Verify Tesseract executable
21
- if not os.path.exists('/usr/bin/tesseract'):
22
- return "Tesseract OCR is not installed. Please install tesseract-ocr."
23
-
24
- text = pytesseract.image_to_string(image)
25
-
26
- if not text.strip():
27
- return "No text could be extracted. Ensure image is clear and readable."
 
 
 
28
 
29
- return text
30
- except Exception as e:
31
- return f"Error extracting text: {str(e)}"
32
- import gradio as gr
33
- import re
34
- import numpy as np
35
- from PIL import Image
36
- import pytesseract
37
- import requests
38
- import json
39
- import os
40
- from dotenv import load_dotenv
41
- import google.generativeai as genai
42
 
43
  # Load environment variables
 
44
  load_dotenv()
45
 
46
- # Configure Gemini API
47
- GEMINI_API_KEY = "AIzaSyBNXFHDQqzi42t6upOtPpUDz2B-J48U60w"
48
- genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
 
 
49
 
50
  # Function to extract text from images using OCR
51
  def extract_text_from_image(image):
52
  try:
53
  if image is None:
54
  return "No image captured. Please try again."
55
- text = pytesseract.image_to_string(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  return text
57
  except Exception as e:
58
  return f"Error extracting text: {str(e)}"
59
-
60
  # Function to parse ingredients from text
61
  def parse_ingredients(text):
62
- # Basic parsing - split by commas, semicolons, and line breaks
63
  if not text:
64
  return []
65
 
66
- # Clean up the text - remove "Ingredients:" prefix if present
67
  text = re.sub(r'^ingredients:?\s*', '', text.lower(), flags=re.IGNORECASE)
68
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  # Split by common ingredient separators
70
  ingredients = re.split(r',|;|\n', text)
71
- ingredients = [i.strip().lower() for i in ingredients if i.strip()]
72
- return ingredients
 
 
 
 
 
 
 
73
 
 
74
  # Function to analyze ingredients with Gemini
75
  def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
76
  """
@@ -81,43 +154,95 @@ def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
81
 
82
  # Prepare the list of ingredients for the prompt
83
  ingredients_text = ", ".join(ingredients_list)
84
-
 
 
 
 
 
 
 
 
85
  # Create a prompt for Gemini
86
  if health_conditions and health_conditions.strip():
87
  prompt = f"""
88
  Analyze the following food ingredients for a person with these health conditions: {health_conditions}
89
-
90
  Ingredients: {ingredients_text}
91
-
92
  For each ingredient:
93
  1. Provide its potential health benefits
94
  2. Identify any potential risks
95
  3. Note if it may affect the specified health conditions
96
-
97
  Then provide an overall assessment of the product's suitability for someone with the specified health conditions.
98
  Format your response in markdown with clear headings and sections.
99
  """
100
  else:
101
  prompt = f"""
102
  Analyze the following food ingredients:
103
-
104
  Ingredients: {ingredients_text}
105
-
106
  For each ingredient:
107
  1. Provide its potential health benefits
108
  2. Identify any potential risks or common allergens associated with it
109
-
110
  Then provide an overall assessment of the product's general health profile.
111
  Format your response in markdown with clear headings and sections.
112
  """
113
 
114
  try:
115
- # Call the Gemini API
116
- model = genai.GenerativeModel('gemini-pro')
117
- response = model.generate_content(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- # Extract and return the analysis
120
- analysis = response.text
 
 
 
 
121
 
122
  # Add disclaimer
123
  disclaimer = """
@@ -130,13 +255,47 @@ def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
130
 
131
  except Exception as e:
132
  # Fallback to basic analysis if API call fails
133
- return f"Error connecting to analysis service: {str(e)}\n\nPlease try again later."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  # Function to process input based on method (camera, upload, or manual entry)
136
  def process_input(input_method, text_input, camera_input, upload_input, health_conditions):
137
  if input_method == "Camera":
138
  if camera_input is not None:
139
  extracted_text = extract_text_from_image(camera_input)
 
 
 
 
140
  ingredients = parse_ingredients(extracted_text)
141
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
142
  else:
@@ -145,13 +304,17 @@ def process_input(input_method, text_input, camera_input, upload_input, health_c
145
  elif input_method == "Image Upload":
146
  if upload_input is not None:
147
  extracted_text = extract_text_from_image(upload_input)
 
 
 
 
148
  ingredients = parse_ingredients(extracted_text)
149
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
150
  else:
151
  return "No image uploaded. Please try again."
152
 
153
  elif input_method == "Manual Entry":
154
- if text_input.strip():
155
  ingredients = parse_ingredients(text_input)
156
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
157
  else:
@@ -173,7 +336,7 @@ with gr.Blocks(title="AI Ingredient Scanner") as app:
173
  )
174
 
175
  # Camera input
176
- camera_input = gr.Image(label="Capture ingredients with camera", type="pil")
177
 
178
  # Image upload
179
  upload_input = gr.Image(label="Upload image of ingredients label", type="pil", visible=False)
@@ -203,9 +366,9 @@ with gr.Blocks(title="AI Ingredient Scanner") as app:
203
  # Show/hide inputs based on selection
204
  def update_visible_inputs(choice):
205
  return {
206
- upload_input: choice == "Image Upload",
207
- camera_input: choice == "Camera",
208
- text_input: choice == "Manual Entry"
209
  }
210
 
211
  input_method.change(update_visible_inputs, input_method, [upload_input, camera_input, text_input])
@@ -239,7 +402,6 @@ with gr.Blocks(title="AI Ingredient Scanner") as app:
239
  2. Take a photo of the ingredients label or enter ingredients manually
240
  3. Optionally enter your health concerns
241
  4. Click "Analyze Ingredients" to get your personalized analysis
242
-
243
  The AI will automatically analyze the ingredients, their health implications, and their potential impact on your specific health concerns.
244
  """)
245
 
@@ -251,7 +413,6 @@ with gr.Blocks(title="AI Ingredient Scanner") as app:
251
  - Allergies: "peanut allergy" or "shellfish allergy"
252
  - Dietary restrictions: "vegetarian" or "gluten-free diet"
253
  - Multiple conditions: "diabetes, high cholesterol, and lactose intolerance"
254
-
255
  The AI will tailor its analysis to your specific needs.
256
  """)
257
 
@@ -269,55 +430,6 @@ with gr.Blocks(title="AI Ingredient Scanner") as app:
269
  Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
270
  """)
271
 
272
- # Function to run when testing without API key
273
- def run_with_dummy_llm():
274
- # Override the LLM function with a dummy version for testing
275
- global analyze_ingredients_with_gemini
276
-
277
- def dummy_analyze(ingredients_list, health_conditions=None):
278
- ingredients_text = ", ".join(ingredients_list)
279
-
280
- report = f"""
281
- # Ingredient Analysis Report
282
-
283
- ## Detected Ingredients
284
- {", ".join([i.title() for i in ingredients_list])}
285
-
286
- ## Overview
287
- This is a simulated analysis since no API key was provided. In the actual application,
288
- the ingredients would be analyzed by an LLM for their health implications.
289
-
290
- ## Health Considerations
291
- """
292
-
293
- if health_conditions:
294
- report += f"""
295
- The analysis would specifically consider these health concerns: {health_conditions}
296
- """
297
- else:
298
- report += """
299
- No specific health concerns were provided, so a general analysis would be performed.
300
- """
301
-
302
- report += """
303
- ## Disclaimer
304
- This analysis is provided for informational purposes only and should not replace professional medical advice.
305
- Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
306
- """
307
-
308
- return report
309
-
310
- # Replace the real function with the dummy
311
- analyze_ingredients_with_gemini = dummy_analyze
312
-
313
- # Launch the app
314
- app.launch()
315
-
316
  # Launch the app
317
  if __name__ == "__main__":
318
- # Check if API key exists
319
- if not os.getenv("GEMINI_API_KEY"):
320
- print("WARNING: No Gemini API key found. Running with simulated LLM responses.")
321
- run_with_dummy_llm()
322
- else:
323
- app.launch()
 
1
  import os
2
  import subprocess
3
  import sys
4
+ import re
5
+ import numpy as np
6
+ from PIL import Image
7
+ import gradio as gr
8
+ import requests
9
+ import json
10
+ from dotenv import load_dotenv
11
 
12
  # Attempt to install pytesseract if not found
13
  try:
 
16
  subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pytesseract'])
17
  import pytesseract
18
 
19
+ # AFTER importing pytesseract, then set the path
20
+ try:
21
+ # First try the default path
22
+ if os.path.exists('/usr/bin/tesseract'):
23
+ pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
24
+ # Try to find it on the PATH
25
+ else:
26
+ tesseract_path = subprocess.check_output(['which', 'tesseract']).decode().strip()
27
+ if tesseract_path:
28
+ pytesseract.pytesseract.tesseract_cmd = tesseract_path
29
+ except:
30
+ # If all else fails, try the default installation path
31
+ pytesseract.pytesseract.tesseract_cmd = 'tesseract'
32
+ try:
33
+ import google.generativeai as genai
34
+ if GEMINI_API_KEY:
35
+ # Configure Gemini API
36
+ genai.configure(api_key=GEMINI_API_KEY)
37
+ print("Gemini API configured successfully")
38
 
39
+ # Test API connection
40
+ try:
41
+ models = genai.list_models()
42
+ print(f"Available models: {[m.name for m in models]}")
43
+ except Exception as e:
44
+ print(f"Error checking Gemini API models: {e}")
45
+ else:
46
+ print("No Gemini API key available. Will use dummy implementation.")
47
+ except ImportError:
48
+ print("Google Generative AI package not found, using dummy implementation")
49
+ genai = None
 
 
50
 
51
  # Load environment variables
52
+ # Load environment variables and set up API key
53
  load_dotenv()
54
 
55
+ # First try to get API key from environment
56
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
57
+
58
+ # Then try Google Colab userdata if available
59
+
60
 
61
  # Function to extract text from images using OCR
62
  def extract_text_from_image(image):
63
  try:
64
  if image is None:
65
  return "No image captured. Please try again."
66
+
67
+ # Verify Tesseract executable is accessible
68
+ try:
69
+ subprocess.run([pytesseract.pytesseract.tesseract_cmd, "--version"],
70
+ check=True, capture_output=True, text=True)
71
+ except (subprocess.SubprocessError, FileNotFoundError):
72
+ return "Tesseract OCR is not installed or not properly configured. Please check installation."
73
+
74
+ # Image preprocessing for better OCR
75
+ import cv2
76
+ import numpy as np
77
+
78
+ # Convert PIL image to OpenCV format
79
+ img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
80
+
81
+ # Convert to grayscale
82
+ gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
83
+
84
+ # Apply thresholding to get black and white image
85
+ _, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
86
+
87
+ # Noise removal
88
+ kernel = np.ones((1, 1), np.uint8)
89
+ binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
90
+
91
+ # Dilate to connect text
92
+ binary = cv2.dilate(binary, kernel, iterations=1)
93
+
94
+ # Convert back to PIL image for tesseract
95
+ binary_pil = Image.fromarray(cv2.bitwise_not(binary))
96
+
97
+ # Run OCR with improved configuration
98
+ custom_config = r'--oem 3 --psm 6 -l eng'
99
+ text = pytesseract.image_to_string(binary_pil, config=custom_config)
100
+
101
+ if not text.strip():
102
+ # Try original image as fallback
103
+ text = pytesseract.image_to_string(image, config=custom_config)
104
+
105
+ if not text.strip():
106
+ return "No text could be extracted. Ensure image is clear and readable."
107
+
108
  return text
109
  except Exception as e:
110
  return f"Error extracting text: {str(e)}"
 
111
  # Function to parse ingredients from text
112
  def parse_ingredients(text):
 
113
  if not text:
114
  return []
115
 
116
+ # Clean up the text
117
  text = re.sub(r'^ingredients:?\s*', '', text.lower(), flags=re.IGNORECASE)
118
+
119
+ # Remove common OCR errors and extraneous characters
120
+ text = re.sub(r'[|\\/@#$%^&*()_+=]', '', text)
121
+
122
+ # Replace common OCR errors
123
+ text = re.sub(r'\bngredients\b', 'ingredients', text)
124
+
125
+ # Handle common OCR misreads
126
+ replacements = {
127
+ '0': 'o', 'l': 'i', '1': 'i',
128
+ '5': 's', '8': 'b', 'Q': 'g',
129
+ }
130
+
131
+ for error, correction in replacements.items():
132
+ text = text.replace(error, correction)
133
+
134
  # Split by common ingredient separators
135
  ingredients = re.split(r',|;|\n', text)
136
+
137
+ # Clean up each ingredient
138
+ cleaned_ingredients = []
139
+ for i in ingredients:
140
+ i = i.strip().lower()
141
+ if i and len(i) > 1: # Ignore single characters which are likely OCR errors
142
+ cleaned_ingredients.append(i)
143
+
144
+ return cleaned_ingredients
145
 
146
+ # Function to analyze ingredients with Gemini
147
  # Function to analyze ingredients with Gemini
148
  def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
149
  """
 
154
 
155
  # Prepare the list of ingredients for the prompt
156
  ingredients_text = ", ".join(ingredients_list)
157
+
158
+ # Add debug print to check API key at function call time
159
+ print(f"GEMINI_API_KEY available: {bool(GEMINI_API_KEY)}")
160
+
161
+ # Check if Gemini API is available
162
+ if not genai or not GEMINI_API_KEY:
163
+ print("Falling back to dummy analysis: API or key not available")
164
+ return dummy_analyze(ingredients_list, health_conditions)
165
+
166
  # Create a prompt for Gemini
167
  if health_conditions and health_conditions.strip():
168
  prompt = f"""
169
  Analyze the following food ingredients for a person with these health conditions: {health_conditions}
 
170
  Ingredients: {ingredients_text}
 
171
  For each ingredient:
172
  1. Provide its potential health benefits
173
  2. Identify any potential risks
174
  3. Note if it may affect the specified health conditions
 
175
  Then provide an overall assessment of the product's suitability for someone with the specified health conditions.
176
  Format your response in markdown with clear headings and sections.
177
  """
178
  else:
179
  prompt = f"""
180
  Analyze the following food ingredients:
 
181
  Ingredients: {ingredients_text}
 
182
  For each ingredient:
183
  1. Provide its potential health benefits
184
  2. Identify any potential risks or common allergens associated with it
 
185
  Then provide an overall assessment of the product's general health profile.
186
  Format your response in markdown with clear headings and sections.
187
  """
188
 
189
  try:
190
+ print("Attempting to use Gemini API...")
191
+ # First, check available models
192
+ try:
193
+ models = genai.list_models()
194
+ available_models = [m.name for m in models]
195
+ print(f"Available models: {available_models}")
196
+
197
+ # Try models in order of preference
198
+ model_names = ['gemini-pro', 'gemini-1.5-pro', 'gemini-1.0-pro']
199
+
200
+ # Find first available model from our preference list
201
+ model_name = None
202
+ for name in model_names:
203
+ if any(name in m for m in available_models):
204
+ model_name = name
205
+ break
206
+
207
+ # If none of our preferred models are available, use the first available model
208
+ if not model_name and available_models:
209
+ model_name = available_models[0]
210
+
211
+ print(f"Selected model: {model_name}")
212
+
213
+ if not model_name:
214
+ print("No available models found")
215
+ return dummy_analyze(ingredients_list, health_conditions) + "\n\n(Using fallback analysis: No available models found)"
216
+
217
+ model = genai.GenerativeModel(model_name)
218
+ print(f"Sending prompt to {model_name}...")
219
+ response = model.generate_content(prompt)
220
+
221
+ # Check if response is valid
222
+ if hasattr(response, 'text') and response.text:
223
+ analysis = response.text
224
+ print("Received valid response from Gemini API")
225
+ else:
226
+ print("Empty response from API")
227
+ return dummy_analyze(ingredients_list, health_conditions) + "\n\n(Using fallback analysis: Empty API response)"
228
+
229
+ except Exception as e:
230
+ print(f"Error accessing Gemini API models: {str(e)}")
231
+ return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
232
+
233
+ # Add disclaimer
234
+ disclaimer = """
235
+ ## Disclaimer
236
+ This analysis is provided for informational purposes only and should not replace professional medical advice.
237
+ Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
238
+ """
239
 
240
+ return analysis + disclaimer
241
+
242
+ except Exception as e:
243
+ # Fallback to basic analysis if API call fails
244
+ print(f"Overall error in analyze_ingredients_with_gemini: {str(e)}")
245
+ return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
246
 
247
  # Add disclaimer
248
  disclaimer = """
 
255
 
256
  except Exception as e:
257
  # Fallback to basic analysis if API call fails
258
+ return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
259
+ # Dummy analysis function for when API is not available
260
+ def dummy_analyze(ingredients_list, health_conditions=None):
261
+ ingredients_text = ", ".join(ingredients_list)
262
+
263
+ report = f"""
264
+ # Ingredient Analysis Report
265
+ ## Detected Ingredients
266
+ {", ".join([i.title() for i in ingredients_list])}
267
+ ## Overview
268
+ This is a simulated analysis since no API key was provided. In the actual application,
269
+ the ingredients would be analyzed by an LLM for their health implications.
270
+ ## Health Considerations
271
+ """
272
+
273
+ if health_conditions:
274
+ report += f"""
275
+ The analysis would specifically consider these health concerns: {health_conditions}
276
+ """
277
+ else:
278
+ report += """
279
+ No specific health concerns were provided, so a general analysis would be performed.
280
+ """
281
+
282
+ report += """
283
+ ## Disclaimer
284
+ This analysis is provided for informational purposes only and should not replace professional medical advice.
285
+ Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
286
+ """
287
+
288
+ return report
289
 
290
  # Function to process input based on method (camera, upload, or manual entry)
291
  def process_input(input_method, text_input, camera_input, upload_input, health_conditions):
292
  if input_method == "Camera":
293
  if camera_input is not None:
294
  extracted_text = extract_text_from_image(camera_input)
295
+ # If OCR fails, inform the user they can try manual entry
296
+ if "Error" in extracted_text or "No text could be extracted" in extracted_text:
297
+ return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
298
+
299
  ingredients = parse_ingredients(extracted_text)
300
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
301
  else:
 
304
  elif input_method == "Image Upload":
305
  if upload_input is not None:
306
  extracted_text = extract_text_from_image(upload_input)
307
+ # If OCR fails, inform the user they can try manual entry
308
+ if "Error" in extracted_text or "No text could be extracted" in extracted_text:
309
+ return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
310
+
311
  ingredients = parse_ingredients(extracted_text)
312
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
313
  else:
314
  return "No image uploaded. Please try again."
315
 
316
  elif input_method == "Manual Entry":
317
+ if text_input and text_input.strip():
318
  ingredients = parse_ingredients(text_input)
319
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
320
  else:
 
336
  )
337
 
338
  # Camera input
339
+ camera_input = gr.Image(label="Capture ingredients with camera", type="pil", visible=True)
340
 
341
  # Image upload
342
  upload_input = gr.Image(label="Upload image of ingredients label", type="pil", visible=False)
 
366
  # Show/hide inputs based on selection
367
  def update_visible_inputs(choice):
368
  return {
369
+ upload_input: gr.update(visible=(choice == "Image Upload")),
370
+ camera_input: gr.update(visible=(choice == "Camera")),
371
+ text_input: gr.update(visible=(choice == "Manual Entry"))
372
  }
373
 
374
  input_method.change(update_visible_inputs, input_method, [upload_input, camera_input, text_input])
 
402
  2. Take a photo of the ingredients label or enter ingredients manually
403
  3. Optionally enter your health concerns
404
  4. Click "Analyze Ingredients" to get your personalized analysis
 
405
  The AI will automatically analyze the ingredients, their health implications, and their potential impact on your specific health concerns.
406
  """)
407
 
 
413
  - Allergies: "peanut allergy" or "shellfish allergy"
414
  - Dietary restrictions: "vegetarian" or "gluten-free diet"
415
  - Multiple conditions: "diabetes, high cholesterol, and lactose intolerance"
 
416
  The AI will tailor its analysis to your specific needs.
417
  """)
418
 
 
430
  Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
431
  """)
432
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  # Launch the app
434
  if __name__ == "__main__":
435
+ app.launch()