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

Update app.py

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