Upload folder using huggingface_hub
Browse files- Dockerfile +21 -0
- README.md +0 -12
- app.py +226 -0
- csv_config/s3_I_LOVE_POKE_ingredients.csv +81 -0
- launcher.sh +16 -0
- requirements.txt +4 -0
- test_images/poke_test1.jpeg +0 -0
- test_images/poke_test2.jpeg +0 -0
- test_images/poke_test3.jpeg +0 -0
- test_images/poke_test4.jpeg +0 -0
- test_images/poke_test5.jpeg +0 -0
- test_images/poke_test6.jpeg +0 -0
- test_images/poke_test7.jpeg +0 -0
Dockerfile
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
# Create a non-root user (HuggingFace Docker Spaces requirement)
|
| 4 |
+
RUN useradd -m -u 1000 user
|
| 5 |
+
USER user
|
| 6 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 7 |
+
|
| 8 |
+
WORKDIR /app
|
| 9 |
+
|
| 10 |
+
# Copy requirements and install
|
| 11 |
+
COPY --chown=user:user requirements.txt .
|
| 12 |
+
RUN pip install --user --no-cache-dir -r requirements.txt
|
| 13 |
+
|
| 14 |
+
# Copy application files
|
| 15 |
+
COPY --chown=user:user . .
|
| 16 |
+
|
| 17 |
+
# Expose port 7860 for HuggingFace Spaces
|
| 18 |
+
EXPOSE 7860
|
| 19 |
+
|
| 20 |
+
# Run Streamlit on the specified port
|
| 21 |
+
CMD ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
|
README.md
DELETED
|
@@ -1,12 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: S3poke
|
| 3 |
-
emoji: 🔥
|
| 4 |
-
colorFrom: gray
|
| 5 |
-
colorTo: pink
|
| 6 |
-
sdk: gradio
|
| 7 |
-
sdk_version: 6.10.0
|
| 8 |
-
app_file: app.py
|
| 9 |
-
pinned: false
|
| 10 |
-
---
|
| 11 |
-
|
| 12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.py
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import requests
|
| 4 |
+
import time
|
| 5 |
+
import textwrap
|
| 6 |
+
import os
|
| 7 |
+
import glob
|
| 8 |
+
|
| 9 |
+
API_BASE_URL = os.environ.get("API_BASE_URL", "http://localhost:8000")
|
| 10 |
+
CLIENT_ID = os.environ.get("CLIENT_ID", "DEMO_CLIENT_001")
|
| 11 |
+
APP_PASSWORD = os.environ.get("APP_PASSWORD", "s3poke") # Imposta questa nei Segreti di HF Space
|
| 12 |
+
|
| 13 |
+
st.set_page_config(page_title="Rilevatore di Ingredienti", layout="wide")
|
| 14 |
+
|
| 15 |
+
# CSS moderno e professionale con il font Inter
|
| 16 |
+
st.markdown("""
|
| 17 |
+
<style>
|
| 18 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
| 19 |
+
|
| 20 |
+
html, body, [class*="css"] {
|
| 21 |
+
font-family: 'Inter', sans-serif;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
.title-container {
|
| 25 |
+
padding-bottom: 1rem;
|
| 26 |
+
margin-bottom: 2rem;
|
| 27 |
+
border-bottom: 1px solid #eaeaea;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.prediction-box {
|
| 31 |
+
padding: 16px;
|
| 32 |
+
margin-bottom: 12px;
|
| 33 |
+
border-radius: 6px;
|
| 34 |
+
border-left: 4px solid #2563eb;
|
| 35 |
+
background-color: #f8fafc;
|
| 36 |
+
color: #1e293b;
|
| 37 |
+
animation: fadeIn 0.5s ease-out;
|
| 38 |
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.prediction-title {
|
| 42 |
+
font-size: 1.1em;
|
| 43 |
+
font-weight: 600;
|
| 44 |
+
color: #0f172a;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
.prediction-score {
|
| 48 |
+
float: right;
|
| 49 |
+
color: #2563eb;
|
| 50 |
+
font-weight: 600;
|
| 51 |
+
font-size: 1.1em;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
.prediction-desc {
|
| 55 |
+
font-size: 0.9em;
|
| 56 |
+
color: #64748b;
|
| 57 |
+
margin-top: 4px;
|
| 58 |
+
display: block;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
@keyframes fadeIn {
|
| 62 |
+
0% { opacity: 0; transform: translateY(5px); }
|
| 63 |
+
100% { opacity: 1; transform: translateY(0); }
|
| 64 |
+
}
|
| 65 |
+
</style>
|
| 66 |
+
""", unsafe_allow_html=True)
|
| 67 |
+
|
| 68 |
+
# Logica di autenticazione
|
| 69 |
+
def check_password():
|
| 70 |
+
if not APP_PASSWORD:
|
| 71 |
+
return True
|
| 72 |
+
|
| 73 |
+
if "password_correct" not in st.session_state:
|
| 74 |
+
st.session_state["password_correct"] = False
|
| 75 |
+
|
| 76 |
+
if not st.session_state["password_correct"]:
|
| 77 |
+
st.markdown('<div class="title-container"><h1>Accesso Rilevatore di Ingredienti</h1></div>', unsafe_allow_html=True)
|
| 78 |
+
st.info("Questo spazio è privato. Inserisci la password di accesso.")
|
| 79 |
+
password = st.text_input("Password", type="password")
|
| 80 |
+
if st.button("Accedi"):
|
| 81 |
+
if password == APP_PASSWORD:
|
| 82 |
+
st.session_state["password_correct"] = True
|
| 83 |
+
st.rerun()
|
| 84 |
+
else:
|
| 85 |
+
st.error("Password errata.")
|
| 86 |
+
return False
|
| 87 |
+
return True
|
| 88 |
+
|
| 89 |
+
if not check_password():
|
| 90 |
+
st.stop()
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
@st.cache_resource(show_spinner=False)
|
| 94 |
+
def configure_api():
|
| 95 |
+
config_dir = os.path.join(os.path.dirname(__file__), "csv_config")
|
| 96 |
+
csv_files = glob.glob(os.path.join(config_dir, "*.csv"))
|
| 97 |
+
|
| 98 |
+
if not csv_files:
|
| 99 |
+
return {"status": "error", "message": "Nessun CSV di configurazione trovato "}
|
| 100 |
+
|
| 101 |
+
try:
|
| 102 |
+
df = pd.read_csv(csv_files[0])
|
| 103 |
+
required_cols = {"PRODUCT_ID", "DESCRIPTION", "FAMILY", "COMPONENT", "EXTRA DESCRIPTION"}
|
| 104 |
+
if not required_cols.issubset(set(df.columns)):
|
| 105 |
+
return {"status": "error", "message": "Colonne richieste mancanti nel CSV."}
|
| 106 |
+
|
| 107 |
+
mask = (df["COMPONENT"].str.lower() == "ingredient") | (df["COMPONENT"].str.lower().str.startswith("proteine"))
|
| 108 |
+
df_filtered = df[mask].copy()
|
| 109 |
+
|
| 110 |
+
df_filtered["id"] = df_filtered["PRODUCT_ID"].astype(str)
|
| 111 |
+
df_filtered["description"] = df_filtered["DESCRIPTION"].astype(str)
|
| 112 |
+
|
| 113 |
+
df_filtered["FAMILY"] = df_filtered["FAMILY"].fillna("")
|
| 114 |
+
df_filtered["EXTRA DESCRIPTION"] = df_filtered["EXTRA DESCRIPTION"].fillna("")
|
| 115 |
+
df_filtered["extra_description"] = df_filtered["COMPONENT"].astype(str) + " - " + df_filtered["EXTRA DESCRIPTION"].astype(str)
|
| 116 |
+
|
| 117 |
+
products = df_filtered[["id", "description", "extra_description"]].to_dict(orient="records")
|
| 118 |
+
|
| 119 |
+
payload = {
|
| 120 |
+
"client_id": CLIENT_ID,
|
| 121 |
+
"context": "Rilevamento degli ingredienti all'interno delle Poke Bowl in Italia.",
|
| 122 |
+
"products": products
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
resp = requests.post(f"{API_BASE_URL}/configure", json=payload)
|
| 126 |
+
if resp.status_code == 200:
|
| 127 |
+
return {"status": "success", "message": "API configurata con successo."}
|
| 128 |
+
else:
|
| 129 |
+
return {"status": "error", "message": f"Errore API {resp.status_code}: {resp.text}"}
|
| 130 |
+
except Exception as e:
|
| 131 |
+
return {"status": "error", "message": f"Impossibile configurare l'API: {e}"}
|
| 132 |
+
|
| 133 |
+
# Inizializza la configurazione silenziosamente
|
| 134 |
+
with st.spinner("Inizializzazione configurazione API..."):
|
| 135 |
+
config_status = configure_api()
|
| 136 |
+
|
| 137 |
+
st.markdown('<div class="title-container"><h1>Rilevatore di Ingredienti</h1></div>', unsafe_allow_html=True)
|
| 138 |
+
|
| 139 |
+
if config_status["status"] == "error":
|
| 140 |
+
st.error(f"Errore di Inizializzazione: {config_status['message']}")
|
| 141 |
+
st.info("Controlla i file di configurazione e la connessione API.")
|
| 142 |
+
|
| 143 |
+
st.markdown("### Analisi dell'Immagine")
|
| 144 |
+
st.markdown("Carica l'immagine di una bowl o seleziona un'immagine di test per identificarne automaticamente gli ingredienti.")
|
| 145 |
+
|
| 146 |
+
# Cerca la cartella delle immagini di test (demo/test_images o ../tests)
|
| 147 |
+
possible_test_dirs = [
|
| 148 |
+
os.path.join(os.path.dirname(__file__), "test_images"),
|
| 149 |
+
os.path.join(os.path.dirname(__file__), "..", "tests")
|
| 150 |
+
]
|
| 151 |
+
test_images_dir = next((d for d in possible_test_dirs if os.path.exists(d)), None)
|
| 152 |
+
|
| 153 |
+
image_to_analyze_path = None
|
| 154 |
+
uploaded_img = None
|
| 155 |
+
|
| 156 |
+
if test_images_dir:
|
| 157 |
+
test_images = []
|
| 158 |
+
for ext in ('*.png', '*.jpg', '*.jpeg', '*.webp'):
|
| 159 |
+
test_images.extend(glob.glob(os.path.join(test_images_dir, ext)))
|
| 160 |
+
test_images = sorted(test_images)
|
| 161 |
+
|
| 162 |
+
if test_images:
|
| 163 |
+
selection_mode = st.radio("Scegli la sorgente dell'immagine:", ["Carica la tua", "Seleziona un'immagine di test"], horizontal=True)
|
| 164 |
+
if selection_mode == "Seleziona un'immagine di test":
|
| 165 |
+
img_names = [os.path.basename(p) for p in test_images]
|
| 166 |
+
selected_img_name = st.selectbox("Seleziona un'immagine di test:", img_names)
|
| 167 |
+
image_to_analyze_path = os.path.join(test_images_dir, selected_img_name)
|
| 168 |
+
else:
|
| 169 |
+
uploaded_img = st.file_uploader("Carica Immagine", type=["png", "jpg", "jpeg", "webp"], label_visibility="collapsed")
|
| 170 |
+
else:
|
| 171 |
+
uploaded_img = st.file_uploader("Carica Immagine", type=["png", "jpg", "jpeg", "webp"], label_visibility="collapsed")
|
| 172 |
+
|
| 173 |
+
if uploaded_img is not None or image_to_analyze_path is not None:
|
| 174 |
+
col1, col2 = st.columns([1, 1], gap="large")
|
| 175 |
+
|
| 176 |
+
with col1:
|
| 177 |
+
if uploaded_img is not None:
|
| 178 |
+
st.image(uploaded_img, use_container_width=True)
|
| 179 |
+
img_name = uploaded_img.name
|
| 180 |
+
img_bytes = uploaded_img.getvalue()
|
| 181 |
+
img_type = uploaded_img.type
|
| 182 |
+
else:
|
| 183 |
+
with open(image_to_analyze_path, "rb") as f:
|
| 184 |
+
img_bytes = f.read()
|
| 185 |
+
st.image(img_bytes, use_container_width=True)
|
| 186 |
+
img_name = os.path.basename(image_to_analyze_path)
|
| 187 |
+
img_type = "image/jpeg" if img_name.lower().endswith(('.jpg', '.jpeg')) else "image/png"
|
| 188 |
+
|
| 189 |
+
with col2:
|
| 190 |
+
if st.button("Identifica Ingredienti", type="primary", use_container_width=True):
|
| 191 |
+
with st.spinner("Analisi dell'immagine in corso..."):
|
| 192 |
+
files = {"image": (img_name, img_bytes, img_type)}
|
| 193 |
+
data = {"client_id": CLIENT_ID}
|
| 194 |
+
try:
|
| 195 |
+
resp = requests.post(f"{API_BASE_URL}/predict", files=files, data=data)
|
| 196 |
+
if resp.status_code == 200:
|
| 197 |
+
predictions = resp.json().get("predictions", [])
|
| 198 |
+
if not predictions:
|
| 199 |
+
st.info("Nessun ingrediente rilevato nell'immagine.")
|
| 200 |
+
else:
|
| 201 |
+
st.success("Analisi completata")
|
| 202 |
+
|
| 203 |
+
result_container = st.empty()
|
| 204 |
+
displayed_markdown = "<h4>Componenti Rilevati</h4>"
|
| 205 |
+
|
| 206 |
+
for pred in predictions:
|
| 207 |
+
score_pct = pred.get("score", 0) * 100
|
| 208 |
+
desc = pred.get("description") or "Nessun dettaglio aggiuntivo disponibile"
|
| 209 |
+
p_id = pred.get("product_id")
|
| 210 |
+
|
| 211 |
+
item_html = textwrap.dedent(f"""
|
| 212 |
+
<div class="prediction-box">
|
| 213 |
+
<span class="prediction-title">{p_id}</span>
|
| 214 |
+
<span class="prediction-score">{score_pct:.1f}%</span><br>
|
| 215 |
+
<span class="prediction-desc">{desc}</span>
|
| 216 |
+
</div>
|
| 217 |
+
""")
|
| 218 |
+
displayed_markdown += item_html
|
| 219 |
+
|
| 220 |
+
result_container.markdown(displayed_markdown, unsafe_allow_html=True)
|
| 221 |
+
time.sleep(0.08) # Elegante ritardo d'animazione
|
| 222 |
+
|
| 223 |
+
else:
|
| 224 |
+
st.error(f"Errore API {resp.status_code}: {resp.text}")
|
| 225 |
+
except Exception as e:
|
| 226 |
+
st.error(f"Impossibile connettersi all'API: {e}")
|
csv_config/s3_I_LOVE_POKE_ingredients.csv
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
PRODUCT_ID,DESCRIPTION,FAMILY,PRICE,COMPONENT,EXTRA DESCRIPTION
|
| 2 |
+
kb01,riso nero,kiosk basi,0,base,black rice
|
| 3 |
+
kb02,riso bianco,kiosk basi,0,base,white rice
|
| 4 |
+
kb03,riso integrale ,kiosk basi,0,base,brown rice
|
| 5 |
+
kb04,cous cous ,kiosk basi,0,base,couscous
|
| 6 |
+
kb05,insalata,kiosk basi,0,base,salad
|
| 7 |
+
kb06,metà metà ,kiosk basi,0,base,half and half
|
| 8 |
+
kb07,insalata,kiosk basi,0,base,salad
|
| 9 |
+
kb08,no base,kiosk basi,0,base,no base
|
| 10 |
+
king01,avocado hass,kiosk ingredienti,0,ingredient,hass avocado
|
| 11 |
+
king02,guacamole,kiosk ingredienti,0,ingredient,guacamole
|
| 12 |
+
king03,alga wakame,kiosk ingredienti,0,ingredient,wakame seaweed
|
| 13 |
+
king04,cream cheese ,kiosk ingredienti,0,ingredient,cream cheese
|
| 14 |
+
king05,feta,kiosk ingredienti,0,ingredient,feta
|
| 15 |
+
king06,mango,kiosk ingredienti,0,ingredient,mango
|
| 16 |
+
king07,hummus,kiosk ingredienti,0,ingredient,hummus
|
| 17 |
+
king08,carciofi,kiosk ingredienti,0,ingredient,artichokes
|
| 18 |
+
king09,zucchine limone menta,kiosk ingredienti,0,ingredient,zucchini with lemon and mint
|
| 19 |
+
king10,olive nere,kiosk ingredienti,0,ingredient,black olives
|
| 20 |
+
king11,mais,kiosk ingredienti,0,ingredient,corn
|
| 21 |
+
king12,zenzero,kiosk ingredienti,0,ingredient,ginger
|
| 22 |
+
king13,cavolo rosso,kiosk ingredienti,0,ingredient,red cabbage
|
| 23 |
+
king14,carote,kiosk ingredienti,0,ingredient,carrots
|
| 24 |
+
king15,cipolla rossa,kiosk ingredienti,0,ingredient,red onion
|
| 25 |
+
king16,pomodorini,kiosk ingredienti,0,ingredient,cherry tomatoes
|
| 26 |
+
king17,cetrioli,kiosk ingredienti,0,ingredient,cucumbers
|
| 27 |
+
king18,edamame,kiosk ingredienti,0,ingredient,edamame
|
| 28 |
+
king19,ananas,kiosk ingredienti,0,ingredient,pineapple
|
| 29 |
+
kprf01,freddo - tartare salmone,kiosk ingredienti,0,proteine fredde. Crudo,cold - salmon tartare
|
| 30 |
+
kprf02,freddo - salmone al naturale,kiosk ingredienti,0,proteine fredde. Crudo,cold - plain salmon
|
| 31 |
+
kprf03,freddo - salmone marinato all'hawaiana,kiosk ingredienti,0,proteine fredde. Crudo,cold - Hawaiian marinated salmon
|
| 32 |
+
kprf04,freddo - tonno al naturale,kiosk ingredienti,0,proteine fredde. Crudo,cold - plain tuna
|
| 33 |
+
kprf05,freddo - gamberi al vapore,kiosk ingredienti,0,proteine fredde. Cotto,cold - steamed shrimp
|
| 34 |
+
kprf06,freddo - tentacoli e patate,kiosk ingredienti,0,proteine fredde. Cotto,cold - octopus tentacles and potatoes
|
| 35 |
+
kprf07,freddo - insalata di tonno,kiosk ingredienti,0,proteine fredde. Crudo,cold - tuna salad
|
| 36 |
+
kprc01,caldo - bocconcini di pollo,kiosk ingredienti,0,proteine calde. Crudo,hot - chicken bites
|
| 37 |
+
kprc02,caldo - salmone a vapore,kiosk ingredienti,0,proteine calde. Cotto,hot - steamed salmon
|
| 38 |
+
kprv01,vegetariano - polpette di soia,kiosk ingredienti,0,proteine vegetale,vegetarian - soy meatballs
|
| 39 |
+
kprv02,vegan - bocconcini planted a limone,kiosk ingredienti,0,proteine vegetale,vegan - lemon plant-based bites
|
| 40 |
+
ksa01,teriyaki ,kiosk ingredienti,0,salsa,teriyaki
|
| 41 |
+
ksa02,ponzu,kiosk ingredienti,0,salsa,ponzu
|
| 42 |
+
ksa03,soia senza glutine,kiosk ingredienti,0,salsa,gluten-free soy sauce
|
| 43 |
+
ksa04,sriracha,kiosk ingredienti,0,salsa,sriracha
|
| 44 |
+
ksa05,olio extra vergine di oliva,kiosk ingredienti,0,salsa,extra virgin olive oil
|
| 45 |
+
ksa06,vinaigrette,kiosk ingredienti,0,salsa,vinaigrette
|
| 46 |
+
ksa07,citronette,kiosk ingredienti,0,salsa,citronette
|
| 47 |
+
ksa08,no salsa,kiosk ingredienti,0,salsa,no sauce
|
| 48 |
+
kexsal01,hawaiian special,kiosk ingredienti,0,salsa extra,hawaiian special
|
| 49 |
+
kexsal02,maio vegana,kiosk ingredienti,0,salsa extra,vegan mayo
|
| 50 |
+
kexsal03,yogurt,kiosk ingredienti,0,salsa extra,yogurt
|
| 51 |
+
kexsal04,maio piccante ,kiosk ingredienti,0,salsa extra,spicy mayo
|
| 52 |
+
kexsal05,maio wasabi,kiosk ingredienti,0,salsa extra,wasabi mayo
|
| 53 |
+
kexsal06,agropiccante tropicale,kiosk ingredienti,0,salsa extra,tropical hot and sour
|
| 54 |
+
ktop01,granella di noci,kiosk ingredienti,0,topping,crushed walnuts
|
| 55 |
+
ktop02,patate crispy,kiosk ingredienti,0,topping,crispy potatoes
|
| 56 |
+
ktop03,mandorle,kiosk ingredienti,0,topping,almonds
|
| 57 |
+
ktop04,sesamo ,kiosk ingredienti,0,topping,sesame
|
| 58 |
+
ktop05,cipolla croccante,kiosk ingredienti,0,topping,crispy onion
|
| 59 |
+
ktop06,mais tostato,kiosk ingredienti,0,topping,toasted corn
|
| 60 |
+
ksd01,Edamame classico,kiosk ingredienti,3,side,classic edamame
|
| 61 |
+
ksd02,Edamame piccante,kiosk ingredienti,3,side,spicy edamame
|
| 62 |
+
ksd03,Edamame citronette,kiosk ingredienti,3,side,citronette edamame
|
| 63 |
+
ksd04,Edamame tartufo,kiosk ingredienti,4,side,truffle edamame
|
| 64 |
+
ksd05,Guacamole chips,kiosk ingredienti,4,side,guacamole & chips
|
| 65 |
+
ksd06,Alghe wakame,kiosk ingredienti,3,side,wakame seaweed
|
| 66 |
+
kd01,Hawaiian Macedonia ,kiosk ingredienti,3.5,dolce,Hawaiian fruit salad
|
| 67 |
+
kd02,Mix mango e ananas fresco,kiosk ingredienti,0,dolce,fresh mango and pineapple mix
|
| 68 |
+
kbv01,Acqua in brick 500ml,kiosk ingredienti,1.5,bevanda,boxed water 500ml
|
| 69 |
+
kbv02,Acqua frizzante 500ml PET,kiosk ingredienti,1.5,bevanda,sparkling water 500ml PET
|
| 70 |
+
kbv03,Coca-Cola 45cl PET,kiosk ingredienti,3,bevanda,Coca-Cola 45cl PET
|
| 71 |
+
kbv04,Coca-Cola zero 45cl PET,kiosk ingredienti,3,bevanda,Coca-Cola Zero 45cl PET
|
| 72 |
+
kbv05,Red Bull energy drink 355ml,kiosk ingredienti,3.5,bevanda,Red Bull energy drink 355ml
|
| 73 |
+
kbv06,Red Bull sugarfree 355ml,kiosk ingredienti,3.5,bevanda,Red Bull sugarfree 355ml
|
| 74 |
+
kbv07,Fuzetea limone 40cl,kiosk ingredienti,3,bevanda,lemon Fuzetea 40cl
|
| 75 |
+
kbv08,Fuzetea pesca 40cl,kiosk ingredienti,3,bevanda,peach Fuzetea 40cl
|
| 76 |
+
kbv09,Chinotto Lurisia 275ml,kiosk ingredienti,3,bevanda,Lurisia Chinotto 275ml
|
| 77 |
+
kbv10,Limonata Lurisia 275ml,kiosk ingredienti,3,bevanda,Lurisia Lemonade 275ml
|
| 78 |
+
kbv11,Gazzosa Lurisia 275ml,kiosk ingredienti,3,bevanda,Lurisia Gazzosa 275ml
|
| 79 |
+
kbr01,Maui Beach Lager 33cl,kiosk ingredienti,4,bevanda,Maui Beach Lager 33cl
|
| 80 |
+
kbr02,Sunset Ipa 33cl,kiosk ingredienti,4,bevanda,Sunset IPA 33cl
|
| 81 |
+
kbr03,Poretti 3 Luppoli 33cl,kiosk ingredienti,4,bevanda,Poretti 3 Luppoli 33cl
|
launcher.sh
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# This script builds and runs the Streamlit demo in a Docker container,
|
| 3 |
+
# mimicking the environment of a Hugging Face Space.
|
| 4 |
+
|
| 5 |
+
# Build the docker image
|
| 6 |
+
echo "Building the Docker image: ingredient-detector-demo..."
|
| 7 |
+
sudo docker build -t ingredient-detector-demo .
|
| 8 |
+
|
| 9 |
+
# Run the docker image
|
| 10 |
+
# Port 7860 is used by the Dockerfile (Hugging Face default)
|
| 11 |
+
echo "Starting the container on http://localhost:7860..."
|
| 12 |
+
sudo docker run -p 7860:7860 \
|
| 13 |
+
-e API_BASE_URL="https://u3wqf8ysjq.eu-central-1.awsapprunner.com" \
|
| 14 |
+
-e CLIENT_ID="DEMO_CLIENT_001" \
|
| 15 |
+
-e APP_PASSWORD="s3poke" \
|
| 16 |
+
ingredient-detector-demo
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
pandas
|
| 3 |
+
requests
|
| 4 |
+
python-dotenv
|
test_images/poke_test1.jpeg
ADDED
|
test_images/poke_test2.jpeg
ADDED
|
test_images/poke_test3.jpeg
ADDED
|
test_images/poke_test4.jpeg
ADDED
|
test_images/poke_test5.jpeg
ADDED
|
test_images/poke_test6.jpeg
ADDED
|
test_images/poke_test7.jpeg
ADDED
|