dev2607 commited on
Commit
a32bfb9
·
verified ·
1 Parent(s): 8e5f0b8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -110
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,120 @@ 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 +140,68 @@ 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 +214,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 +263,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 +295,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 +325,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 +361,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 +372,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 +389,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
+
33
+ # Load environment variables
34
+ load_dotenv()
35
 
36
+ # Import and configure Gemini API
37
+ try:
38
+ import google.generativeai as genai
39
+ # Configure Gemini API
40
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
41
+ if GEMINI_API_KEY:
42
+ genai.configure(api_key=GEMINI_API_KEY)
43
+ except ImportError:
44
+ print("Google Generative AI package not found, using dummy implementation")
45
+ genai = None
46
+
47
+ # Function to extract text from images using OCR
48
  def extract_text_from_image(image):
49
  try:
50
  if image is None:
51
  return "No image captured. Please try again."
52
 
53
+ # Verify Tesseract executable is accessible
54
+ try:
55
+ subprocess.run([pytesseract.pytesseract.tesseract_cmd, "--version"],
56
+ check=True, capture_output=True, text=True)
57
+ except (subprocess.SubprocessError, FileNotFoundError):
58
+ return "Tesseract OCR is not installed or not properly configured. Please check installation."
59
+
60
+ # Image preprocessing for better OCR
61
+ import cv2
62
+ import numpy as np
63
+
64
+ # Convert PIL image to OpenCV format
65
+ img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
66
+
67
+ # Convert to grayscale
68
+ gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
69
+
70
+ # Apply thresholding to get black and white image
71
+ _, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
72
+
73
+ # Noise removal
74
+ kernel = np.ones((1, 1), np.uint8)
75
+ binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
76
+
77
+ # Dilate to connect text
78
+ binary = cv2.dilate(binary, kernel, iterations=1)
79
+
80
+ # Convert back to PIL image for tesseract
81
+ binary_pil = Image.fromarray(cv2.bitwise_not(binary))
82
 
83
+ # Run OCR with improved configuration
84
+ custom_config = r'--oem 3 --psm 6 -l eng'
85
+ text = pytesseract.image_to_string(binary_pil, config=custom_config)
86
 
87
+ if not text.strip():
88
+ # Try original image as fallback
89
+ text = pytesseract.image_to_string(image, config=custom_config)
90
+
91
  if not text.strip():
92
  return "No text could be extracted. Ensure image is clear and readable."
93
 
94
  return text
95
  except Exception as e:
96
  return f"Error extracting text: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  # Function to parse ingredients from text
98
  def parse_ingredients(text):
 
99
  if not text:
100
  return []
101
 
102
+ # Clean up the text
103
  text = re.sub(r'^ingredients:?\s*', '', text.lower(), flags=re.IGNORECASE)
104
+
105
+ # Remove common OCR errors and extraneous characters
106
+ text = re.sub(r'[|\\/@#$%^&*()_+=]', '', text)
107
+
108
+ # Replace common OCR errors
109
+ text = re.sub(r'\bngredients\b', 'ingredients', text)
110
+
111
+ # Handle common OCR misreads
112
+ replacements = {
113
+ '0': 'o', 'l': 'i', '1': 'i',
114
+ '5': 's', '8': 'b', 'Q': 'g',
115
+ }
116
+
117
+ for error, correction in replacements.items():
118
+ text = text.replace(error, correction)
119
+
120
  # Split by common ingredient separators
121
  ingredients = re.split(r',|;|\n', text)
122
+
123
+ # Clean up each ingredient
124
+ cleaned_ingredients = []
125
+ for i in ingredients:
126
+ i = i.strip().lower()
127
+ if i and len(i) > 1: # Ignore single characters which are likely OCR errors
128
+ cleaned_ingredients.append(i)
129
+
130
+ return cleaned_ingredients
131
 
132
+ # Function to analyze ingredients with Gemini
133
  # Function to analyze ingredients with Gemini
134
  def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
135
  """
 
140
 
141
  # Prepare the list of ingredients for the prompt
142
  ingredients_text = ", ".join(ingredients_list)
143
+
144
+ # Check if Gemini API is available
145
+ if not genai or not os.getenv("GEMINI_API_KEY"):
146
+ return dummy_analyze(ingredients_list, health_conditions)
147
+
148
  # Create a prompt for Gemini
149
  if health_conditions and health_conditions.strip():
150
  prompt = f"""
151
  Analyze the following food ingredients for a person with these health conditions: {health_conditions}
 
152
  Ingredients: {ingredients_text}
 
153
  For each ingredient:
154
  1. Provide its potential health benefits
155
  2. Identify any potential risks
156
  3. Note if it may affect the specified health conditions
 
157
  Then provide an overall assessment of the product's suitability for someone with the specified health conditions.
158
  Format your response in markdown with clear headings and sections.
159
  """
160
  else:
161
  prompt = f"""
162
  Analyze the following food ingredients:
 
163
  Ingredients: {ingredients_text}
 
164
  For each ingredient:
165
  1. Provide its potential health benefits
166
  2. Identify any potential risks or common allergens associated with it
 
167
  Then provide an overall assessment of the product's general health profile.
168
  Format your response in markdown with clear headings and sections.
169
  """
170
 
171
  try:
172
+ # First, check available models
173
+ try:
174
+ models = genai.list_models()
175
+ available_models = [m.name for m in models]
176
+
177
+ # Try models in order of preference
178
+ model_names = ['gemini-pro', 'gemini-1.5-pro', 'gemini-1.0-pro']
179
+
180
+ # Find first available model from our preference list
181
+ model_name = None
182
+ for name in model_names:
183
+ if any(name in m for m in available_models):
184
+ model_name = name
185
+ break
186
+
187
+ # If none of our preferred models are available, use the first available model
188
+ if not model_name and available_models:
189
+ model_name = available_models[0]
190
+
191
+ if not model_name:
192
+ return dummy_analyze(ingredients_list, health_conditions) + "\n\n(Using fallback analysis: No available models found)"
193
+
194
+ model = genai.GenerativeModel(model_name)
195
+ response = model.generate_content(prompt)
196
+
197
+ # Check if response is valid
198
+ if hasattr(response, 'text') and response.text:
199
+ analysis = response.text
200
+ else:
201
+ return dummy_analyze(ingredients_list, health_conditions) + "\n\n(Using fallback analysis: Empty API response)"
202
+
203
+ except Exception as e:
204
+ return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
205
 
206
  # Add disclaimer
207
  disclaimer = """
 
214
 
215
  except Exception as e:
216
  # Fallback to basic analysis if API call fails
217
+ return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
218
+ # Dummy analysis function for when API is not available
219
+ def dummy_analyze(ingredients_list, health_conditions=None):
220
+ ingredients_text = ", ".join(ingredients_list)
221
+
222
+ report = f"""
223
+ # Ingredient Analysis Report
224
+ ## Detected Ingredients
225
+ {", ".join([i.title() for i in ingredients_list])}
226
+ ## Overview
227
+ This is a simulated analysis since no API key was provided. In the actual application,
228
+ the ingredients would be analyzed by an LLM for their health implications.
229
+ ## Health Considerations
230
+ """
231
+
232
+ if health_conditions:
233
+ report += f"""
234
+ The analysis would specifically consider these health concerns: {health_conditions}
235
+ """
236
+ else:
237
+ report += """
238
+ No specific health concerns were provided, so a general analysis would be performed.
239
+ """
240
+
241
+ report += """
242
+ ## Disclaimer
243
+ This analysis is provided for informational purposes only and should not replace professional medical advice.
244
+ Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
245
+ """
246
+
247
+ return report
248
 
249
  # Function to process input based on method (camera, upload, or manual entry)
250
  def process_input(input_method, text_input, camera_input, upload_input, health_conditions):
251
  if input_method == "Camera":
252
  if camera_input is not None:
253
  extracted_text = extract_text_from_image(camera_input)
254
+ # If OCR fails, inform the user they can try manual entry
255
+ if "Error" in extracted_text or "No text could be extracted" in extracted_text:
256
+ return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
257
+
258
  ingredients = parse_ingredients(extracted_text)
259
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
260
  else:
 
263
  elif input_method == "Image Upload":
264
  if upload_input is not None:
265
  extracted_text = extract_text_from_image(upload_input)
266
+ # If OCR fails, inform the user they can try manual entry
267
+ if "Error" in extracted_text or "No text could be extracted" in extracted_text:
268
+ return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
269
+
270
  ingredients = parse_ingredients(extracted_text)
271
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
272
  else:
273
  return "No image uploaded. Please try again."
274
 
275
  elif input_method == "Manual Entry":
276
+ if text_input and text_input.strip():
277
  ingredients = parse_ingredients(text_input)
278
  return analyze_ingredients_with_gemini(ingredients, health_conditions)
279
  else:
 
295
  )
296
 
297
  # Camera input
298
+ camera_input = gr.Image(label="Capture ingredients with camera", type="pil", visible=True)
299
 
300
  # Image upload
301
  upload_input = gr.Image(label="Upload image of ingredients label", type="pil", visible=False)
 
325
  # Show/hide inputs based on selection
326
  def update_visible_inputs(choice):
327
  return {
328
+ upload_input: gr.update(visible=(choice == "Image Upload")),
329
+ camera_input: gr.update(visible=(choice == "Camera")),
330
+ text_input: gr.update(visible=(choice == "Manual Entry"))
331
  }
332
 
333
  input_method.change(update_visible_inputs, input_method, [upload_input, camera_input, text_input])
 
361
  2. Take a photo of the ingredients label or enter ingredients manually
362
  3. Optionally enter your health concerns
363
  4. Click "Analyze Ingredients" to get your personalized analysis
 
364
  The AI will automatically analyze the ingredients, their health implications, and their potential impact on your specific health concerns.
365
  """)
366
 
 
372
  - Allergies: "peanut allergy" or "shellfish allergy"
373
  - Dietary restrictions: "vegetarian" or "gluten-free diet"
374
  - Multiple conditions: "diabetes, high cholesterol, and lactose intolerance"
 
375
  The AI will tailor its analysis to your specific needs.
376
  """)
377
 
 
389
  Always consult with a healthcare provider regarding dietary restrictions, allergies, or health conditions.
390
  """)
391
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  # Launch the app
393
  if __name__ == "__main__":
394
+ app.launch()