Cruicis commited on
Commit
ac00431
·
verified ·
1 Parent(s): 23b9e95

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -120
app.py CHANGED
@@ -1,10 +1,10 @@
1
  import streamlit as st
2
 
3
-
4
  # Dish allergen recognition part
5
  # load the necessary libraries
6
  import matplotlib.pyplot as plt
7
  import seaborn
 
8
  # set the style of the plots
9
  plt.rcParams["figure.figsize"] = (15, 10)
10
  plt.rcParams["figure.dpi"] = 125
@@ -13,14 +13,15 @@ plt.rcParams['font.family'] = ['sans-serif']
13
  plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
14
  plt.style.use('ggplot')
15
  seaborn.set_style("whitegrid", {'axes.grid': False})
16
- plt.rcParams['image.cmap'] = 'gray' # grayscale looks better
17
- from pathlib import Path # Path management
18
  import numpy as np
19
  import pandas as pd
20
  import os
21
  from skimage.io import imread as imread
22
  from skimage.util import montage
23
  from PIL import Image
 
24
  montage_rgb = lambda x: np.stack([montage(x[:, :, :, i]) for i in range(x.shape[3])], -1)
25
  from skimage.color import label2rgb
26
 
@@ -32,7 +33,7 @@ mapping_file = Path('clean_list.json')
32
  alleg_df = pd.read_json(mapping_file)
33
 
34
  # add the image path into the mapping dataset
35
- alleg_df['image_path'] = alleg_df['image_path'].map(lambda x: image_dir / 'new' / x)
36
  print(alleg_df['image_path'].map(lambda x: x.exists()).value_counts())
37
  allergens = alleg_df.columns[3:].tolist()
38
  alleg_df.sample(2)
@@ -42,7 +43,7 @@ from pathlib import Path
42
  # load the color features
43
  color_file = Path('color_features.json')
44
  color_feat_df = pd.read_json(color_file)
45
- color_feat_df['image_path'] = color_feat_df['image_path'].map(lambda x: image_dir / 'new' / x)
46
 
47
  # create a dictionary of color features
48
  color_feat_dict = {c_row['image_path']: c_row['color_features'] for _, c_row in color_feat_df.iterrows()}
@@ -51,21 +52,21 @@ alleg_df['color_features'] = alleg_df['image_path'].map(color_feat_dict.get)
51
  alleg_df.sample(2)
52
 
53
  # calculate the correlation matrix of the allergens and draw the heatmap
54
- co_all = np.corrcoef(np.stack(alleg_df[allergens].applymap(lambda x: 1 if x>0 else 0).values, 0).T)
55
  fig, ax1 = plt.subplots(1, 1, figsize=(10, 10))
56
  seaborn.heatmap(co_all, annot=True, fmt='2.1%', ax=ax1, cmap='RdBu', vmin=-1, vmax=1)
57
  ax1.set_xticklabels(allergens, rotation=90)
58
  ax1.set_yticklabels(allergens);
59
 
60
-
61
  # package the allergens together
62
- alleg_df['allergy_vec'] = alleg_df[allergens].applymap(lambda x: 1 if x>0 else 0).values.tolist()
63
 
64
  # split the dataset into training and validation sets
65
  from sklearn.model_selection import train_test_split
66
- train_df, valid_df = train_test_split(alleg_df.drop(columns='ingredients_list'),
67
- test_size=0.1,
68
- random_state=2019,
 
69
  stratify=alleg_df['allergy_vec'].map(lambda x: x[0:3]))
70
 
71
  # reset the index of the training and validation datasets
@@ -76,26 +77,25 @@ valid_df.reset_index(inplace=True)
76
  print(train_df.shape[0], 'training images')
77
  print(valid_df.shape[0], 'validation images')
78
 
79
-
80
  # there are 8687 training images and 966 validation images
81
 
82
  # The np.stack() function is used to stack the data in the DataFrame into a NumPy array.
83
  train_x_vec = np.stack(train_df['color_features'].values, 0)
84
  train_y_vec = np.stack(train_df['allergy_vec'], 0)
85
 
86
-
87
  # load the necessary libraries for the model
88
 
89
  from sklearn.pipeline import make_pipeline
90
  from sklearn.ensemble import RandomForestRegressor
91
  from sklearn.preprocessing import RobustScaler # RobustScaler
92
  # create a pipeline for the model, it will make the model first scale the data and then use the Random Forest Regressor to train the model
93
- #rf_pipe = make_pipeline(RobustScaler(), RandomForestRegressor(n_estimators=15))
94
- #rf_pipe.fit(train_x_vec, train_y_vec)
95
  # But in this file, In order to shorten the running time of the app, we saved the trained model file in advance, and read the trained model directly here
96
 
97
 
98
  import joblib
 
99
  # read trained model result
100
  loaded_rf_pipe = joblib.load('rf_model.joblib')
101
 
@@ -107,11 +107,12 @@ from sklearn.metrics import roc_auc_score, roc_curve, accuracy_score
107
  import matplotlib.pyplot as plt
108
  from imageio import imread
109
 
 
110
  @st.cache_data
111
  def show_model_results(_in_model, picture_number=None):
112
  # Ensure 'valid_df', 'valid_x_vec', 'valid_y_vec', 'train_df', 'train_x_vec', 'train_y_vec', 'allergens' are defined
113
  # stack the data in the DataFrame into a NumPy array.
114
-
115
  valid_x_vec = np.stack(valid_df['color_features'].values, 0)
116
  valid_y_vec = np.stack(valid_df['allergy_vec'], 0)
117
 
@@ -131,9 +132,9 @@ def show_model_results(_in_model, picture_number=None):
131
  auc = roc_auc_score(y_vec[:, i], valid_pred[:, i])
132
  acc = accuracy_score(y_vec[:, i], valid_pred[:, i] > 0.5)
133
  ax1.plot(tpr, fpr, '.-', label='{}: AUC {:0.2f}, Accuracy: {:2.0%}'.format(c_allergen, auc, acc))
134
- all_rows += [{'allegen': c_allergen,
135
- 'prediction': valid_pred[j, i],
136
- 'class': 'Positive' if y_vec[j, i] > 0.5 else 'Negative'}
137
  for j in range(valid_pred.shape[0])]
138
 
139
  # Plotting the predictions of the model versus the true labels
@@ -148,13 +149,12 @@ def show_model_results(_in_model, picture_number=None):
148
  d_ax.set_title('Overall')
149
  d_ax.legend()
150
 
151
-
152
  # Plotting the predictions of the model versus the true labels for the test picture
153
-
154
  ax1.legend()
155
  for (_, c_row), (c_ax, d_ax) in zip(
156
- valid_df.iloc[valid_num:valid_num+1].iterrows(),
157
- m_axs[1:]):
158
  c_ax.imshow(imread(c_row['image_path']))
159
  c_ax.set_title(c_row['title'])
160
  c_ax.axis('off')
@@ -166,18 +166,20 @@ def show_model_results(_in_model, picture_number=None):
166
  d_ax.set_yticklabels(allergens, rotation=0)
167
  d_ax.set_xlim(0, 1)
168
  d_ax.legend()
169
-
170
  # add the current figures into Streamlit page
171
  st.pyplot(fig)
172
  return st.write("Completed")
173
 
 
174
  # Get the image paths
175
  image_paths = valid_df['image_path'].tolist()
176
 
177
 
178
  # Streamlit app
179
  def recognition():
180
- st.write(f'<span style="font-size:20px;">We use the images from the test set as examples.</span>', unsafe_allow_html=True)
 
181
 
182
  image_paths = valid_df['image_path'].tolist()
183
 
@@ -192,46 +194,47 @@ def recognition():
192
  image = imread(image_paths[index])
193
  axes[i, j].imshow(image)
194
  axes[i, j].axis('off')
195
- axes[i, j].set_title(f'Image {index+1}')
196
 
197
  st.pyplot(fig)
198
 
199
- num_images = 10
200
 
201
  # User interaction to select image
202
- st.write(f'<span style="font-size:20px;">Enter the image number you want to analyze.</span>', unsafe_allow_html=True)
 
203
 
204
  # User input
205
  choice = st.number_input(f"Range (1-{num_images}): ", min_value=1, max_value=num_images)
206
-
207
  # Show model results
208
  if st.button('Show Results'):
209
- show_model_results(loaded_rf_pipe, choice-1)
210
-
211
-
212
 
213
 
214
  # Menu-allergen database part
215
  # load the libraries we need
216
  import streamlit as st
217
  import pandas as pd
218
- import plotly.express as px # Streamlined plotting and interactive visualization
 
219
  # input our dataset
220
 
221
  # Create a text element and let customers know the data is loading.
222
  data = pd.read_csv('data.csv')
223
 
 
224
  # set the function to filter the allgergens customers may have
225
  # The mask is created by negating the result of checking whether the 'Allergens' column in the data contains any of the allergens joined by '|'.
226
  # | (pipe symbol) is used for "or" logic when joining the allergens that means the union of selected allergons will be filtered.
227
  def filter_products(allergens):
228
  # The ~ in front of it negates the result. This is for deleting the food data containing the allergens
229
- mask = ~data['Allergens'].str.contains('|'.join(allergens), case=False, na=False) # case=False shows not considering case, and na=False handles missing values appropriately.
 
230
  filtered_data = data[mask]
231
  return filtered_data
232
 
233
 
234
-
235
  import streamlit as st
236
  import folium
237
  from streamlit_folium import st_folium
@@ -242,72 +245,76 @@ from dotenv import load_dotenv
242
 
243
  import streamlit as st
244
 
245
-
246
-
247
-
248
-
249
-
250
-
251
- page = st.sidebar.radio("Page", ["Home Page", "Menu-allergen database", "Dish_Allergen_Recognition", "Hospital Navigator"])
252
 
253
  if page == "Home Page":
254
  st.title("TrallergyFree")
255
  st.image('logo.jpg')
256
- st.write("The purpose of the TrallergyFree app is to provide a reliable and convenient solution for individuals with food allergies, ensuring their safety and peace of mind in various dining situations. The app aims to assist users in identifying allergens in dishes, particularly when they are unable to communicate effectively about their dietary restrictions, such as when traveling abroad. By leveraging advanced image recognition technology, TrallergyFree empowers users to make informed decisions about their food choices, thereby reducing the risk of allergic reactions and enhancing their overall dining experience.")
257
-
 
258
 
259
  elif page == "Menu-allergen database":
260
  st.title("Menu-allergen database")
261
  # st.multiselect is a widget in the streamlit library. It is used to create a multiple selection dropdown menu.
262
  # It allows users to select multiple options from a given list of choices which are collected by unique
263
 
264
- allergens = st.multiselect("What foods are you allergic to?",
265
- [
266
- "Wheat",
267
- "Nuts",
268
- "Sesame",
269
- "Soy",
270
- "Mustard",
271
- "Celery",
272
- "Dairy",
273
- "Egg",
274
- 'Others',
275
- ]
276
- )
277
-
278
  # When customers select one or more allergens, our interactive page will recommend the dishes that don't contain the allergens
279
  if allergens:
280
  # Filter and display the recommendations
281
- recommended_products = filter_products(allergens)
282
- if not recommended_products.empty: # case when there is food not containing the selected allergens
283
  # Group products by type and create a tab for each type
284
  st.write("Below is a list of allergen-free products. Please make your selection✅")
285
- #st.write("🔸You can choose the food you want to eat and select it in the box⬜🔸")
286
- product_types = recommended_products['type'].unique() # de-weighted product types for choosing
287
- tabs = st.tabs([str(pt) for pt in product_types]) # show tabs function
288
-
289
  # create an empty dataframe for all selected products
290
  all_selected_products = pd.DataFrame()
291
- for i, tab in enumerate(tabs): # interactive function with tabs for products
292
  with tab:
293
  type_specific_products = recommended_products[recommended_products['type'] == product_types[i]]
294
- type_specific_products['selected'] = False # This adds a 'selected' column and initializes it to False.
 
295
  for j, row in type_specific_products.iterrows():
296
  # create columns and display the product name and allergens
297
- cols = st.columns([1, 1, 1])
298
  cols[0].write(row['Products'])
299
  cols[1].write(row['Allergens'])
300
- selected = cols[2].checkbox("Select", key=f"select_{i}_{j}") # set a checkbox for selection. The key is set uniquely for each row and tab
301
- type_specific_products.at[j, 'selected'] = selected # Updates the 'selected' value in the type-specific product based on the checkbox state
 
 
302
  selected_products = type_specific_products[type_specific_products['selected']]
303
- all_selected_products = pd.concat([all_selected_products, selected_products]) # row concatenation, the axis in pd.concat is 0 in default
304
-
305
- if not all_selected_products.empty: # case when customers select products
 
306
  st.divider()
307
  st.write("Below is the nutritional information for the selected products:")
308
- st.dataframe(all_selected_products[['type', 'Products', 'Allergens', 'Total Energy (Kcal)', 'Carbohydrate (g)', 'Protein (g)', 'Total Fat (g)', 'Sodium (g)', 'Sugar (g)']])
309
- nutritional_totals = all_selected_products[['Total Energy (Kcal)', 'Carbohydrate (g)', 'Protein (g)', 'Total Fat (g)', 'Sodium (g)', 'Sugar (g)']].sum()
310
-
 
 
 
 
311
  # Display metrics for nutritional totals in a 3x2 grid
312
  st.divider()
313
  st.write("Below are the nutritional totals for the selected products:")
@@ -321,47 +328,50 @@ elif page == "Menu-allergen database":
321
  with col3:
322
  st.metric("Protein", f"{nutritional_totals['Protein (g)']} g")
323
  st.metric("Sugar", f"{nutritional_totals['Sugar (g)']} g")
324
-
325
  st.divider()
326
  st.write("Below are the nutrient proportions of the selected products:")
327
- # Create pie charts for each nutritional metric in 3x2 grid
328
  # If they are multi selections, each product will contribute proportion in the pie chart
329
- metrics = ['Total Energy (Kcal)', 'Carbohydrate (g)', 'Protein (g)', 'Total Fat (g)', 'Sodium (g)', 'Sugar (g)']
 
330
  for i in range(0, len(metrics), 2):
331
  cols = st.columns(2) # Define three columns
332
  for j in range(2):
333
  if i + j < len(metrics): # Check if the current index is within the metrics array
334
  with cols[j]:
335
  metric = metrics[i + j]
336
- fig = px.pie(all_selected_products, values=metric, names='Products', title=f'Proportion of {metric}',
337
- width=300, height=300) # Adjust width and height if necessary
 
338
  st.plotly_chart(fig)
339
  else:
340
- st.write("No selected products in this category.") # case when no product is selected
341
  else:
342
- st.write("No products found without the specified allergens.") # case when there is no food without selected allergens
343
-
 
344
  elif page == "Dish_Allergen_Recognition":
345
  st.title('Dish Allergen Recognition')
346
  if __name__ == "__main__":
347
  recognition()
348
- elif page == "Hospital Navigator":
349
- st.title("Hospital Navigator")
350
  # Selection box for campus
351
  campus = st.selectbox(
352
  'Select the campus:',
353
  ('Yanqihu Campus', 'Zhongguancun Campus')
354
  )
355
-
356
  if campus == 'Yanqihu Campus':
357
  st.header('Hospitals and pharmacies within 2km of Yanqihu Campus')
358
-
359
  # User's location for Yanqihu Campus
360
  latitude = 40.407521
361
  longitude = 116.68452
362
-
363
  hospital_map = folium.Map(location=[latitude, longitude], zoom_start=12)
364
-
365
  # Add a marker for the user's location
366
  folium.Marker(
367
  location=[latitude, longitude],
@@ -369,7 +379,7 @@ elif page == "Hospital Navigator":
369
  tooltip="User's Location",
370
  icon=folium.Icon(color='black', icon="info-sign") # Using built-in icon
371
  ).add_to(hospital_map)
372
-
373
  df_hospitals = pd.read_excel('yanqihu_hospitals.xlsx', engine='openpyxl')
374
  st.write("The table below shows hospitals within 2km of you.🏥")
375
  st.dataframe(df_hospitals)
@@ -382,7 +392,7 @@ elif page == "Hospital Navigator":
382
  tooltip=row['name'],
383
  icon=folium.Icon(color='blue', icon="info-sign") # Using built-in icon
384
  ).add_to(hospital_map)
385
-
386
  df_pharmacies = pd.read_excel('yanqihu_pharmacies.xlsx', engine='openpyxl')
387
  st.write("The table below shows pharmacies within 2km of you.💊")
388
  st.dataframe(df_pharmacies)
@@ -395,19 +405,19 @@ elif page == "Hospital Navigator":
395
  tooltip=row['name'],
396
  icon=folium.Icon(color='purple', icon="info-sign") # Using built-in icon
397
  ).add_to(hospital_map)
398
-
399
  # Display the map in Streamlit
400
  st_folium(hospital_map, width=725, height=500)
401
-
402
  elif campus == 'Zhongguancun Campus':
403
  st.header('Hospitals and pharmacies within 2km of Zhongguancun Campus')
404
-
405
  # User's location for Zhongguancun Campus
406
  latitude = 39.979402
407
  longitude = 116.3134541
408
-
409
  hospital_map = folium.Map(location=[latitude, longitude], zoom_start=14)
410
-
411
  # Add a marker for the user's location
412
  folium.Marker(
413
  location=[latitude, longitude],
@@ -415,7 +425,7 @@ elif page == "Hospital Navigator":
415
  tooltip="User's Location",
416
  icon=folium.Icon(color='black', icon="info-sign") # Using built-in icon
417
  ).add_to(hospital_map)
418
-
419
  df_hospitals = pd.read_excel('zhongguancun_hospitals.xlsx', engine='openpyxl')
420
  st.write("The table below shows hospitals within 2km of you.🏥")
421
  st.dataframe(df_hospitals)
@@ -428,7 +438,7 @@ elif page == "Hospital Navigator":
428
  tooltip=row['name'],
429
  icon=folium.Icon(color='blue', icon="info-sign") # Using built-in icon
430
  ).add_to(hospital_map)
431
-
432
  df_pharmacies = pd.read_excel('zhongguancun_pharmacies.xlsx', engine='openpyxl')
433
  st.write("The table below shows pharmacies within 2km of you.💊")
434
  st.dataframe(df_pharmacies)
@@ -441,38 +451,33 @@ elif page == "Hospital Navigator":
441
  tooltip=row['name'],
442
  icon=folium.Icon(color='purple', icon="info-sign") # Using built-in icon
443
  ).add_to(hospital_map)
444
-
445
  # Display the map in Streamlit
446
  st_folium(hospital_map, width=725, height=500)
447
-
448
  # Explanation of color keys
449
  st.divider()
450
  st.write("Color Key:\n- Black🖤: Your current location📍\n- Blue💙: Hospitals🏥\n- Purple💜: Pharmacies💊")
451
-
452
- st.divider()
453
-
454
-
455
  # Streamlit page title
456
  st.title('Enter Your Location')
457
-
458
  # Allow users to input latitude and longitude
459
  latitude = st.number_input('Enter your latitude:(eg.39.918058)', value=0.0, format="%.6f")
460
  longitude = st.number_input('Enter your longitude:(eg.116.397026)', value=0.0, format="%.6f")
461
-
462
  # Display the inputted location information if valid
463
  if latitude and longitude:
464
  st.write(f"Your entered location is at latitude {latitude} and longitude {longitude}")
465
-
466
  # Load environment variables
467
  load_dotenv()
468
  api_key = os.getenv('api_key') # Your Amap API key
469
-
470
- # User's location
471
- #latitude = 39.993015
472
- #longitude = 116.473168
473
-
474
  hospital_map = folium.Map(location=[latitude, longitude], zoom_start=14)
475
-
476
  # Add a marker for the user's location
477
  folium.Marker(
478
  location=[latitude, longitude],
@@ -480,21 +485,21 @@ elif page == "Hospital Navigator":
480
  tooltip="User's Location",
481
  icon=folium.Icon(color='black')
482
  ).add_to(hospital_map)
483
-
484
  # Hospital API URL
485
  url_hospital = f'https://restapi.amap.com/v3/place/around?key={api_key}&location={longitude},{latitude}&types=090100&radius=2000&offset=20&page=1&extensions=all'
486
  # Pharmacy API URL
487
  url_pharmacy = f'https://restapi.amap.com/v3/place/around?key={api_key}&location={longitude},{latitude}&types=090601&radius=2000&offset=20&page=1&extensions=all'
488
-
489
  try:
490
  # Make API request for hospitals
491
  response_hospital = requests.get(url_hospital)
492
  response_pharmacy = requests.get(url_pharmacy) # API request for pharmacies
493
-
494
  if response_hospital.status_code == 200 and response_pharmacy.status_code == 200:
495
  data_hospital = response_hospital.json()
496
  data_pharmacy = response_pharmacy.json()
497
-
498
  # Process hospital data
499
  if 'pois' in data_hospital:
500
  df_hospitals = pd.DataFrame(data_hospital['pois'])
@@ -511,7 +516,7 @@ elif page == "Hospital Navigator":
511
  tooltip=row['name'],
512
  icon=folium.Icon(color='blue')
513
  ).add_to(hospital_map)
514
-
515
  # Process pharmacy data
516
  if 'pois' in data_pharmacy:
517
  df_pharmacies = pd.DataFrame(data_pharmacy['pois'])
@@ -528,11 +533,11 @@ elif page == "Hospital Navigator":
528
  tooltip=row['name'],
529
  icon=folium.Icon(color='purple') # Different color for pharmacies
530
  ).add_to(hospital_map)
531
-
532
  # Display the map in Streamlit
533
  st.title('Nearby Hospitals and Pharmacies')
534
  st_folium(hospital_map, width=725, height=500)
535
- st.write("Color Key:\n- Black🖤: Your current location📍\n- Blue💙: Hospitals🏥\n- Purple💜: Pharmacies💊")
536
  else:
537
  st.error("Failed to retrieve data. Check the status codes.")
538
  except Exception as e:
 
1
  import streamlit as st
2
 
 
3
  # Dish allergen recognition part
4
  # load the necessary libraries
5
  import matplotlib.pyplot as plt
6
  import seaborn
7
+
8
  # set the style of the plots
9
  plt.rcParams["figure.figsize"] = (15, 10)
10
  plt.rcParams["figure.dpi"] = 125
 
13
  plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
14
  plt.style.use('ggplot')
15
  seaborn.set_style("whitegrid", {'axes.grid': False})
16
+ plt.rcParams['image.cmap'] = 'gray' # grayscale looks better
17
+ from pathlib import Path # Path management
18
  import numpy as np
19
  import pandas as pd
20
  import os
21
  from skimage.io import imread as imread
22
  from skimage.util import montage
23
  from PIL import Image
24
+
25
  montage_rgb = lambda x: np.stack([montage(x[:, :, :, i]) for i in range(x.shape[3])], -1)
26
  from skimage.color import label2rgb
27
 
 
33
  alleg_df = pd.read_json(mapping_file)
34
 
35
  # add the image path into the mapping dataset
36
+ alleg_df['image_path'] = alleg_df['image_path'].map(lambda x: image_dir / 'new' / x)
37
  print(alleg_df['image_path'].map(lambda x: x.exists()).value_counts())
38
  allergens = alleg_df.columns[3:].tolist()
39
  alleg_df.sample(2)
 
43
  # load the color features
44
  color_file = Path('color_features.json')
45
  color_feat_df = pd.read_json(color_file)
46
+ color_feat_df['image_path'] = color_feat_df['image_path'].map(lambda x: image_dir / 'new' / x)
47
 
48
  # create a dictionary of color features
49
  color_feat_dict = {c_row['image_path']: c_row['color_features'] for _, c_row in color_feat_df.iterrows()}
 
52
  alleg_df.sample(2)
53
 
54
  # calculate the correlation matrix of the allergens and draw the heatmap
55
+ co_all = np.corrcoef(np.stack(alleg_df[allergens].applymap(lambda x: 1 if x > 0 else 0).values, 0).T)
56
  fig, ax1 = plt.subplots(1, 1, figsize=(10, 10))
57
  seaborn.heatmap(co_all, annot=True, fmt='2.1%', ax=ax1, cmap='RdBu', vmin=-1, vmax=1)
58
  ax1.set_xticklabels(allergens, rotation=90)
59
  ax1.set_yticklabels(allergens);
60
 
 
61
  # package the allergens together
62
+ alleg_df['allergy_vec'] = alleg_df[allergens].applymap(lambda x: 1 if x > 0 else 0).values.tolist()
63
 
64
  # split the dataset into training and validation sets
65
  from sklearn.model_selection import train_test_split
66
+
67
+ train_df, valid_df = train_test_split(alleg_df.drop(columns='ingredients_list'),
68
+ test_size=0.1,
69
+ random_state=2019,
70
  stratify=alleg_df['allergy_vec'].map(lambda x: x[0:3]))
71
 
72
  # reset the index of the training and validation datasets
 
77
  print(train_df.shape[0], 'training images')
78
  print(valid_df.shape[0], 'validation images')
79
 
 
80
  # there are 8687 training images and 966 validation images
81
 
82
  # The np.stack() function is used to stack the data in the DataFrame into a NumPy array.
83
  train_x_vec = np.stack(train_df['color_features'].values, 0)
84
  train_y_vec = np.stack(train_df['allergy_vec'], 0)
85
 
 
86
  # load the necessary libraries for the model
87
 
88
  from sklearn.pipeline import make_pipeline
89
  from sklearn.ensemble import RandomForestRegressor
90
  from sklearn.preprocessing import RobustScaler # RobustScaler
91
  # create a pipeline for the model, it will make the model first scale the data and then use the Random Forest Regressor to train the model
92
+ # rf_pipe = make_pipeline(RobustScaler(), RandomForestRegressor(n_estimators=15))
93
+ # rf_pipe.fit(train_x_vec, train_y_vec)
94
  # But in this file, In order to shorten the running time of the app, we saved the trained model file in advance, and read the trained model directly here
95
 
96
 
97
  import joblib
98
+
99
  # read trained model result
100
  loaded_rf_pipe = joblib.load('rf_model.joblib')
101
 
 
107
  import matplotlib.pyplot as plt
108
  from imageio import imread
109
 
110
+
111
  @st.cache_data
112
  def show_model_results(_in_model, picture_number=None):
113
  # Ensure 'valid_df', 'valid_x_vec', 'valid_y_vec', 'train_df', 'train_x_vec', 'train_y_vec', 'allergens' are defined
114
  # stack the data in the DataFrame into a NumPy array.
115
+
116
  valid_x_vec = np.stack(valid_df['color_features'].values, 0)
117
  valid_y_vec = np.stack(valid_df['allergy_vec'], 0)
118
 
 
132
  auc = roc_auc_score(y_vec[:, i], valid_pred[:, i])
133
  acc = accuracy_score(y_vec[:, i], valid_pred[:, i] > 0.5)
134
  ax1.plot(tpr, fpr, '.-', label='{}: AUC {:0.2f}, Accuracy: {:2.0%}'.format(c_allergen, auc, acc))
135
+ all_rows += [{'allegen': c_allergen,
136
+ 'prediction': valid_pred[j, i],
137
+ 'class': 'Positive' if y_vec[j, i] > 0.5 else 'Negative'}
138
  for j in range(valid_pred.shape[0])]
139
 
140
  # Plotting the predictions of the model versus the true labels
 
149
  d_ax.set_title('Overall')
150
  d_ax.legend()
151
 
 
152
  # Plotting the predictions of the model versus the true labels for the test picture
153
+
154
  ax1.legend()
155
  for (_, c_row), (c_ax, d_ax) in zip(
156
+ valid_df.iloc[valid_num:valid_num + 1].iterrows(),
157
+ m_axs[1:]):
158
  c_ax.imshow(imread(c_row['image_path']))
159
  c_ax.set_title(c_row['title'])
160
  c_ax.axis('off')
 
166
  d_ax.set_yticklabels(allergens, rotation=0)
167
  d_ax.set_xlim(0, 1)
168
  d_ax.legend()
169
+
170
  # add the current figures into Streamlit page
171
  st.pyplot(fig)
172
  return st.write("Completed")
173
 
174
+
175
  # Get the image paths
176
  image_paths = valid_df['image_path'].tolist()
177
 
178
 
179
  # Streamlit app
180
  def recognition():
181
+ st.write(f'<span style="font-size:20px;">We use the images from the test set as examples.</span>',
182
+ unsafe_allow_html=True)
183
 
184
  image_paths = valid_df['image_path'].tolist()
185
 
 
194
  image = imread(image_paths[index])
195
  axes[i, j].imshow(image)
196
  axes[i, j].axis('off')
197
+ axes[i, j].set_title(f'Image {index + 1}')
198
 
199
  st.pyplot(fig)
200
 
201
+ num_images = 10
202
 
203
  # User interaction to select image
204
+ st.write(f'<span style="font-size:20px;">Enter the image number you want to analyze.</span>',
205
+ unsafe_allow_html=True)
206
 
207
  # User input
208
  choice = st.number_input(f"Range (1-{num_images}): ", min_value=1, max_value=num_images)
209
+
210
  # Show model results
211
  if st.button('Show Results'):
212
+ show_model_results(loaded_rf_pipe, choice - 1)
 
 
213
 
214
 
215
  # Menu-allergen database part
216
  # load the libraries we need
217
  import streamlit as st
218
  import pandas as pd
219
+ import plotly.express as px # Streamlined plotting and interactive visualization
220
+
221
  # input our dataset
222
 
223
  # Create a text element and let customers know the data is loading.
224
  data = pd.read_csv('data.csv')
225
 
226
+
227
  # set the function to filter the allgergens customers may have
228
  # The mask is created by negating the result of checking whether the 'Allergens' column in the data contains any of the allergens joined by '|'.
229
  # | (pipe symbol) is used for "or" logic when joining the allergens that means the union of selected allergons will be filtered.
230
  def filter_products(allergens):
231
  # The ~ in front of it negates the result. This is for deleting the food data containing the allergens
232
+ mask = ~data['Allergens'].str.contains('|'.join(allergens), case=False,
233
+ na=False) # case=False shows not considering case, and na=False handles missing values appropriately.
234
  filtered_data = data[mask]
235
  return filtered_data
236
 
237
 
 
238
  import streamlit as st
239
  import folium
240
  from streamlit_folium import st_folium
 
245
 
246
  import streamlit as st
247
 
248
+ page = st.sidebar.radio("Page",
249
+ ["Home Page", "Menu-allergen database", "Dish_Allergen_Recognition", "Hospital Navigator"])
 
 
 
 
 
250
 
251
  if page == "Home Page":
252
  st.title("TrallergyFree")
253
  st.image('logo.jpg')
254
+ st.write(
255
+ "The purpose of the TrallergyFree app is to provide a reliable and convenient solution for individuals with food allergies, ensuring their safety and peace of mind in various dining situations. The app aims to assist users in identifying allergens in dishes, particularly when they are unable to communicate effectively about their dietary restrictions, such as when traveling abroad. By leveraging advanced image recognition technology, TrallergyFree empowers users to make informed decisions about their food choices, thereby reducing the risk of allergic reactions and enhancing their overall dining experience.")
256
+
257
 
258
  elif page == "Menu-allergen database":
259
  st.title("Menu-allergen database")
260
  # st.multiselect is a widget in the streamlit library. It is used to create a multiple selection dropdown menu.
261
  # It allows users to select multiple options from a given list of choices which are collected by unique
262
 
263
+ allergens = st.multiselect("What foods are you allergic to?",
264
+ [
265
+ "Wheat",
266
+ "Nuts",
267
+ "Sesame",
268
+ "Soy",
269
+ "Mustard",
270
+ "Celery",
271
+ "Dairy",
272
+ "Egg",
273
+ 'Others',
274
+ ]
275
+ )
276
+
277
  # When customers select one or more allergens, our interactive page will recommend the dishes that don't contain the allergens
278
  if allergens:
279
  # Filter and display the recommendations
280
+ recommended_products = filter_products(allergens)
281
+ if not recommended_products.empty: # case when there is food not containing the selected allergens
282
  # Group products by type and create a tab for each type
283
  st.write("Below is a list of allergen-free products. Please make your selection✅")
284
+ # st.write("🔸You can choose the food you want to eat and select it in the box⬜🔸")
285
+ product_types = recommended_products['type'].unique() # de-weighted product types for choosing
286
+ tabs = st.tabs([str(pt) for pt in product_types]) # show tabs function
287
+
288
  # create an empty dataframe for all selected products
289
  all_selected_products = pd.DataFrame()
290
+ for i, tab in enumerate(tabs): # interactive function with tabs for products
291
  with tab:
292
  type_specific_products = recommended_products[recommended_products['type'] == product_types[i]]
293
+ type_specific_products[
294
+ 'selected'] = False # This adds a 'selected' column and initializes it to False.
295
  for j, row in type_specific_products.iterrows():
296
  # create columns and display the product name and allergens
297
+ cols = st.columns([1, 1, 1])
298
  cols[0].write(row['Products'])
299
  cols[1].write(row['Allergens'])
300
+ selected = cols[2].checkbox("Select",
301
+ key=f"select_{i}_{j}") # set a checkbox for selection. The key is set uniquely for each row and tab
302
+ type_specific_products.at[
303
+ j, 'selected'] = selected # Updates the 'selected' value in the type-specific product based on the checkbox state
304
  selected_products = type_specific_products[type_specific_products['selected']]
305
+ all_selected_products = pd.concat([all_selected_products,
306
+ selected_products]) # row concatenation, the axis in pd.concat is 0 in default
307
+ #display the nutritional information of selected products
308
+ if not all_selected_products.empty: # case when customers select products
309
  st.divider()
310
  st.write("Below is the nutritional information for the selected products:")
311
+ st.dataframe(all_selected_products[
312
+ ['type', 'Products', 'Allergens', 'Total Energy (Kcal)', 'Carbohydrate (g)',
313
+ 'Protein (g)', 'Total Fat (g)', 'Sodium (g)', 'Sugar (g)']])
314
+ nutritional_totals = all_selected_products[
315
+ ['Total Energy (Kcal)', 'Carbohydrate (g)', 'Protein (g)', 'Total Fat (g)', 'Sodium (g)',
316
+ 'Sugar (g)']].sum()
317
+
318
  # Display metrics for nutritional totals in a 3x2 grid
319
  st.divider()
320
  st.write("Below are the nutritional totals for the selected products:")
 
328
  with col3:
329
  st.metric("Protein", f"{nutritional_totals['Protein (g)']} g")
330
  st.metric("Sugar", f"{nutritional_totals['Sugar (g)']} g")
331
+
332
  st.divider()
333
  st.write("Below are the nutrient proportions of the selected products:")
334
+ # Create pie charts for each nutritional metric in 2x3 grid
335
  # If they are multi selections, each product will contribute proportion in the pie chart
336
+ metrics = ['Total Energy (Kcal)', 'Carbohydrate (g)', 'Protein (g)', 'Total Fat (g)', 'Sodium (g)',
337
+ 'Sugar (g)']
338
  for i in range(0, len(metrics), 2):
339
  cols = st.columns(2) # Define three columns
340
  for j in range(2):
341
  if i + j < len(metrics): # Check if the current index is within the metrics array
342
  with cols[j]:
343
  metric = metrics[i + j]
344
+ fig = px.pie(all_selected_products, values=metric, names='Products',
345
+ title=f'Proportion of {metric}',
346
+ width=300, height=300) # Adjust width and height if necessary
347
  st.plotly_chart(fig)
348
  else:
349
+ st.write("No selected products in this category.") # case when no product is selected
350
  else:
351
+ st.write(
352
+ "No products found without the specified allergens.") # case when there is no food without selected allergens
353
+
354
  elif page == "Dish_Allergen_Recognition":
355
  st.title('Dish Allergen Recognition')
356
  if __name__ == "__main__":
357
  recognition()
358
+ elif page == "Hospital Navigator":
359
+ st.title("Hospital Navigator")
360
  # Selection box for campus
361
  campus = st.selectbox(
362
  'Select the campus:',
363
  ('Yanqihu Campus', 'Zhongguancun Campus')
364
  )
365
+ # Yanqihu campus part
366
  if campus == 'Yanqihu Campus':
367
  st.header('Hospitals and pharmacies within 2km of Yanqihu Campus')
368
+
369
  # User's location for Yanqihu Campus
370
  latitude = 40.407521
371
  longitude = 116.68452
372
+ # Create a map centered at the user's location
373
  hospital_map = folium.Map(location=[latitude, longitude], zoom_start=12)
374
+
375
  # Add a marker for the user's location
376
  folium.Marker(
377
  location=[latitude, longitude],
 
379
  tooltip="User's Location",
380
  icon=folium.Icon(color='black', icon="info-sign") # Using built-in icon
381
  ).add_to(hospital_map)
382
+ #import the dataset of hospitals nearby yanqihu campus
383
  df_hospitals = pd.read_excel('yanqihu_hospitals.xlsx', engine='openpyxl')
384
  st.write("The table below shows hospitals within 2km of you.🏥")
385
  st.dataframe(df_hospitals)
 
392
  tooltip=row['name'],
393
  icon=folium.Icon(color='blue', icon="info-sign") # Using built-in icon
394
  ).add_to(hospital_map)
395
+ #import the dataset of pharmacies nearby yanqihu campus
396
  df_pharmacies = pd.read_excel('yanqihu_pharmacies.xlsx', engine='openpyxl')
397
  st.write("The table below shows pharmacies within 2km of you.💊")
398
  st.dataframe(df_pharmacies)
 
405
  tooltip=row['name'],
406
  icon=folium.Icon(color='purple', icon="info-sign") # Using built-in icon
407
  ).add_to(hospital_map)
408
+
409
  # Display the map in Streamlit
410
  st_folium(hospital_map, width=725, height=500)
411
+
412
  elif campus == 'Zhongguancun Campus':
413
  st.header('Hospitals and pharmacies within 2km of Zhongguancun Campus')
414
+
415
  # User's location for Zhongguancun Campus
416
  latitude = 39.979402
417
  longitude = 116.3134541
418
+ # Create a map centered at the user's location
419
  hospital_map = folium.Map(location=[latitude, longitude], zoom_start=14)
420
+
421
  # Add a marker for the user's location
422
  folium.Marker(
423
  location=[latitude, longitude],
 
425
  tooltip="User's Location",
426
  icon=folium.Icon(color='black', icon="info-sign") # Using built-in icon
427
  ).add_to(hospital_map)
428
+ #import the dataset of hospitals nearby zhongguancun campus
429
  df_hospitals = pd.read_excel('zhongguancun_hospitals.xlsx', engine='openpyxl')
430
  st.write("The table below shows hospitals within 2km of you.🏥")
431
  st.dataframe(df_hospitals)
 
438
  tooltip=row['name'],
439
  icon=folium.Icon(color='blue', icon="info-sign") # Using built-in icon
440
  ).add_to(hospital_map)
441
+ #import the dataset of pharmacies nearby zhongguancun campus
442
  df_pharmacies = pd.read_excel('zhongguancun_pharmacies.xlsx', engine='openpyxl')
443
  st.write("The table below shows pharmacies within 2km of you.💊")
444
  st.dataframe(df_pharmacies)
 
451
  tooltip=row['name'],
452
  icon=folium.Icon(color='purple', icon="info-sign") # Using built-in icon
453
  ).add_to(hospital_map)
454
+
455
  # Display the map in Streamlit
456
  st_folium(hospital_map, width=725, height=500)
457
+
458
  # Explanation of color keys
459
  st.divider()
460
  st.write("Color Key:\n- Black🖤: Your current location📍\n- Blue💙: Hospitals🏥\n- Purple💜: Pharmacies💊")
461
+
462
+ st.divider()
463
+
 
464
  # Streamlit page title
465
  st.title('Enter Your Location')
466
+
467
  # Allow users to input latitude and longitude
468
  latitude = st.number_input('Enter your latitude:(eg.39.918058)', value=0.0, format="%.6f")
469
  longitude = st.number_input('Enter your longitude:(eg.116.397026)', value=0.0, format="%.6f")
470
+
471
  # Display the inputted location information if valid
472
  if latitude and longitude:
473
  st.write(f"Your entered location is at latitude {latitude} and longitude {longitude}")
474
+
475
  # Load environment variables
476
  load_dotenv()
477
  api_key = os.getenv('api_key') # Your Amap API key
478
+ # Create a map centered at the user's location
 
 
 
 
479
  hospital_map = folium.Map(location=[latitude, longitude], zoom_start=14)
480
+
481
  # Add a marker for the user's location
482
  folium.Marker(
483
  location=[latitude, longitude],
 
485
  tooltip="User's Location",
486
  icon=folium.Icon(color='black')
487
  ).add_to(hospital_map)
488
+
489
  # Hospital API URL
490
  url_hospital = f'https://restapi.amap.com/v3/place/around?key={api_key}&location={longitude},{latitude}&types=090100&radius=2000&offset=20&page=1&extensions=all'
491
  # Pharmacy API URL
492
  url_pharmacy = f'https://restapi.amap.com/v3/place/around?key={api_key}&location={longitude},{latitude}&types=090601&radius=2000&offset=20&page=1&extensions=all'
493
+
494
  try:
495
  # Make API request for hospitals
496
  response_hospital = requests.get(url_hospital)
497
  response_pharmacy = requests.get(url_pharmacy) # API request for pharmacies
498
+
499
  if response_hospital.status_code == 200 and response_pharmacy.status_code == 200:
500
  data_hospital = response_hospital.json()
501
  data_pharmacy = response_pharmacy.json()
502
+
503
  # Process hospital data
504
  if 'pois' in data_hospital:
505
  df_hospitals = pd.DataFrame(data_hospital['pois'])
 
516
  tooltip=row['name'],
517
  icon=folium.Icon(color='blue')
518
  ).add_to(hospital_map)
519
+
520
  # Process pharmacy data
521
  if 'pois' in data_pharmacy:
522
  df_pharmacies = pd.DataFrame(data_pharmacy['pois'])
 
533
  tooltip=row['name'],
534
  icon=folium.Icon(color='purple') # Different color for pharmacies
535
  ).add_to(hospital_map)
536
+
537
  # Display the map in Streamlit
538
  st.title('Nearby Hospitals and Pharmacies')
539
  st_folium(hospital_map, width=725, height=500)
540
+ st.write("Color Key:\n- Black🖤: Your current location📍\n- Blue💙: Hospitals🏥\n- Purple💜: Pharmacies💊")
541
  else:
542
  st.error("Failed to retrieve data. Check the status codes.")
543
  except Exception as e: