dev2607 commited on
Commit
2022843
·
verified ·
1 Parent(s): 0b1e53f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -148
app.py CHANGED
@@ -33,122 +33,21 @@ except:
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") or GEMINI_API_KEY
41
- if GEMINI_API_KEY:
42
- genai.configure(api_key=GEMINI_API_KEY)
43
- print("Gemini API configured successfully")
44
- else:
45
- print("Warning: No Gemini API key found. Will use fallback analysis.")
46
- except ImportError:
47
- print("Google Generative AI package not found, using dummy implementation")
48
- genai = None
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
-
56
- # Verify Tesseract executable is accessible
57
- try:
58
- subprocess.run([pytesseract.pytesseract.tesseract_cmd, "--version"],
59
- check=True, capture_output=True, text=True)
60
- except (subprocess.SubprocessError, FileNotFoundError):
61
- return "Tesseract OCR is not installed or not properly configured. Please check installation."
62
-
63
- # Image preprocessing for better OCR
64
- import cv2
65
- import numpy as np
66
-
67
- # Convert PIL image to OpenCV format
68
- img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
69
-
70
- # Convert to grayscale
71
- gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
72
-
73
- # Apply thresholding to get black and white image
74
- _, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
75
-
76
- # Noise removal
77
- kernel = np.ones((1, 1), np.uint8)
78
- binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
79
-
80
- # Dilate to connect text
81
- binary = cv2.dilate(binary, kernel, iterations=1)
82
-
83
- # Convert back to PIL image for tesseract
84
- binary_pil = Image.fromarray(cv2.bitwise_not(binary))
85
-
86
- # Run OCR with improved configuration
87
- custom_config = r'--oem 3 --psm 6 -l eng'
88
- text = pytesseract.image_to_string(binary_pil, config=custom_config)
89
-
90
- if not text.strip():
91
- # Try original image as fallback
92
- text = pytesseract.image_to_string(image, config=custom_config)
93
-
94
- if not text.strip():
95
- return "No text could be extracted. Ensure image is clear and readable."
96
-
97
- return text
98
- except Exception as e:
99
- return f"Error extracting text: {str(e)}"
100
- # Function to parse ingredients from text
101
- def parse_ingredients(text):
102
- if not text:
103
- return []
104
-
105
- # Clean up the text
106
- text = re.sub(r'^ingredients:?\s*', '', text.lower(), flags=re.IGNORECASE)
107
-
108
- # Remove common OCR errors and extraneous characters
109
- text = re.sub(r'[|\\/@#$%^&*()_+=]', '', text)
110
-
111
- # Replace common OCR errors
112
- text = re.sub(r'\bngredients\b', 'ingredients', text)
113
-
114
- # Handle common OCR misreads
115
- replacements = {
116
- '0': 'o', 'l': 'i', '1': 'i',
117
- '5': 's', '8': 'b', 'Q': 'g',
118
- }
119
-
120
- for error, correction in replacements.items():
121
- text = text.replace(error, correction)
122
-
123
- # Split by common ingredient separators
124
- ingredients = re.split(r',|;|\n', text)
125
-
126
- # Clean up each ingredient
127
- cleaned_ingredients = []
128
- for i in ingredients:
129
- i = i.strip().lower()
130
- if i and len(i) > 1: # Ignore single characters which are likely OCR errors
131
- cleaned_ingredients.append(i)
132
-
133
- return cleaned_ingredients
134
-
135
- # Function to analyze ingredients with Gemini
136
- # Function to analyze ingredients with Gemini
137
- def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
138
  """
139
- Use Gemini to analyze ingredients and provide health insights
140
  """
141
  if not ingredients_list:
142
  return "No ingredients detected or provided."
143
 
144
  # Prepare the list of ingredients for the prompt
145
  ingredients_text = ", ".join(ingredients_list)
146
-
147
- # Check if Gemini API is available
148
- if not genai or not os.getenv("GEMINI_API_KEY"):
149
- return dummy_analyze(ingredients_list, health_conditions)
150
-
151
- # Create a prompt for Gemini
152
  if health_conditions and health_conditions.strip():
153
  prompt = f"""
154
  Analyze the following food ingredients for a person with these health conditions: {health_conditions}
@@ -172,39 +71,22 @@ def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
172
  """
173
 
174
  try:
175
- # First, check available models
176
- try:
177
- models = genai.list_models()
178
- available_models = [m.name for m in 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
- if not model_name:
195
- return dummy_analyze(ingredients_list, health_conditions) + "\n\n(Using fallback analysis: No available models found)"
196
-
197
- model = genai.GenerativeModel(model_name)
198
- response = model.generate_content(prompt)
199
-
200
- # Check if response is valid
201
- if hasattr(response, 'text') and response.text:
202
- analysis = response.text
203
- else:
204
- return dummy_analyze(ingredients_list, health_conditions) + "\n\n(Using fallback analysis: Empty API response)"
205
-
206
- except Exception as e:
207
- return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
208
 
209
  # Add disclaimer
210
  disclaimer = """
@@ -218,6 +100,8 @@ def analyze_ingredients_with_gemini(ingredients_list, health_conditions=None):
218
  except Exception as e:
219
  # Fallback to basic analysis if API call fails
220
  return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
 
 
221
  # Dummy analysis function for when API is not available
222
  def dummy_analyze(ingredients_list, health_conditions=None):
223
  ingredients_text = ", ".join(ingredients_list)
@@ -227,8 +111,8 @@ def dummy_analyze(ingredients_list, health_conditions=None):
227
  ## Detected Ingredients
228
  {", ".join([i.title() for i in ingredients_list])}
229
  ## Overview
230
- This is a simulated analysis since no API key was provided. In the actual application,
231
- the ingredients would be analyzed by an LLM for their health implications.
232
  ## Health Considerations
233
  """
234
 
@@ -249,6 +133,93 @@ def dummy_analyze(ingredients_list, health_conditions=None):
249
 
250
  return report
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  # Function to process input based on method (camera, upload, or manual entry)
253
  def process_input(input_method, text_input, camera_input, upload_input, health_conditions):
254
  if input_method == "Camera":
@@ -257,9 +228,9 @@ def process_input(input_method, text_input, camera_input, upload_input, health_c
257
  # If OCR fails, inform the user they can try manual entry
258
  if "Error" in extracted_text or "No text could be extracted" in extracted_text:
259
  return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
260
-
261
  ingredients = parse_ingredients(extracted_text)
262
- return analyze_ingredients_with_gemini(ingredients, health_conditions)
263
  else:
264
  return "No camera image captured. Please try again."
265
 
@@ -269,16 +240,16 @@ def process_input(input_method, text_input, camera_input, upload_input, health_c
269
  # If OCR fails, inform the user they can try manual entry
270
  if "Error" in extracted_text or "No text could be extracted" in extracted_text:
271
  return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
272
-
273
  ingredients = parse_ingredients(extracted_text)
274
- return analyze_ingredients_with_gemini(ingredients, health_conditions)
275
  else:
276
  return "No image uploaded. Please try again."
277
 
278
  elif input_method == "Manual Entry":
279
  if text_input and text_input.strip():
280
  ingredients = parse_ingredients(text_input)
281
- return analyze_ingredients_with_gemini(ingredients, health_conditions)
282
  else:
283
  return "No ingredients entered. Please try again."
284
 
 
33
  # Load environment variables
34
  load_dotenv()
35
 
36
+ # Mistral API Key
37
+ MISTRAL_API_KEY = "GlrVCBWyvTYjWGKl5jqtK4K41uWWJ79F"
 
 
 
 
 
 
 
 
 
 
 
38
 
39
+ # Import and configure Mistral API
40
+ def analyze_ingredients_with_mistral(ingredients_list, health_conditions=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  """
42
+ Use Mistral AI to analyze ingredients and provide health insights.
43
  """
44
  if not ingredients_list:
45
  return "No ingredients detected or provided."
46
 
47
  # Prepare the list of ingredients for the prompt
48
  ingredients_text = ", ".join(ingredients_list)
49
+
50
+ # Create a prompt for Mistral
 
 
 
 
51
  if health_conditions and health_conditions.strip():
52
  prompt = f"""
53
  Analyze the following food ingredients for a person with these health conditions: {health_conditions}
 
71
  """
72
 
73
  try:
74
+ headers = {
75
+ "Authorization": f"Bearer {MISTRAL_API_KEY}",
76
+ "Content-Type": "application/json"
77
+ }
78
+ data = {
79
+ "model": "mistral-small", # Or another suitable model
80
+ "messages": [{"role": "user", "content": prompt}],
81
+ "temperature": 0.7,
82
+ }
83
+
84
+ response = requests.post("https://api.mistral.ai/v1/chat/completions", headers=headers, json=data)
85
+
86
+ if response.status_code == 200:
87
+ analysis = response.json()['choices'][0]['message']['content']
88
+ else:
89
+ return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: Mistral API Error - {response.status_code} - {response.text})"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
  # Add disclaimer
92
  disclaimer = """
 
100
  except Exception as e:
101
  # Fallback to basic analysis if API call fails
102
  return dummy_analyze(ingredients_list, health_conditions) + f"\n\n(Using fallback analysis: {str(e)})"
103
+
104
+
105
  # Dummy analysis function for when API is not available
106
  def dummy_analyze(ingredients_list, health_conditions=None):
107
  ingredients_text = ", ".join(ingredients_list)
 
111
  ## Detected Ingredients
112
  {", ".join([i.title() for i in ingredients_list])}
113
  ## Overview
114
+ This is a simulated analysis since the Mistral API call failed. In the actual application,
115
+ the ingredients would be analyzed by Mistral for their health implications.
116
  ## Health Considerations
117
  """
118
 
 
133
 
134
  return report
135
 
136
+ # Function to extract text from images using OCR
137
+ def extract_text_from_image(image):
138
+ try:
139
+ if image is None:
140
+ return "No image captured. Please try again."
141
+
142
+ # Verify Tesseract executable is accessible
143
+ try:
144
+ subprocess.run([pytesseract.pytesseract.tesseract_cmd, "--version"],
145
+ check=True, capture_output=True, text=True)
146
+ except (subprocess.SubprocessError, FileNotFoundError):
147
+ return "Tesseract OCR is not installed or not properly configured. Please check installation."
148
+
149
+ # Image preprocessing for better OCR
150
+ import cv2
151
+ import numpy as np
152
+
153
+ # Convert PIL image to OpenCV format
154
+ img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
155
+
156
+ # Convert to grayscale
157
+ gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
158
+
159
+ # Apply thresholding to get black and white image
160
+ _, binary = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
161
+
162
+ # Noise removal
163
+ kernel = np.ones((1, 1), np.uint8)
164
+ binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
165
+
166
+ # Dilate to connect text
167
+ binary = cv2.dilate(binary, kernel, iterations=1)
168
+
169
+ # Convert back to PIL image for tesseract
170
+ binary_pil = Image.fromarray(cv2.bitwise_not(binary))
171
+
172
+ # Run OCR with improved configuration
173
+ custom_config = r'--oem 3 --psm 6 -l eng'
174
+ text = pytesseract.image_to_string(binary_pil, config=custom_config)
175
+
176
+ if not text.strip():
177
+ # Try original image as fallback
178
+ text = pytesseract.image_to_string(image, config=custom_config)
179
+
180
+ if not text.strip():
181
+ return "No text could be extracted. Ensure image is clear and readable."
182
+
183
+ return text
184
+ except Exception as e:
185
+ return f"Error extracting text: {str(e)}"
186
+
187
+ # Function to parse ingredients from text
188
+ def parse_ingredients(text):
189
+ if not text:
190
+ return []
191
+
192
+ # Clean up the text
193
+ text = re.sub(r'^ingredients:?\s*', '', text.lower(), flags=re.IGNORECASE)
194
+
195
+ # Remove common OCR errors and extraneous characters
196
+ text = re.sub(r'[|\\/@#$%^&*()_+=]', '', text)
197
+
198
+ # Replace common OCR errors
199
+ text = re.sub(r'\bngredients\b', 'ingredients', text)
200
+
201
+ # Handle common OCR misreads
202
+ replacements = {
203
+ '0': 'o', 'l': 'i', '1': 'i',
204
+ '5': 's', '8': 'b', 'Q': 'g',
205
+ }
206
+
207
+ for error, correction in replacements.items():
208
+ text = text.replace(error, correction)
209
+
210
+ # Split by common ingredient separators
211
+ ingredients = re.split(r',|;|\n', text)
212
+
213
+ # Clean up each ingredient
214
+ cleaned_ingredients = []
215
+ for i in ingredients:
216
+ i = i.strip().lower()
217
+ if i and len(i) > 1: # Ignore single characters which are likely OCR errors
218
+ cleaned_ingredients.append(i)
219
+
220
+ return cleaned_ingredients
221
+
222
+
223
  # Function to process input based on method (camera, upload, or manual entry)
224
  def process_input(input_method, text_input, camera_input, upload_input, health_conditions):
225
  if input_method == "Camera":
 
228
  # If OCR fails, inform the user they can try manual entry
229
  if "Error" in extracted_text or "No text could be extracted" in extracted_text:
230
  return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
231
+
232
  ingredients = parse_ingredients(extracted_text)
233
+ return analyze_ingredients_with_mistral(ingredients, health_conditions)
234
  else:
235
  return "No camera image captured. Please try again."
236
 
 
240
  # If OCR fails, inform the user they can try manual entry
241
  if "Error" in extracted_text or "No text could be extracted" in extracted_text:
242
  return extracted_text + "\n\nPlease try using the 'Manual Entry' option instead."
243
+
244
  ingredients = parse_ingredients(extracted_text)
245
+ return analyze_ingredients_with_mistral(ingredients, health_conditions)
246
  else:
247
  return "No image uploaded. Please try again."
248
 
249
  elif input_method == "Manual Entry":
250
  if text_input and text_input.strip():
251
  ingredients = parse_ingredients(text_input)
252
+ return analyze_ingredients_with_mistral(ingredients, health_conditions)
253
  else:
254
  return "No ingredients entered. Please try again."
255